@moltbankhq/openclaw 0.1.17 → 0.1.18

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
- Standalone CLI 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
 
@@ -22,6 +22,12 @@ Or from a local install:
22
22
  npm exec --package @moltbankhq/openclaw -- moltbank setup
23
23
  ```
24
24
 
25
+ OpenClaw plugin mode still exists in this pass for compatibility:
26
+
27
+ ```bash
28
+ openclaw plugins install @moltbankhq/openclaw
29
+ ```
30
+
25
31
  ## What it does
26
32
 
27
33
  This package connects your OpenClaw agent to MoltBank's stablecoin treasury infrastructure. During setup, it:
@@ -34,7 +40,7 @@ This package connects your OpenClaw agent to MoltBank's stablecoin treasury infr
34
40
 
35
41
  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.
36
42
 
37
- The standalone `moltbank` CLI is the supported path for install, setup, auth status, and repair flows. Actual treasury operations should run through the installed skill wrapper scripts inside the skill directory.
43
+ The standalone `moltbank` CLI is for install, setup, auth status, and repair flows. Actual treasury operations should run through the installed skill wrapper scripts inside the skill directory.
38
44
 
39
45
  ## Setup
40
46
 
@@ -44,16 +50,15 @@ After installing, run:
44
50
  moltbank setup
45
51
  ```
46
52
 
47
- By default, `moltbank setup` issues the browser activation code, starts background finalization, and returns without hanging on approval. Host setup also skips automatic gateway restarts by default so the active chat session is not interrupted. Use `moltbank setup --blocking` or `moltbank setup-blocking` when you want the command to wait for approval and finish readiness verification in the foreground.
53
+ The CLI will guide you through linking your MoltBank account via browser-based OAuth. Once approved, setup verifies a real authenticated balance read before reporting success.
48
54
 
49
55
  ## CLI Commands
50
56
 
51
57
  | Command | Description |
52
58
  |---------|-------------|
53
- | `moltbank setup` | Start setup in nonblocking mode and return after issuing activation steps |
54
- | `moltbank setup --blocking` | Run setup and wait for approval in the foreground |
59
+ | `moltbank setup` | Run full setup and verify readiness |
55
60
  | `moltbank setup --verbose` | Run setup with detailed logs |
56
- | `moltbank setup-blocking` | Run setup and wait for approval |
61
+ | `moltbank setup-blocking` | Alias for setup |
57
62
  | `moltbank status` | Check current auth state |
58
63
  | `moltbank auth-status` | Alias for `moltbank status` |
59
64
  | `moltbank sandbox-setup` | Reconfigure sandbox Docker settings |
@@ -66,7 +71,25 @@ CLI flags:
66
71
 
67
72
  ```bash
68
73
  moltbank setup --app-base-url https://app.moltbank.bot --skill-name MoltBank
69
- moltbank setup --workspace ~/.openclaw/workspace
74
+ ```
75
+
76
+ Plugin mode can still read optional config from your `openclaw.json`:
77
+
78
+ Optional config in your `openclaw.json`:
79
+
80
+ ```json
81
+ {
82
+ "plugins": {
83
+ "entries": {
84
+ "moltbank": {
85
+ "config": {
86
+ "appBaseUrl": "https://app.moltbank.bot",
87
+ "skillName": "MoltBank"
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
70
93
  ```
71
94
 
72
95
  ## Environment Variables
@@ -75,10 +98,8 @@ moltbank setup --workspace ~/.openclaw/workspace
75
98
  |----------|-------------|
76
99
  | `APP_BASE_URL` | MoltBank deployment URL (default: `https://app.moltbank.bot`) |
77
100
  | `MOLTBANK_SKILL_NAME` | Override skill folder name (default: `MoltBank`) |
78
- | `OPENCLAW_WORKSPACE` | Override OpenClaw workspace path (default: `~/.openclaw/workspace`) |
79
101
  | `MOLTBANK_CREDENTIALS_PATH` | Custom credentials file path |
80
102
  | `MOLTBANK_SETUP_AUTH_WAIT_MODE` | `blocking` or `nonblocking` (default: `nonblocking`) |
81
- | `MOLTBANK_SETUP_RESTART_GATEWAY` | `true` or `false` (default: `false`) |
82
103
 
83
104
  ## Capabilities
84
105
 
package/cli.ts CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  createSetupCommandLogger,
6
6
  ensureMcporterConfig,
7
7
  getAppBaseUrl,
8
- getSetupAuthWaitMode,
9
8
  getSetupGatewayRestartEnabled,
10
9
  getSkillDir,
11
10
  injectSandboxEnv,
@@ -17,7 +16,6 @@ import {
17
16
  type CliConfig = {
18
17
  appBaseUrl?: string;
19
18
  skillName?: string;
20
- workspacePath?: string;
21
19
  };
22
20
 
23
21
  type ParsedArgs = {
@@ -37,7 +35,7 @@ Usage:
37
35
 
38
36
  Commands:
39
37
  setup Run MoltBank setup
40
- setup-blocking Run setup and wait for approval
38
+ setup-blocking Alias for setup
41
39
  status Show current MoltBank auth state
42
40
  auth-status Alias for status
43
41
  sandbox-setup Reconfigure sandbox Docker settings in openclaw.json
@@ -47,18 +45,15 @@ Commands:
47
45
  Options:
48
46
  --app-base-url <url> Override MoltBank deployment URL
49
47
  --skill-name <name> Override skill folder name
50
- --workspace <path> Override OpenClaw workspace path
51
- --blocking Wait for browser approval before setup exits
48
+ --blocking Compatibility flag; setup already waits for approval
52
49
  --restart-gateway Force gateway restart after successful host setup
53
- --no-restart-gateway Skip gateway restart after successful host setup (default)
50
+ --no-restart-gateway Skip gateway restart after successful host setup
54
51
  --verbose Show detailed setup logs
55
52
  -h, --help Show help
56
53
  -v, --version Show package version
57
54
 
58
55
  Examples:
59
56
  moltbank setup
60
- moltbank setup --workspace ~/.openclaw/workspace
61
- moltbank setup --blocking
62
57
  moltbank setup --verbose
63
58
  moltbank status
64
59
  moltbank register --app-base-url https://app.moltbank.bot
@@ -142,21 +137,6 @@ function parseArgs(argv: string[]): ParsedArgs {
142
137
  continue;
143
138
  }
144
139
 
145
- if (arg === '--workspace') {
146
- const value = argv[index + 1];
147
- if (!value) {
148
- throw new Error('missing value for --workspace');
149
- }
150
- config.workspacePath = value;
151
- index += 1;
152
- continue;
153
- }
154
-
155
- if (arg.startsWith('--workspace=')) {
156
- config.workspacePath = arg.slice('--workspace='.length);
157
- continue;
158
- }
159
-
160
140
  if (arg.startsWith('-')) {
161
141
  throw new Error(`unknown option: ${arg}`);
162
142
  }
@@ -180,11 +160,10 @@ async function runCommand(parsed: ParsedArgs): Promise<void> {
180
160
 
181
161
  switch (parsed.command) {
182
162
  case 'setup': {
183
- const restartGatewayOnSuccess = parsed.restartGateway ?? getSetupGatewayRestartEnabled(false);
163
+ const restartGatewayOnSuccess = parsed.restartGateway ?? getSetupGatewayRestartEnabled(process.platform === 'win32');
184
164
  const logger = createSetupCommandLogger({ verbose: parsed.verbose, statusCommand: 'moltbank status' });
185
- const authWaitMode = parsed.blocking ? 'blocking' : getSetupAuthWaitMode('nonblocking');
186
165
  try {
187
- await runSetup(cfg, logger, { authWaitMode, restartGatewayOnSuccess });
166
+ await runSetup(cfg, logger, { authWaitMode: 'blocking', restartGatewayOnSuccess });
188
167
  } finally {
189
168
  logger.finish();
190
169
  }
@@ -192,7 +171,7 @@ async function runCommand(parsed: ParsedArgs): Promise<void> {
192
171
  }
193
172
 
194
173
  case 'setup-blocking': {
195
- const restartGatewayOnSuccess = parsed.restartGateway ?? getSetupGatewayRestartEnabled(false);
174
+ const restartGatewayOnSuccess = parsed.restartGateway ?? getSetupGatewayRestartEnabled(process.platform === 'win32');
196
175
  const logger = createSetupCommandLogger({ verbose: parsed.verbose, statusCommand: 'moltbank status' });
197
176
  try {
198
177
  await runSetup(cfg, logger, { authWaitMode: 'blocking', restartGatewayOnSuccess });
package/index.ts CHANGED
@@ -1,16 +1,15 @@
1
1
  import { spawn, spawnSync } from 'child_process';
2
2
  import { chmodSync, chownSync, existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync, type Dirent } from 'fs';
3
- import { join, dirname, basename, resolve } from 'path';
3
+ import { join, dirname, basename } from 'path';
4
4
  import { homedir, userInfo } from 'os';
5
5
  const IS_WIN = process.platform === 'win32';
6
6
 
7
7
  type OpenclawConfig = Record<string, unknown>;
8
8
  type ParsedJsonObject = Record<string, unknown>;
9
9
 
10
- export interface MoltbankConfig {
10
+ export interface MoltbankPluginConfig {
11
11
  skillName?: string;
12
12
  appBaseUrl?: string;
13
- workspacePath?: string;
14
13
  }
15
14
 
16
15
  interface CredentialsOrganization {
@@ -54,6 +53,36 @@ export interface SetupCommandLoggerOptions {
54
53
  statusCommand?: string;
55
54
  }
56
55
 
56
+ interface ServiceDefinition {
57
+ id: string;
58
+ start: () => void | Promise<void>;
59
+ stop: () => void | Promise<void>;
60
+ }
61
+
62
+ interface CliCommandLike {
63
+ command(name: string): CliCommandLike;
64
+ createCommand(name: string): CliCommandLike;
65
+ description(text: string): CliCommandLike;
66
+ addCommand(command: CliCommandLike): CliCommandLike;
67
+ action(handler: () => void | Promise<void>): CliCommandLike;
68
+ }
69
+
70
+ interface PluginApiConfig {
71
+ plugins?: {
72
+ entries?: {
73
+ moltbank?: {
74
+ config?: MoltbankPluginConfig;
75
+ };
76
+ };
77
+ };
78
+ }
79
+
80
+ interface PluginApi extends LoggerApi {
81
+ config?: PluginApiConfig;
82
+ registerService(service: ServiceDefinition): void;
83
+ registerCli(handler: (args: { program: CliCommandLike }) => void, options: { commands: string[] }): void;
84
+ }
85
+
57
86
  export type AuthWaitMode = 'blocking' | 'nonblocking';
58
87
  const oauthPollers = new Map<string, ReturnType<typeof spawn>>();
59
88
  const backgroundFinalizers = new Map<string, ReturnType<typeof spawn>>();
@@ -392,13 +421,9 @@ function resolveWindowsCommandPath(command: string, env: NodeJS.ProcessEnv): str
392
421
  const extensions = getWindowsCommandExtensions(env);
393
422
 
394
423
  const tryCandidates = (basePath: string): string => {
395
- const lower = basePath.toLowerCase();
396
- const hasKnownExtension = extensions.some((ext) => lower.endsWith(ext));
397
-
398
- if (hasKnownExtension && existsSync(basePath)) {
399
- return basePath;
400
- }
424
+ if (existsSync(basePath)) return basePath;
401
425
 
426
+ const lower = basePath.toLowerCase();
402
427
  for (const ext of extensions) {
403
428
  if (lower.endsWith(ext)) continue;
404
429
  const candidate = `${basePath}${ext}`;
@@ -407,10 +432,6 @@ function resolveWindowsCommandPath(command: string, env: NodeJS.ProcessEnv): str
407
432
  }
408
433
  }
409
434
 
410
- if (existsSync(basePath)) {
411
- return basePath;
412
- }
413
-
414
435
  return '';
415
436
  };
416
437
 
@@ -895,41 +916,19 @@ function lastNonEmptyLine(text: string): string {
895
916
  return lines.length ? lines[lines.length - 1] : '';
896
917
  }
897
918
 
898
- function normalizeWorkspacePath(rawPath: string): string {
899
- const trimmed = rawPath.trim();
900
- if (!trimmed) {
901
- return join(homedir(), '.openclaw', 'workspace');
902
- }
903
-
904
- if (trimmed === '~') {
905
- return homedir();
906
- }
907
-
908
- if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) {
909
- return resolve(join(homedir(), trimmed.slice(2)));
910
- }
911
-
912
- return resolve(trimmed);
913
- }
914
-
915
- export function getWorkspace(cfg?: MoltbankConfig): string {
916
- return normalizeWorkspacePath(cfg?.workspacePath || process.env.OPENCLAW_WORKSPACE || join(homedir(), '.openclaw', 'workspace'));
919
+ function getWorkspace(): string {
920
+ return process.env.OPENCLAW_WORKSPACE || join(homedir(), '.openclaw', 'workspace');
917
921
  }
918
922
 
919
- function getWorkspaceFromSkillDir(skillDir: string): string {
920
- const skillsDir = dirname(skillDir);
921
- return basename(skillsDir) === 'skills' ? dirname(skillsDir) : getWorkspace();
922
- }
923
-
924
- export function getSkillName(cfg: MoltbankConfig): string {
923
+ export function getSkillName(cfg: MoltbankPluginConfig): string {
925
924
  return cfg?.skillName || process.env.MOLTBANK_SKILL_NAME || 'MoltBank';
926
925
  }
927
926
 
928
- export function getAppBaseUrl(cfg: MoltbankConfig): string {
927
+ export function getAppBaseUrl(cfg: MoltbankPluginConfig): string {
929
928
  return (cfg?.appBaseUrl || process.env.APP_BASE_URL || 'https://app.moltbank.bot').trim();
930
929
  }
931
930
 
932
- export function getSkillBundleBaseUrl(cfg: MoltbankConfig): string {
931
+ export function getSkillBundleBaseUrl(cfg: MoltbankPluginConfig): string {
933
932
  const base = getAppBaseUrl(cfg).replace(/\/$/, '');
934
933
  return base.endsWith('/skill') ? base : `${base}/skill`;
935
934
  }
@@ -945,9 +944,9 @@ function isSandboxEnabled(): boolean {
945
944
  }
946
945
  }
947
946
 
948
- export function getSkillDir(cfg: MoltbankConfig): string {
947
+ export function getSkillDir(cfg: MoltbankPluginConfig): string {
949
948
  const skillName = getSkillName(cfg);
950
- return join(getWorkspace(cfg), 'skills', skillName);
949
+ return join(getWorkspace(), 'skills', skillName);
951
950
  }
952
951
 
953
952
  function getCredentialsPath(): string {
@@ -1066,6 +1065,47 @@ function setNestedValue(obj: Record<string, unknown>, path: string[], value: unk
1066
1065
  current[path[path.length - 1]] = value;
1067
1066
  }
1068
1067
 
1068
+ function cleanupStaleMoltbankPluginLoadPaths(api: LoggerApi): boolean {
1069
+ try {
1070
+ const config = readOpenclawConfig();
1071
+ const loadPathsPath = ['plugins', 'load', 'paths'];
1072
+ const current = getNestedValue(config, loadPathsPath);
1073
+ if (!Array.isArray(current)) return false;
1074
+
1075
+ const removed: string[] = [];
1076
+ const next: unknown[] = [];
1077
+
1078
+ for (const entry of current) {
1079
+ if (typeof entry !== 'string') {
1080
+ next.push(entry);
1081
+ continue;
1082
+ }
1083
+
1084
+ const normalized = entry.replace(/\\/g, '/').toLowerCase();
1085
+ const looksLikeMoltbankPath = normalized.includes('moltbank');
1086
+ if (looksLikeMoltbankPath && !existsSync(entry)) {
1087
+ removed.push(entry);
1088
+ continue;
1089
+ }
1090
+
1091
+ next.push(entry);
1092
+ }
1093
+
1094
+ if (removed.length === 0) {
1095
+ api.logger.info('[moltbank] ✓ no stale MoltBank plugin load paths found');
1096
+ return false;
1097
+ }
1098
+
1099
+ setNestedValue(config, loadPathsPath, next);
1100
+ writeOpenclawConfig(config);
1101
+ api.logger.info(`[moltbank] ✓ removed stale MoltBank plugin load path(s): ${removed.join(', ')}`);
1102
+ return true;
1103
+ } catch (e) {
1104
+ api.logger.warn('[moltbank] could not clean stale MoltBank plugin load paths: ' + String(e));
1105
+ return false;
1106
+ }
1107
+ }
1108
+
1069
1109
  // ─── mcporter ────────────────────────────────────────────────────────────────
1070
1110
 
1071
1111
  function ensureMcporter(api: LoggerApi) {
@@ -1652,12 +1692,11 @@ function startBackgroundFinalizeAfterAuth(skillDir: string, appBaseUrl: string,
1652
1692
  }
1653
1693
  if (existing) backgroundFinalizers.delete(skillDir);
1654
1694
 
1655
- const child = spawn('moltbank', ['setup-blocking'], {
1695
+ const child = spawn('openclaw', ['moltbank', 'setup-blocking'], {
1656
1696
  cwd: skillDir,
1657
1697
  env: {
1658
1698
  ...process.env,
1659
1699
  APP_BASE_URL: appBaseUrl,
1660
- OPENCLAW_WORKSPACE: getWorkspaceFromSkillDir(skillDir),
1661
1700
  MOLTBANK_SETUP_AUTH_WAIT_MODE: 'blocking'
1662
1701
  },
1663
1702
  stdio: 'ignore', // Ignore logs so it doesn't hang the parent
@@ -1810,7 +1849,7 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
1810
1849
  const backgroundTimeoutSeconds = Math.max(30, expiresAt - now - 5);
1811
1850
  startBackgroundOauthPoll(skillDir, appBaseUrl, credsPath, deviceCode, backgroundTimeoutSeconds, api);
1812
1851
  api.logger.info('[moltbank] once approved in browser, setup finalization will continue automatically');
1813
- api.logger.info('[moltbank] optional immediate check: `moltbank status`');
1852
+ api.logger.info('[moltbank] optional immediate check: `openclaw moltbank auth-status`');
1814
1853
  return false;
1815
1854
  }
1816
1855
 
@@ -2210,7 +2249,7 @@ function restartGatewayAfterHostSetup(api: LoggerApi): boolean {
2210
2249
  // ─── main setup ───────────────────────────────────────────────────────────────
2211
2250
 
2212
2251
  export async function runSetup(
2213
- cfg: MoltbankConfig,
2252
+ cfg: MoltbankPluginConfig,
2214
2253
  api: LoggerApi,
2215
2254
  options: { authWaitMode?: AuthWaitMode; restartGatewayOnSuccess?: boolean } = {}
2216
2255
  ) {
@@ -2229,6 +2268,9 @@ export async function runSetup(
2229
2268
  api.logger.info(`[moltbank] skill dir: ${skillDir}`);
2230
2269
  api.logger.info(`[moltbank] base url: ${appBaseUrl}`);
2231
2270
  api.logger.info(`[moltbank] ══════════════════════════════════════`);
2271
+ api.logger.info('[moltbank] preflight: cleaning stale MoltBank plugin load paths...');
2272
+ cleanupStaleMoltbankPluginLoadPaths(api);
2273
+
2232
2274
  if (sandbox) {
2233
2275
  api.logger.info('[moltbank] configuring sandbox mode...');
2234
2276
 
@@ -2256,10 +2298,10 @@ export async function runSetup(
2256
2298
  if (pending) {
2257
2299
  api.logger.warn('[moltbank] sandbox auth pending — startup continues without blocking channel startup');
2258
2300
  api.logger.info('[moltbank] setup will finalize automatically after browser approval');
2259
- api.logger.info('[moltbank] optional immediate check: `moltbank status`');
2301
+ api.logger.info('[moltbank] optional immediate check: `openclaw moltbank auth-status`');
2260
2302
  } else {
2261
2303
  api.logger.warn('[moltbank] sandbox auth setup failed before issuing a valid device code');
2262
- api.logger.warn('[moltbank] review the previous error and rerun `moltbank setup`');
2304
+ api.logger.warn('[moltbank] review the previous error and rerun `openclaw moltbank setup`');
2263
2305
  }
2264
2306
  return;
2265
2307
  }
@@ -2315,10 +2357,10 @@ export async function runSetup(
2315
2357
  if (pending) {
2316
2358
  api.logger.warn('[moltbank] host auth pending — startup continues without blocking channel startup');
2317
2359
  api.logger.info('[moltbank] setup will finalize automatically after browser approval');
2318
- api.logger.info('[moltbank] optional immediate check: `moltbank status`');
2360
+ api.logger.info('[moltbank] optional immediate check: `openclaw moltbank auth-status`');
2319
2361
  } else {
2320
2362
  api.logger.warn('[moltbank] host auth setup failed before issuing a valid device code');
2321
- api.logger.warn('[moltbank] review the previous error and rerun `moltbank setup`');
2363
+ api.logger.warn('[moltbank] review the previous error and rerun `openclaw moltbank setup`');
2322
2364
  }
2323
2365
  return;
2324
2366
  }
@@ -2335,11 +2377,7 @@ export async function runSetup(
2335
2377
  api.logger.info('[moltbank] [host 8/8] running readiness balance check...');
2336
2378
  hostReady = runHostReadinessCheck(skillDir, api);
2337
2379
 
2338
- if (hostReady) {
2339
- api.logger.info('[moltbank] host mode — setup completed with onboarding flow');
2340
- } else {
2341
- api.logger.warn('[moltbank] host mode setup finished, but operational readiness verification failed');
2342
- }
2380
+ api.logger.info('[moltbank] host mode — setup completed with onboarding flow');
2343
2381
 
2344
2382
  if (hostReady && restartGatewayOnSuccess) {
2345
2383
  restartGatewayAfterHostSetup(api);
@@ -2347,19 +2385,13 @@ export async function runSetup(
2347
2385
  }
2348
2386
 
2349
2387
  api.logger.info(`[moltbank] ══════════════════════════════════════`);
2350
- if (sandbox || hostReady) {
2351
- api.logger.info(`[moltbank] ✓ setup complete`);
2352
- } else {
2353
- api.logger.warn('[moltbank] ✗ setup incomplete');
2354
- }
2388
+ api.logger.info(`[moltbank] setup complete`);
2355
2389
  if (sandbox) {
2356
2390
  api.logger.info('[moltbank] ⏳ sandbox will be recreated in ~8s — send a message to the agent after that');
2357
2391
  } else if (hostReady) {
2358
2392
  api.logger.info('[moltbank] ✓ host ready');
2359
- } else {
2360
- api.logger.warn('[moltbank] ✗ host not ready');
2361
2393
  }
2362
- api.logger.info(`[moltbank] skill: ${skillDir}/skill/SKILL.md`);
2394
+ api.logger.info(`[moltbank] skill: ${skillDir}/SKILL.md`);
2363
2395
  api.logger.info(`[moltbank] mcporter: ${skillDir}/assets/mcporter.json`);
2364
2396
  if (sandbox) {
2365
2397
  const finalConfig = readOpenclawConfig();
@@ -2401,9 +2433,118 @@ export async function runSetup(
2401
2433
  }
2402
2434
  }
2403
2435
  api.logger.info(`[moltbank] ══════════════════════════════════════`);
2404
-
2405
- if (!sandbox && !hostReady) {
2406
- throw new Error('host setup incomplete: authenticated readiness verification failed');
2407
- }
2408
2436
  }
2409
2437
 
2438
+ // ─── plugin register ──────────────────────────────────────────────────────────
2439
+
2440
+ export default function register(api: PluginApi) {
2441
+ const cfg: MoltbankPluginConfig = api.config?.plugins?.entries?.moltbank?.config ?? {};
2442
+
2443
+ api.registerService({
2444
+ id: 'moltbank-setup',
2445
+ start: async () => {
2446
+ await runSetup(cfg, api, { authWaitMode: 'nonblocking' });
2447
+ },
2448
+ stop: async () => {
2449
+ stopBackgroundOauthPoll(getSkillDir(cfg));
2450
+ stopBackgroundFinalize(getSkillDir(cfg));
2451
+ api.logger.info('[moltbank] plugin stopped');
2452
+ }
2453
+ });
2454
+
2455
+ api.registerCli(
2456
+ ({ program }: { program: CliCommandLike }) => {
2457
+ program
2458
+ .command('moltbank')
2459
+ .description('MoltBank plugin commands')
2460
+ .addCommand(
2461
+ program
2462
+ .createCommand('setup')
2463
+ .description('Re-run MoltBank setup')
2464
+ .action(async () => {
2465
+ const verbose = process.argv.includes('--verbose');
2466
+ const restartGatewayOnSuccess = getSetupGatewayRestartEnabled(IS_WIN);
2467
+ const logger = createSetupCommandLogger({ verbose, statusCommand: 'openclaw moltbank status' });
2468
+ try {
2469
+ await runSetup(cfg, logger, { authWaitMode: 'blocking', restartGatewayOnSuccess });
2470
+ } finally {
2471
+ logger.finish();
2472
+ }
2473
+ })
2474
+ )
2475
+ .addCommand(
2476
+ program
2477
+ .createCommand('setup-blocking')
2478
+ .description('Re-run full MoltBank setup and wait for OAuth approval')
2479
+ .action(async () => {
2480
+ const verbose = process.argv.includes('--verbose');
2481
+ const restartGatewayOnSuccess = getSetupGatewayRestartEnabled(IS_WIN);
2482
+ const logger = createSetupCommandLogger({ verbose, statusCommand: 'openclaw moltbank status' });
2483
+ try {
2484
+ await runSetup(cfg, logger, { authWaitMode: 'blocking', restartGatewayOnSuccess });
2485
+ } finally {
2486
+ logger.finish();
2487
+ }
2488
+ })
2489
+ )
2490
+ .addCommand(
2491
+ program
2492
+ .createCommand('sandbox-setup')
2493
+ .description('Reconfigure sandbox docker in openclaw.json')
2494
+ .action(() => {
2495
+ const changed = configureSandbox({ logger: console });
2496
+ if (changed) {
2497
+ recreateSandboxAndRestart({ logger: console });
2498
+ } else {
2499
+ console.log('[moltbank] No sandbox docker changes — not scheduling teardown');
2500
+ }
2501
+ })
2502
+ )
2503
+ .addCommand(
2504
+ program
2505
+ .createCommand('inject-key')
2506
+ .description('Re-inject sandbox env vars from credentials.json')
2507
+ .action(() => {
2508
+ const skillDir = getSkillDir(cfg);
2509
+ const changed = injectSandboxEnv(skillDir, { logger: console });
2510
+ if (changed) {
2511
+ recreateSandboxAndRestart({ logger: console });
2512
+ } else {
2513
+ console.log('[moltbank] No env changes — not scheduling teardown');
2514
+ }
2515
+ })
2516
+ )
2517
+ .addCommand(
2518
+ program
2519
+ .createCommand('status')
2520
+ .description('Show current MoltBank auth state')
2521
+ .action(() => {
2522
+ const appBaseUrl = getAppBaseUrl(cfg);
2523
+ const skillDir = getSkillDir(cfg);
2524
+ printAuthStatus(skillDir, appBaseUrl, { logger: console });
2525
+ })
2526
+ )
2527
+ .addCommand(
2528
+ program
2529
+ .createCommand('auth-status')
2530
+ .description('Show current MoltBank auth state (credentials, pending code, background poll)')
2531
+ .action(() => {
2532
+ const appBaseUrl = getAppBaseUrl(cfg);
2533
+ const skillDir = getSkillDir(cfg);
2534
+ printAuthStatus(skillDir, appBaseUrl, { logger: console });
2535
+ })
2536
+ )
2537
+ .addCommand(
2538
+ program
2539
+ .createCommand('register')
2540
+ .description('Re-register mcporter server')
2541
+ .action(() => {
2542
+ const appBaseUrl = getAppBaseUrl(cfg);
2543
+ const skillDir = getSkillDir(cfg);
2544
+ ensureMcporterConfig(skillDir, appBaseUrl, { logger: console });
2545
+ })
2546
+ );
2547
+ },
2548
+ { commands: ['moltbank'] }
2549
+ );
2550
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "moltbank",
3
+ "name": "MoltBank",
4
+ "description": "MoltBank stablecoin treasury plugin for OpenClaw — installs mcporter, npm dependencies, sandbox setup, and the MoltBank skill",
5
+ "configSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "appBaseUrl": {
10
+ "type": "string",
11
+ "description": "MoltBank deployment URL"
12
+ },
13
+ "skillName": {
14
+ "type": "string",
15
+ "description": "Override skill folder name (default: MoltBank)"
16
+ }
17
+ }
18
+ },
19
+ "uiHints": {
20
+ "appBaseUrl": {
21
+ "label": "App Base URL",
22
+ "placeholder": "https://app.moltbank.bot"
23
+ },
24
+ "skillName": {
25
+ "label": "Skill Name",
26
+ "placeholder": "MoltBank"
27
+ }
28
+ }
29
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moltbankhq/openclaw",
3
- "version": "0.1.17",
4
- "description": "MoltBank stablecoin treasury CLI and OpenClaw skill installer",
3
+ "version": "0.1.18",
4
+ "description": "MoltBank stablecoin treasury CLI and OpenClaw plugin",
5
5
  "homepage": "https://github.com/moltbankhq/openclaw-plugin#readme",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,8 +24,14 @@
24
24
  "README.md",
25
25
  "bin/",
26
26
  "cli.ts",
27
- "index.ts"
27
+ "index.ts",
28
+ "openclaw.plugin.json"
28
29
  ],
30
+ "openclaw": {
31
+ "extensions": [
32
+ "./index.ts"
33
+ ]
34
+ },
29
35
  "keywords": [
30
36
  "moltbank",
31
37
  "openclaw",