openbroker 1.0.89 → 1.1.0

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.
@@ -1,86 +0,0 @@
1
- {
2
- "id": "openbroker",
3
- "name": "OpenBroker — Hyperliquid Trading",
4
- "version": "1.0.89",
5
- "description": "Trade on Hyperliquid DEX with background position monitoring",
6
- "configSchema": {
7
- "type": "object",
8
- "properties": {
9
- "privateKey": {
10
- "type": "string",
11
- "description": "Hyperliquid wallet private key (0x-prefixed). Falls back to HYPERLIQUID_PRIVATE_KEY env var or ~/.openbroker/.env"
12
- },
13
- "accountAddress": {
14
- "type": "string",
15
- "description": "Master account address (for API wallets). Falls back to HYPERLIQUID_ACCOUNT_ADDRESS"
16
- },
17
- "network": {
18
- "type": "string",
19
- "enum": ["mainnet", "testnet"],
20
- "default": "mainnet",
21
- "description": "Network to use: mainnet or testnet"
22
- },
23
- "hooksToken": {
24
- "type": "string",
25
- "description": "Bearer token for gateway hooks endpoint (must match hooks.token in gateway config). Falls back to OPENCLAW_HOOKS_TOKEN env var."
26
- },
27
- "watcher": {
28
- "type": "object",
29
- "properties": {
30
- "enabled": {
31
- "type": "boolean",
32
- "default": true,
33
- "description": "Enable background position watcher"
34
- },
35
- "pollIntervalMs": {
36
- "type": "number",
37
- "default": 30000,
38
- "description": "Poll interval in milliseconds"
39
- },
40
- "pnlChangeThresholdPct": {
41
- "type": "number",
42
- "default": 5,
43
- "description": "Notify when unrealized PnL changes by this percentage"
44
- },
45
- "marginUsageWarningPct": {
46
- "type": "number",
47
- "default": 80,
48
- "description": "Warn when margin usage exceeds this percentage"
49
- },
50
- "notifyOnPositionChange": {
51
- "type": "boolean",
52
- "default": true,
53
- "description": "Send hook when positions open/close/change size"
54
- },
55
- "notifyOnFunding": {
56
- "type": "boolean",
57
- "default": true,
58
- "description": "Send hook for funding rate alerts"
59
- }
60
- },
61
- "additionalProperties": false
62
- }
63
- },
64
- "additionalProperties": false
65
- },
66
- "uiHints": {
67
- "privateKey": {
68
- "label": "Private Key",
69
- "sensitive": true,
70
- "placeholder": "0x..."
71
- },
72
- "accountAddress": {
73
- "label": "Account Address",
74
- "placeholder": "0x..."
75
- },
76
- "network": {
77
- "label": "Network",
78
- "placeholder": "mainnet"
79
- },
80
- "hooksToken": {
81
- "label": "Hooks Token",
82
- "sensitive": true,
83
- "placeholder": "your-hooks-secret"
84
- }
85
- }
86
- }
@@ -1,127 +0,0 @@
1
- // CLI commands for the OpenClaw plugin
2
- // Registered via api.registerCli with Commander.js-style program
3
-
4
- import type { PluginLogger, OpenClawPluginApi } from './types.js';
5
- import type { PositionWatcher } from './watcher.js';
6
-
7
- export function registerCliCommands(
8
- api: OpenClawPluginApi,
9
- watcher: PositionWatcher | null,
10
- logger: PluginLogger,
11
- ): void {
12
- api.registerCli(({ program }) => {
13
- const ob = program.command('ob').description('OpenBroker Hyperliquid trading tools');
14
-
15
- ob
16
- .command('watch')
17
- .description('Start the position watcher in foreground (for debugging)')
18
- .option('--interval <ms>', 'Poll interval in milliseconds', '30000')
19
- .action(async (opts: unknown) => {
20
- // If watcher is already running (gateway context), show its status
21
- if (watcher && watcher.getStatus().running) {
22
- const status = watcher.getStatus();
23
- console.log('Position watcher is running as a background service.');
24
- console.log(`Account: ${status.accountAddress}`);
25
- console.log(`Tracking ${status.positions.length} position(s)`);
26
- console.log(`Events detected: ${status.eventsDetected}`);
27
- console.log(`Last poll: ${status.lastPollAt ?? 'Never'}`);
28
- return;
29
- }
30
-
31
- // CLI context: start watcher in foreground for debugging
32
- const { PositionWatcher: WatcherClass } = await import('./watcher.js');
33
- const { interval: intervalStr } = opts as { interval: string };
34
- const interval = parseInt(intervalStr, 10);
35
-
36
- const fgWatcher = new WatcherClass({
37
- logger,
38
- gatewayPort: api.gatewayPort || 0,
39
- pollIntervalMs: interval,
40
- notifyOnPositionChange: api.gatewayPort > 0,
41
- });
42
-
43
- console.log('Starting position watcher in foreground...');
44
- console.log(`Poll interval: ${interval / 1000}s`);
45
- console.log('Press Ctrl+C to stop.\n');
46
-
47
- process.on('SIGINT', async () => {
48
- await fgWatcher.stop();
49
- process.exit(0);
50
- });
51
-
52
- await fgWatcher.start();
53
-
54
- // Keep alive
55
- await new Promise(() => {});
56
- });
57
-
58
- ob
59
- .command('status')
60
- .description('Show position watcher status and current positions')
61
- .action(async () => {
62
- // If watcher is running in gateway context, show its live state
63
- if (watcher && watcher.getStatus().running) {
64
- const status = watcher.getStatus();
65
-
66
- console.log('OpenBroker Position Watcher Status');
67
- console.log('==================================\n');
68
- console.log(`Running: Yes (background service)`);
69
- console.log(`Account: ${status.accountAddress}`);
70
- console.log(`Poll interval: ${status.pollIntervalMs / 1000}s`);
71
- console.log(`Events detected: ${status.eventsDetected}`);
72
- console.log(`Last poll: ${status.lastPollAt ?? 'Never'}`);
73
- console.log(`Equity: $${status.equity ?? '?'}`);
74
- console.log(`Margin used: ${status.marginUsedPct?.toFixed(1) ?? '?'}%`);
75
-
76
- if (status.positions.length === 0) {
77
- console.log('\nNo open positions.');
78
- } else {
79
- console.log(`\nOpen Positions (${status.positions.length}):`);
80
- for (const p of status.positions) {
81
- const side = parseFloat(p.size) > 0 ? 'LONG' : 'SHORT';
82
- console.log(` ${p.coin} ${side}`);
83
- console.log(` Size: ${p.size}`);
84
- console.log(` Entry: $${p.entryPrice}`);
85
- console.log(` Unreal PnL: $${p.unrealizedPnl}`);
86
- console.log(` Liq Price: ${p.liquidationPrice ? `$${p.liquidationPrice}` : 'N/A'}`);
87
- console.log('');
88
- }
89
- }
90
- return;
91
- }
92
-
93
- // CLI context: one-shot account query
94
- console.log('OpenBroker Status (live query)\n');
95
- try {
96
- const { getClient } = await import('../core/client.js');
97
- const { formatUsd } = await import('../core/utils.js');
98
- const client = getClient();
99
- const state = await client.getUserState();
100
-
101
- console.log(`Account: ${client.address}`);
102
- console.log(`Equity: ${formatUsd(parseFloat(state.marginSummary.accountValue))}`);
103
- console.log(`Margin: ${formatUsd(parseFloat(state.marginSummary.totalMarginUsed))}`);
104
-
105
- const positions = state.assetPositions.filter(ap => parseFloat(ap.position.szi) !== 0);
106
- if (positions.length === 0) {
107
- console.log('\nNo open positions.');
108
- } else {
109
- console.log(`\nOpen Positions (${positions.length}):`);
110
- for (const ap of positions) {
111
- const p = ap.position;
112
- const side = parseFloat(p.szi) > 0 ? 'LONG' : 'SHORT';
113
- console.log(` ${p.coin} ${side} ${p.szi} @ $${p.entryPx} | PnL: $${p.unrealizedPnl} | Liq: ${p.liquidationPx ?? 'N/A'}`);
114
- }
115
- }
116
- } catch (err) {
117
- console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
118
- console.log('\nRun "openbroker setup" to configure your wallet.');
119
- }
120
-
121
- if (watcher && !watcher.getStatus().running) {
122
- console.log('\nNote: The background watcher is registered but not running.');
123
- console.log('It starts automatically with the gateway. Use "openclaw ob watch" for foreground mode.');
124
- }
125
- });
126
- }, { commands: ['ob'] });
127
- }
@@ -1,30 +0,0 @@
1
- // Maps OpenClaw plugin config → process.env vars
2
- // Only sets vars that are not already defined (env vars take priority)
3
-
4
- import type { OpenBrokerPluginConfig } from './types.js';
5
-
6
- const CONFIG_MAP: Record<string, string> = {
7
- privateKey: 'HYPERLIQUID_PRIVATE_KEY',
8
- accountAddress: 'HYPERLIQUID_ACCOUNT_ADDRESS',
9
- network: 'HYPERLIQUID_NETWORK',
10
- };
11
-
12
- /**
13
- * Inject plugin config values into process.env if not already set.
14
- *
15
- * Priority chain:
16
- * 1. Real env vars (highest — already in process.env)
17
- * 2. Plugin config (injected here)
18
- * 3. ~/.openbroker/.env (loaded by core/config.ts)
19
- * 4. Hardcoded defaults in core/config.ts
20
- */
21
- export function applyConfigBridge(pluginConfig: Record<string, unknown>): void {
22
- const config = pluginConfig as OpenBrokerPluginConfig;
23
-
24
- for (const [key, envVar] of Object.entries(CONFIG_MAP)) {
25
- const value = config[key as keyof OpenBrokerPluginConfig];
26
- if (value !== undefined && value !== null && typeof value !== 'object' && !process.env[envVar]) {
27
- process.env[envVar] = String(value);
28
- }
29
- }
30
- }
@@ -1,133 +0,0 @@
1
- // OpenClaw Plugin Entry Point for OpenBroker
2
-
3
- import type { OpenClawPluginApi, OpenBrokerPluginConfig, PluginLogger } from './types.js';
4
- import { applyConfigBridge } from './config-bridge.js';
5
- import { PositionWatcher } from './watcher.js';
6
- import { createTools } from './tools.js';
7
- import { registerCliCommands } from './cli.js';
8
-
9
- /**
10
- * AutomationService — restarts automations from the file-based registry
11
- * when the OpenClaw gateway starts. When the gateway process dies,
12
- * automations die with it. On next start, this service reads the registry
13
- * and restarts any automations that were previously running.
14
- */
15
- function createAutomationService(logger: PluginLogger, gatewayPort?: number, hooksToken?: string) {
16
- return {
17
- id: 'openbroker-automations',
18
-
19
- async start() {
20
- const { getAutomationsToRestart } = await import('../auto/registry.js');
21
- const entries = getAutomationsToRestart();
22
-
23
- if (entries.length === 0) {
24
- logger.debug('No automations to restart');
25
- return;
26
- }
27
-
28
- logger.info(`Restarting ${entries.length} automation(s) from previous session`);
29
-
30
- const { startAutomation } = await import('../auto/runtime.js');
31
- const { resolveScriptPath } = await import('../auto/loader.js');
32
-
33
- for (const entry of entries) {
34
- try {
35
- // Verify script still exists before restarting
36
- const scriptPath = resolveScriptPath(entry.scriptPath);
37
- await startAutomation({
38
- scriptPath,
39
- id: entry.id,
40
- dryRun: entry.dryRun,
41
- verbose: entry.verbose,
42
- pollIntervalMs: entry.pollIntervalMs,
43
- gatewayPort,
44
- hooksToken,
45
- });
46
- logger.info(`Restarted automation: ${entry.id}`);
47
- } catch (err) {
48
- const msg = err instanceof Error ? err.message : String(err);
49
- logger.error(`Failed to restart automation "${entry.id}": ${msg}`);
50
-
51
- // Mark as errored in registry so it doesn't retry forever
52
- const { markAutomationError } = await import('../auto/registry.js');
53
- markAutomationError(entry.id, msg);
54
- }
55
- }
56
- },
57
-
58
- async stop() {
59
- // Stop all in-process automations but keep them in the file registry
60
- // so they restart when the gateway comes back up
61
- const { getRunningAutomations } = await import('../auto/runtime.js');
62
- const running = getRunningAutomations();
63
-
64
- for (const auto of running) {
65
- try {
66
- await auto.stop({ persist: false }); // Keep in registry for restart
67
- logger.info(`Stopped automation for gateway shutdown: ${auto.id}`);
68
- } catch (err) {
69
- logger.error(`Error stopping automation "${auto.id}": ${err instanceof Error ? err.message : String(err)}`);
70
- }
71
- }
72
- },
73
- };
74
- }
75
-
76
- export default {
77
- id: 'openbroker',
78
- name: 'OpenBroker — Hyperliquid Trading',
79
-
80
- register(api: OpenClawPluginApi): void {
81
- const { logger, gatewayPort } = api;
82
- const pluginConfig = (api.pluginConfig ?? {}) as OpenBrokerPluginConfig;
83
-
84
- // 1. Apply config bridge: inject plugin config → process.env
85
- applyConfigBridge(pluginConfig as Record<string, unknown>);
86
- logger.debug('OpenBroker config bridge applied');
87
-
88
- // 2. Register background position watcher (unless disabled)
89
- let watcher: PositionWatcher | null = null;
90
- const watcherEnabled = pluginConfig.watcher?.enabled !== false;
91
-
92
- if (watcherEnabled) {
93
- watcher = new PositionWatcher({
94
- logger,
95
- gatewayPort,
96
- hooksToken: pluginConfig.hooksToken,
97
- accountAddress: pluginConfig.accountAddress
98
- || process.env.HYPERLIQUID_ACCOUNT_ADDRESS
99
- || undefined,
100
- network: pluginConfig.network || process.env.HYPERLIQUID_NETWORK,
101
- pollIntervalMs: pluginConfig.watcher?.pollIntervalMs,
102
- pnlChangeThresholdPct: pluginConfig.watcher?.pnlChangeThresholdPct,
103
- marginUsageWarningPct: pluginConfig.watcher?.marginUsageWarningPct,
104
- notifyOnPositionChange: pluginConfig.watcher?.notifyOnPositionChange,
105
- notifyOnFunding: pluginConfig.watcher?.notifyOnFunding,
106
- });
107
- api.registerService(watcher);
108
- logger.debug('OpenBroker position watcher registered');
109
- } else {
110
- logger.debug('OpenBroker position watcher disabled by config');
111
- }
112
-
113
- // 3. Register automation restart service
114
- const resolvedHooksToken = pluginConfig.hooksToken || process.env.OPENCLAW_HOOKS_TOKEN;
115
- api.registerService(createAutomationService(logger, gatewayPort, resolvedHooksToken));
116
- logger.debug('OpenBroker automation service registered');
117
-
118
- // 4. Register agent tools
119
- const tools = createTools({
120
- watcher,
121
- gatewayPort,
122
- hooksToken: pluginConfig.hooksToken || process.env.OPENCLAW_HOOKS_TOKEN,
123
- });
124
- for (const tool of tools) {
125
- api.registerTool(tool);
126
- }
127
- logger.debug(`Registered ${tools.length} OpenBroker agent tools`);
128
-
129
- // 5. Register CLI commands
130
- registerCliCommands(api, watcher, logger);
131
- logger.debug('OpenBroker CLI commands registered');
132
- },
133
- };