@moltbankhq/openclaw 0.1.7 → 0.1.8
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 +8 -5
- package/index.ts +97 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ This package connects your OpenClaw agent to MoltBank's stablecoin treasury infr
|
|
|
40
40
|
|
|
41
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.
|
|
42
42
|
|
|
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.
|
|
44
|
+
|
|
43
45
|
## Setup
|
|
44
46
|
|
|
45
47
|
After installing, run:
|
|
@@ -48,16 +50,17 @@ After installing, run:
|
|
|
48
50
|
moltbank setup
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
The CLI will guide you through linking your MoltBank account via browser-based OAuth. Once approved, setup
|
|
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.
|
|
52
54
|
|
|
53
55
|
## CLI Commands
|
|
54
56
|
|
|
55
57
|
| Command | Description |
|
|
56
58
|
|---------|-------------|
|
|
57
|
-
| `moltbank setup` | Run full setup
|
|
58
|
-
| `moltbank setup --
|
|
59
|
-
| `moltbank setup-blocking` |
|
|
60
|
-
| `moltbank
|
|
59
|
+
| `moltbank setup` | Run full setup and verify readiness |
|
|
60
|
+
| `moltbank setup --verbose` | Run setup with detailed logs |
|
|
61
|
+
| `moltbank setup-blocking` | Alias for setup |
|
|
62
|
+
| `moltbank status` | Check current auth state |
|
|
63
|
+
| `moltbank auth-status` | Alias for `moltbank status` |
|
|
61
64
|
| `moltbank sandbox-setup` | Reconfigure sandbox Docker settings |
|
|
62
65
|
| `moltbank inject-key` | Re-inject env vars from credentials |
|
|
63
66
|
| `moltbank register` | Re-register mcporter MCP server |
|
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execSync, spawn } from 'child_process';
|
|
1
|
+
import { execSync, spawn, spawnSync } from 'child_process';
|
|
2
2
|
import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs';
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
@@ -763,6 +763,84 @@ function readPendingOauthCode(skillDir: string): {
|
|
|
763
763
|
}
|
|
764
764
|
}
|
|
765
765
|
|
|
766
|
+
function clearPendingOauthCode(skillDir: string): void {
|
|
767
|
+
const pendingPath = join(skillDir, '.oauth_device_code.json');
|
|
768
|
+
try {
|
|
769
|
+
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
770
|
+
} catch {
|
|
771
|
+
// ignore
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function runProcess(
|
|
776
|
+
command: string,
|
|
777
|
+
args: string[],
|
|
778
|
+
opts: { cwd?: string; silent?: boolean; env?: Record<string, string | undefined> } = {}
|
|
779
|
+
) {
|
|
780
|
+
try {
|
|
781
|
+
const mergedEnv: NodeJS.ProcessEnv = { ...process.env, ...(opts.env ?? {}) };
|
|
782
|
+
const result = spawnSync(command, args, {
|
|
783
|
+
cwd: opts.cwd,
|
|
784
|
+
env: mergedEnv,
|
|
785
|
+
encoding: 'utf8',
|
|
786
|
+
stdio: opts.silent ? 'pipe' : 'inherit'
|
|
787
|
+
});
|
|
788
|
+
return {
|
|
789
|
+
ok: result.status === 0,
|
|
790
|
+
stdout: (result.stdout ?? '').toString().trim(),
|
|
791
|
+
stderr: (result.stderr ?? '').toString().trim()
|
|
792
|
+
};
|
|
793
|
+
} catch (e: unknown) {
|
|
794
|
+
return {
|
|
795
|
+
ok: false,
|
|
796
|
+
stdout: '',
|
|
797
|
+
stderr: getExecErrorMessage(e)
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function runMoltbankWrapper(skillDir: string, args: string[]) {
|
|
803
|
+
if (IS_WIN) {
|
|
804
|
+
const ps1Path = join(skillDir, 'scripts', 'moltbank.ps1');
|
|
805
|
+
return runProcess('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ps1Path, ...args], {
|
|
806
|
+
cwd: skillDir,
|
|
807
|
+
silent: true
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
return runProcess(join(skillDir, 'scripts', 'moltbank.sh'), args, {
|
|
812
|
+
cwd: skillDir,
|
|
813
|
+
silent: true
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function verifyMoltbankOperationalReadiness(skillDir: string, api: LoggerApi): boolean {
|
|
818
|
+
const existing = parseActiveTokenFromCredentials();
|
|
819
|
+
if (!existing.ok || !existing.activeOrg) {
|
|
820
|
+
api.logger.warn('[moltbank] ✗ setup incomplete: authenticated org could not be resolved for balance verification');
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const verification = runMoltbankWrapper(skillDir, [
|
|
825
|
+
'call',
|
|
826
|
+
'MoltBank.get_balance',
|
|
827
|
+
`organizationName=${existing.activeOrg}`,
|
|
828
|
+
'date=today'
|
|
829
|
+
]);
|
|
830
|
+
|
|
831
|
+
if (verification.ok) {
|
|
832
|
+
api.logger.info(`[moltbank] ✓ balance verification passed (org: ${existing.activeOrg})`);
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
api.logger.warn('[moltbank] ✗ setup incomplete: auth is linked, but balance verification failed (`MoltBank.get_balance`)');
|
|
837
|
+
const detail = verification.stderr || verification.stdout;
|
|
838
|
+
if (detail) {
|
|
839
|
+
api.logger.warn('[moltbank] verification detail: ' + detail);
|
|
840
|
+
}
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
|
|
766
844
|
function startBackgroundOauthPoll(
|
|
767
845
|
skillDir: string,
|
|
768
846
|
appBaseUrl: string,
|
|
@@ -814,12 +892,7 @@ function startBackgroundOauthPoll(
|
|
|
814
892
|
oauthPollers.delete(skillDir);
|
|
815
893
|
const after = parseActiveTokenFromCredentials();
|
|
816
894
|
if (after.ok) {
|
|
817
|
-
|
|
818
|
-
try {
|
|
819
|
-
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
820
|
-
} catch {
|
|
821
|
-
// ignore
|
|
822
|
-
}
|
|
895
|
+
clearPendingOauthCode(skillDir);
|
|
823
896
|
api.logger.info(`[moltbank] ✓ background onboarding completed (active org: ${after.activeOrg})`);
|
|
824
897
|
try {
|
|
825
898
|
startBackgroundFinalizeAfterAuth(skillDir, appBaseUrl, api);
|
|
@@ -899,6 +972,8 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
899
972
|
const existing = parseActiveTokenFromCredentials();
|
|
900
973
|
if (existing.ok) {
|
|
901
974
|
stopBackgroundOauthPoll(skillDir);
|
|
975
|
+
stopBackgroundFinalize(skillDir);
|
|
976
|
+
clearPendingOauthCode(skillDir);
|
|
902
977
|
api.logger.info(`[moltbank] ✓ credentials.json already available (active org: ${existing.activeOrg})`);
|
|
903
978
|
return true;
|
|
904
979
|
}
|
|
@@ -1037,11 +1112,7 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
1037
1112
|
|
|
1038
1113
|
if (oauthError === 'invalid_grant') {
|
|
1039
1114
|
api.logger.warn('[moltbank] ✗ onboarding code expired or already consumed (invalid_grant)');
|
|
1040
|
-
|
|
1041
|
-
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
1042
|
-
} catch {
|
|
1043
|
-
// ignore
|
|
1044
|
-
}
|
|
1115
|
+
clearPendingOauthCode(skillDir);
|
|
1045
1116
|
} else {
|
|
1046
1117
|
api.logger.warn('[moltbank] ✗ onboarding poll failed or timed out');
|
|
1047
1118
|
}
|
|
@@ -1058,11 +1129,7 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
1058
1129
|
return false;
|
|
1059
1130
|
}
|
|
1060
1131
|
|
|
1061
|
-
|
|
1062
|
-
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
1063
|
-
} catch {
|
|
1064
|
-
// ignore
|
|
1065
|
-
}
|
|
1132
|
+
clearPendingOauthCode(skillDir);
|
|
1066
1133
|
|
|
1067
1134
|
api.logger.info(`[moltbank] ✓ onboarding completed (active org: ${after.activeOrg})`);
|
|
1068
1135
|
return true;
|
|
@@ -1071,7 +1138,10 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
1071
1138
|
export function printAuthStatus(skillDir: string, appBaseUrl: string, api: LoggerApi): void {
|
|
1072
1139
|
const now = Math.floor(Date.now() / 1000);
|
|
1073
1140
|
const existing = parseActiveTokenFromCredentials();
|
|
1074
|
-
|
|
1141
|
+
if (existing.ok) {
|
|
1142
|
+
clearPendingOauthCode(skillDir);
|
|
1143
|
+
}
|
|
1144
|
+
const pending = existing.ok ? null : readPendingOauthCode(skillDir);
|
|
1075
1145
|
const poller = oauthPollers.get(skillDir);
|
|
1076
1146
|
const finalizer = backgroundFinalizers.get(skillDir);
|
|
1077
1147
|
const pollerRunning = Boolean(poller && poller.exitCode === null && !poller.killed);
|
|
@@ -1441,6 +1511,11 @@ export async function runSetup(
|
|
|
1441
1511
|
} else {
|
|
1442
1512
|
api.logger.info('[moltbank] sandbox unchanged — no restart needed');
|
|
1443
1513
|
}
|
|
1514
|
+
|
|
1515
|
+
api.logger.info('[moltbank] [sandbox 11/11] verifying authenticated balance read...');
|
|
1516
|
+
if (!verifyMoltbankOperationalReadiness(skillDir, api)) {
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1444
1519
|
} else {
|
|
1445
1520
|
api.logger.info('[moltbank] [host 1/8] ensuring mcporter on host...');
|
|
1446
1521
|
ensureMcporter(api);
|
|
@@ -1483,19 +1558,11 @@ export async function runSetup(
|
|
|
1483
1558
|
api.logger.info('[moltbank] [host 7/8] writing/registering mcporter config...');
|
|
1484
1559
|
ensureMcporterConfig(skillDir, appBaseUrl, api);
|
|
1485
1560
|
|
|
1486
|
-
api.logger.info('[moltbank] [host 8/8]
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
? `powershell -NoProfile -ExecutionPolicy Bypass -File "${ps1Path}" list MoltBank`
|
|
1491
|
-
: `"${skillDir}/scripts/moltbank.sh" list MoltBank`;
|
|
1492
|
-
const smoke = run(smokeCmd, { cwd: skillDir, silent: true });
|
|
1493
|
-
if (!smoke.ok) {
|
|
1494
|
-
api.logger.warn('[moltbank] host setup incomplete: smoke test failed (`moltbank list MoltBank` via platform script)');
|
|
1495
|
-
} else {
|
|
1496
|
-
api.logger.info('[moltbank] host smoke test passed (`moltbank list MoltBank`)');
|
|
1561
|
+
api.logger.info('[moltbank] [host 8/8] verifying authenticated balance read...');
|
|
1562
|
+
hostReady = verifyMoltbankOperationalReadiness(skillDir, api);
|
|
1563
|
+
if (!hostReady) {
|
|
1564
|
+
return;
|
|
1497
1565
|
}
|
|
1498
|
-
hostReady = smoke.ok;
|
|
1499
1566
|
|
|
1500
1567
|
api.logger.info('[moltbank] host mode — setup completed with onboarding flow');
|
|
1501
1568
|
}
|