@moltbankhq/openclaw 0.1.7 → 0.1.9
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 +107 -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';
|
|
@@ -498,6 +498,7 @@ const SKILL_FILES = [
|
|
|
498
498
|
'SKILL.md',
|
|
499
499
|
'skill.json',
|
|
500
500
|
'install.sh',
|
|
501
|
+
'package.json',
|
|
501
502
|
'assets/mcporter.json',
|
|
502
503
|
'references/heartbeat.md',
|
|
503
504
|
'references/onboarding.md',
|
|
@@ -763,6 +764,93 @@ function readPendingOauthCode(skillDir: string): {
|
|
|
763
764
|
}
|
|
764
765
|
}
|
|
765
766
|
|
|
767
|
+
function clearPendingOauthCode(skillDir: string): void {
|
|
768
|
+
const pendingPath = join(skillDir, '.oauth_device_code.json');
|
|
769
|
+
try {
|
|
770
|
+
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
771
|
+
} catch {
|
|
772
|
+
// ignore
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function runProcess(
|
|
777
|
+
command: string,
|
|
778
|
+
args: string[],
|
|
779
|
+
opts: { cwd?: string; silent?: boolean; env?: Record<string, string | undefined> } = {}
|
|
780
|
+
) {
|
|
781
|
+
try {
|
|
782
|
+
const mergedEnv: NodeJS.ProcessEnv = { ...process.env, ...(opts.env ?? {}) };
|
|
783
|
+
const result = spawnSync(command, args, {
|
|
784
|
+
cwd: opts.cwd,
|
|
785
|
+
env: mergedEnv,
|
|
786
|
+
encoding: 'utf8',
|
|
787
|
+
stdio: opts.silent ? 'pipe' : 'inherit'
|
|
788
|
+
});
|
|
789
|
+
return {
|
|
790
|
+
ok: result.status === 0,
|
|
791
|
+
stdout: (result.stdout ?? '').toString().trim(),
|
|
792
|
+
stderr: (result.stderr ?? '').toString().trim()
|
|
793
|
+
};
|
|
794
|
+
} catch (e: unknown) {
|
|
795
|
+
return {
|
|
796
|
+
ok: false,
|
|
797
|
+
stdout: '',
|
|
798
|
+
stderr: getExecErrorMessage(e)
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function runMoltbankWrapper(
|
|
804
|
+
skillDir: string,
|
|
805
|
+
args: string[],
|
|
806
|
+
env: Record<string, string | undefined> = {}
|
|
807
|
+
) {
|
|
808
|
+
if (IS_WIN) {
|
|
809
|
+
const ps1Path = join(skillDir, 'scripts', 'moltbank.ps1');
|
|
810
|
+
return runProcess('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ps1Path, ...args], {
|
|
811
|
+
cwd: skillDir,
|
|
812
|
+
silent: true,
|
|
813
|
+
env
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return runProcess(join(skillDir, 'scripts', 'moltbank.sh'), args, {
|
|
818
|
+
cwd: skillDir,
|
|
819
|
+
silent: true,
|
|
820
|
+
env
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function verifyMoltbankOperationalReadiness(skillDir: string, api: LoggerApi): boolean {
|
|
825
|
+
const existing = parseActiveTokenFromCredentials();
|
|
826
|
+
if (!existing.ok || !existing.activeOrg || !existing.token) {
|
|
827
|
+
api.logger.warn('[moltbank] ✗ setup incomplete: authenticated org could not be resolved for balance verification');
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
const verification = runMoltbankWrapper(skillDir, [
|
|
832
|
+
'call',
|
|
833
|
+
'MoltBank.get_balance',
|
|
834
|
+
`organizationName=${existing.activeOrg}`,
|
|
835
|
+
'date=today'
|
|
836
|
+
], {
|
|
837
|
+
MOLTBANK: existing.token,
|
|
838
|
+
ACTIVE_ORG_OVERRIDE: existing.activeOrg
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
if (verification.ok) {
|
|
842
|
+
api.logger.info(`[moltbank] ✓ balance verification passed (org: ${existing.activeOrg})`);
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
api.logger.warn('[moltbank] ✗ setup incomplete: auth is linked, but balance verification failed (`MoltBank.get_balance`)');
|
|
847
|
+
const detail = verification.stderr || verification.stdout;
|
|
848
|
+
if (detail) {
|
|
849
|
+
api.logger.warn('[moltbank] verification detail: ' + detail);
|
|
850
|
+
}
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
|
|
766
854
|
function startBackgroundOauthPoll(
|
|
767
855
|
skillDir: string,
|
|
768
856
|
appBaseUrl: string,
|
|
@@ -814,12 +902,7 @@ function startBackgroundOauthPoll(
|
|
|
814
902
|
oauthPollers.delete(skillDir);
|
|
815
903
|
const after = parseActiveTokenFromCredentials();
|
|
816
904
|
if (after.ok) {
|
|
817
|
-
|
|
818
|
-
try {
|
|
819
|
-
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
820
|
-
} catch {
|
|
821
|
-
// ignore
|
|
822
|
-
}
|
|
905
|
+
clearPendingOauthCode(skillDir);
|
|
823
906
|
api.logger.info(`[moltbank] ✓ background onboarding completed (active org: ${after.activeOrg})`);
|
|
824
907
|
try {
|
|
825
908
|
startBackgroundFinalizeAfterAuth(skillDir, appBaseUrl, api);
|
|
@@ -899,6 +982,8 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
899
982
|
const existing = parseActiveTokenFromCredentials();
|
|
900
983
|
if (existing.ok) {
|
|
901
984
|
stopBackgroundOauthPoll(skillDir);
|
|
985
|
+
stopBackgroundFinalize(skillDir);
|
|
986
|
+
clearPendingOauthCode(skillDir);
|
|
902
987
|
api.logger.info(`[moltbank] ✓ credentials.json already available (active org: ${existing.activeOrg})`);
|
|
903
988
|
return true;
|
|
904
989
|
}
|
|
@@ -1037,11 +1122,7 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
1037
1122
|
|
|
1038
1123
|
if (oauthError === 'invalid_grant') {
|
|
1039
1124
|
api.logger.warn('[moltbank] ✗ onboarding code expired or already consumed (invalid_grant)');
|
|
1040
|
-
|
|
1041
|
-
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
1042
|
-
} catch {
|
|
1043
|
-
// ignore
|
|
1044
|
-
}
|
|
1125
|
+
clearPendingOauthCode(skillDir);
|
|
1045
1126
|
} else {
|
|
1046
1127
|
api.logger.warn('[moltbank] ✗ onboarding poll failed or timed out');
|
|
1047
1128
|
}
|
|
@@ -1058,11 +1139,7 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
1058
1139
|
return false;
|
|
1059
1140
|
}
|
|
1060
1141
|
|
|
1061
|
-
|
|
1062
|
-
if (existsSync(pendingPath)) unlinkSync(pendingPath);
|
|
1063
|
-
} catch {
|
|
1064
|
-
// ignore
|
|
1065
|
-
}
|
|
1142
|
+
clearPendingOauthCode(skillDir);
|
|
1066
1143
|
|
|
1067
1144
|
api.logger.info(`[moltbank] ✓ onboarding completed (active org: ${after.activeOrg})`);
|
|
1068
1145
|
return true;
|
|
@@ -1071,7 +1148,10 @@ function ensureMoltbankAuth(skillDir: string, appBaseUrl: string, api: LoggerApi
|
|
|
1071
1148
|
export function printAuthStatus(skillDir: string, appBaseUrl: string, api: LoggerApi): void {
|
|
1072
1149
|
const now = Math.floor(Date.now() / 1000);
|
|
1073
1150
|
const existing = parseActiveTokenFromCredentials();
|
|
1074
|
-
|
|
1151
|
+
if (existing.ok) {
|
|
1152
|
+
clearPendingOauthCode(skillDir);
|
|
1153
|
+
}
|
|
1154
|
+
const pending = existing.ok ? null : readPendingOauthCode(skillDir);
|
|
1075
1155
|
const poller = oauthPollers.get(skillDir);
|
|
1076
1156
|
const finalizer = backgroundFinalizers.get(skillDir);
|
|
1077
1157
|
const pollerRunning = Boolean(poller && poller.exitCode === null && !poller.killed);
|
|
@@ -1441,6 +1521,11 @@ export async function runSetup(
|
|
|
1441
1521
|
} else {
|
|
1442
1522
|
api.logger.info('[moltbank] sandbox unchanged — no restart needed');
|
|
1443
1523
|
}
|
|
1524
|
+
|
|
1525
|
+
api.logger.info('[moltbank] [sandbox 11/11] verifying authenticated balance read...');
|
|
1526
|
+
if (!verifyMoltbankOperationalReadiness(skillDir, api)) {
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1444
1529
|
} else {
|
|
1445
1530
|
api.logger.info('[moltbank] [host 1/8] ensuring mcporter on host...');
|
|
1446
1531
|
ensureMcporter(api);
|
|
@@ -1483,19 +1568,11 @@ export async function runSetup(
|
|
|
1483
1568
|
api.logger.info('[moltbank] [host 7/8] writing/registering mcporter config...');
|
|
1484
1569
|
ensureMcporterConfig(skillDir, appBaseUrl, api);
|
|
1485
1570
|
|
|
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`)');
|
|
1571
|
+
api.logger.info('[moltbank] [host 8/8] verifying authenticated balance read...');
|
|
1572
|
+
hostReady = verifyMoltbankOperationalReadiness(skillDir, api);
|
|
1573
|
+
if (!hostReady) {
|
|
1574
|
+
return;
|
|
1497
1575
|
}
|
|
1498
|
-
hostReady = smoke.ok;
|
|
1499
1576
|
|
|
1500
1577
|
api.logger.info('[moltbank] host mode — setup completed with onboarding flow');
|
|
1501
1578
|
}
|