@moltbankhq/openclaw 0.1.16 → 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 +27 -4
- package/cli.ts +0 -18
- package/index.ts +208 -67
- package/openclaw.plugin.json +29 -0
- package/package.json +9 -3
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
|
|
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
|
|
|
@@ -65,7 +71,25 @@ CLI flags:
|
|
|
65
71
|
|
|
66
72
|
```bash
|
|
67
73
|
moltbank setup --app-base-url https://app.moltbank.bot --skill-name MoltBank
|
|
68
|
-
|
|
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
|
+
}
|
|
69
93
|
```
|
|
70
94
|
|
|
71
95
|
## Environment Variables
|
|
@@ -74,7 +98,6 @@ moltbank setup --workspace ~/.openclaw/workspace
|
|
|
74
98
|
|----------|-------------|
|
|
75
99
|
| `APP_BASE_URL` | MoltBank deployment URL (default: `https://app.moltbank.bot`) |
|
|
76
100
|
| `MOLTBANK_SKILL_NAME` | Override skill folder name (default: `MoltBank`) |
|
|
77
|
-
| `OPENCLAW_WORKSPACE` | Override OpenClaw workspace path (default: `~/.openclaw/workspace`) |
|
|
78
101
|
| `MOLTBANK_CREDENTIALS_PATH` | Custom credentials file path |
|
|
79
102
|
| `MOLTBANK_SETUP_AUTH_WAIT_MODE` | `blocking` or `nonblocking` (default: `nonblocking`) |
|
|
80
103
|
|
package/cli.ts
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
type CliConfig = {
|
|
17
17
|
appBaseUrl?: string;
|
|
18
18
|
skillName?: string;
|
|
19
|
-
workspacePath?: string;
|
|
20
19
|
};
|
|
21
20
|
|
|
22
21
|
type ParsedArgs = {
|
|
@@ -46,7 +45,6 @@ Commands:
|
|
|
46
45
|
Options:
|
|
47
46
|
--app-base-url <url> Override MoltBank deployment URL
|
|
48
47
|
--skill-name <name> Override skill folder name
|
|
49
|
-
--workspace <path> Override OpenClaw workspace path
|
|
50
48
|
--blocking Compatibility flag; setup already waits for approval
|
|
51
49
|
--restart-gateway Force gateway restart after successful host setup
|
|
52
50
|
--no-restart-gateway Skip gateway restart after successful host setup
|
|
@@ -56,7 +54,6 @@ Options:
|
|
|
56
54
|
|
|
57
55
|
Examples:
|
|
58
56
|
moltbank setup
|
|
59
|
-
moltbank setup --workspace ~/.openclaw/workspace
|
|
60
57
|
moltbank setup --verbose
|
|
61
58
|
moltbank status
|
|
62
59
|
moltbank register --app-base-url https://app.moltbank.bot
|
|
@@ -140,21 +137,6 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
140
137
|
continue;
|
|
141
138
|
}
|
|
142
139
|
|
|
143
|
-
if (arg === '--workspace') {
|
|
144
|
-
const value = argv[index + 1];
|
|
145
|
-
if (!value) {
|
|
146
|
-
throw new Error('missing value for --workspace');
|
|
147
|
-
}
|
|
148
|
-
config.workspacePath = value;
|
|
149
|
-
index += 1;
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (arg.startsWith('--workspace=')) {
|
|
154
|
-
config.workspacePath = arg.slice('--workspace='.length);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
140
|
if (arg.startsWith('-')) {
|
|
159
141
|
throw new Error(`unknown option: ${arg}`);
|
|
160
142
|
}
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
899
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
947
|
+
export function getSkillDir(cfg: MoltbankPluginConfig): string {
|
|
949
948
|
const skillName = getSkillName(cfg);
|
|
950
|
-
return join(getWorkspace(
|
|
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('
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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}/
|
|
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.
|
|
4
|
-
"description": "MoltBank stablecoin treasury CLI and OpenClaw
|
|
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",
|