openbroker 1.0.88 → 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.
- package/package.json +6 -6
- package/scripts/auto/cli.ts +6 -0
- package/scripts/auto/runtime.ts +85 -11
- package/scripts/auto/types.ts +20 -0
- package/scripts/core/client.ts +1 -1
- package/scripts/core/config.ts +3 -0
- package/scripts/core/types.ts +1 -0
- package/scripts/lib.ts +80 -0
- package/scripts/operations/bracket.ts +216 -202
- package/scripts/operations/chase.ts +184 -166
- package/scripts/setup/env.ts +11 -0
- package/scripts/setup/onboard.ts +3 -3
- package/SKILL.md +0 -1182
- package/openclaw.plugin.json +0 -86
- package/scripts/auto/dashboard-forwarder.ts +0 -77
- package/scripts/plugin/cli.ts +0 -127
- package/scripts/plugin/config-bridge.ts +0 -30
- package/scripts/plugin/index.ts +0 -133
- package/scripts/plugin/tools.ts +0 -1686
- package/scripts/plugin/types.ts +0 -158
- package/scripts/plugin/watcher.ts +0 -318
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openbroker",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Hyperliquid trading CLI - execute orders, manage positions, and run trading strategies",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"openbroker": "./bin/openbroker.js"
|
|
8
8
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
9
|
+
"main": "./scripts/lib.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./scripts/lib.ts",
|
|
12
|
+
"./package.json": "./package.json"
|
|
11
13
|
},
|
|
12
14
|
"files": [
|
|
13
15
|
"bin/",
|
|
14
16
|
"scripts/",
|
|
15
17
|
"config/example.env",
|
|
16
|
-
"openclaw.plugin.json",
|
|
17
18
|
"README.md",
|
|
18
|
-
"CHANGELOG.md"
|
|
19
|
-
"SKILL.md"
|
|
19
|
+
"CHANGELOG.md"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"onboard": "tsx scripts/setup/onboard.ts",
|
package/scripts/auto/cli.ts
CHANGED
|
@@ -130,6 +130,10 @@ async function runCommand(args: Record<string, string | boolean>, positional: st
|
|
|
130
130
|
process.exit(1);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
const envHooksToken = process.env.OPENCLAW_HOOKS_TOKEN;
|
|
134
|
+
const envGatewayPortStr = process.env.OPENCLAW_GATEWAY_PORT;
|
|
135
|
+
const envGatewayPort = envGatewayPortStr ? parseInt(envGatewayPortStr, 10) : undefined;
|
|
136
|
+
|
|
133
137
|
const automation = await startAutomation({
|
|
134
138
|
scriptPath,
|
|
135
139
|
id,
|
|
@@ -138,6 +142,8 @@ async function runCommand(args: Record<string, string | boolean>, positional: st
|
|
|
138
142
|
pollIntervalMs,
|
|
139
143
|
useWebSocket,
|
|
140
144
|
initialState: Object.keys(initialState).length > 0 ? initialState : undefined,
|
|
145
|
+
hooksToken: envHooksToken,
|
|
146
|
+
gatewayPort: envGatewayPort && !isNaN(envGatewayPort) ? envGatewayPort : undefined,
|
|
141
147
|
});
|
|
142
148
|
|
|
143
149
|
// Graceful shutdown on SIGINT/SIGTERM
|
package/scripts/auto/runtime.ts
CHANGED
|
@@ -15,9 +15,9 @@ import { AutomationEventBus } from './events.js';
|
|
|
15
15
|
import { loadAutomation } from './loader.js';
|
|
16
16
|
import { registerAutomation, unregisterAutomation, getRegisteredAutomations as getRegisteredFromFile } from './registry.js';
|
|
17
17
|
import { createAutomationAudit, toSerializable, type AutomationAuditSink } from './audit.js';
|
|
18
|
-
import { withDashboardForwarder, forwardAgentAction } from './dashboard-forwarder.js';
|
|
19
18
|
import type {
|
|
20
19
|
AutomationAPI,
|
|
20
|
+
AutomationAuditObserver,
|
|
21
21
|
AutomationEventPayloads,
|
|
22
22
|
AutomationEventType,
|
|
23
23
|
AutomationLogger,
|
|
@@ -30,6 +30,70 @@ import type {
|
|
|
30
30
|
RunningAutomation,
|
|
31
31
|
} from './types.js';
|
|
32
32
|
|
|
33
|
+
// ── Observer fan-out ────────────────────────────────────────────────
|
|
34
|
+
//
|
|
35
|
+
// External monitoring packages (e.g. `openbroker-monitoring`) plug into
|
|
36
|
+
// the audit pipeline as observers. We auto-load them via convention
|
|
37
|
+
// dynamic-import: any package whose default export is a factory returning
|
|
38
|
+
// an `AutomationAuditObserver` (or null) and that resolves through Node's
|
|
39
|
+
// module resolver gets wired in at startup.
|
|
40
|
+
|
|
41
|
+
const CONVENTION_OBSERVER_PACKAGES = ['openbroker-monitoring'];
|
|
42
|
+
|
|
43
|
+
type ObserverFactory =
|
|
44
|
+
| AutomationAuditObserver
|
|
45
|
+
| ((opts?: unknown) => AutomationAuditObserver | null | undefined);
|
|
46
|
+
|
|
47
|
+
async function loadConventionObservers(log: AutomationLogger): Promise<AutomationAuditObserver[]> {
|
|
48
|
+
const observers: AutomationAuditObserver[] = [];
|
|
49
|
+
for (const name of CONVENTION_OBSERVER_PACKAGES) {
|
|
50
|
+
try {
|
|
51
|
+
const mod = await import(name);
|
|
52
|
+
const exported: ObserverFactory | undefined = mod.default ?? mod;
|
|
53
|
+
const observer = typeof exported === 'function' ? exported() : exported;
|
|
54
|
+
if (observer && typeof observer === 'object') {
|
|
55
|
+
observers.push(observer as AutomationAuditObserver);
|
|
56
|
+
log.debug(`Loaded audit observer: ${name}`);
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const code = (err as NodeJS.ErrnoException | undefined)?.code;
|
|
60
|
+
if (code !== 'ERR_MODULE_NOT_FOUND' && code !== 'MODULE_NOT_FOUND') {
|
|
61
|
+
log.warn(`Failed to load audit observer "${name}": ${err instanceof Error ? err.message : String(err)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return observers;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function fanOutNote(observers: AutomationAuditObserver[], kind: string, payload?: unknown): void {
|
|
69
|
+
for (const o of observers) {
|
|
70
|
+
try { o.onNote?.(kind, payload); } catch { /* observer must not break runtime */ }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function fanOutMetric(
|
|
75
|
+
observers: AutomationAuditObserver[],
|
|
76
|
+
name: string,
|
|
77
|
+
value: number,
|
|
78
|
+
tags?: Record<string, unknown>,
|
|
79
|
+
): void {
|
|
80
|
+
for (const o of observers) {
|
|
81
|
+
try { o.onMetric?.(name, value, tags); } catch { /* observer must not break runtime */ }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function fanOutAgentAction(
|
|
86
|
+
observers: AutomationAuditObserver[],
|
|
87
|
+
action: string,
|
|
88
|
+
status: 'success' | 'error',
|
|
89
|
+
details: Record<string, unknown>,
|
|
90
|
+
txHash?: string,
|
|
91
|
+
): void {
|
|
92
|
+
for (const o of observers) {
|
|
93
|
+
try { o.onAgentAction?.(action, status, details, txHash); } catch { /* observer must not break runtime */ }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
33
97
|
const STATE_DIR = path.join(os.homedir(), '.openbroker', 'state');
|
|
34
98
|
const AUDITED_WRITE_METHODS = new Set([
|
|
35
99
|
'order', 'marketOrder', 'limitOrder', 'triggerOrder',
|
|
@@ -155,6 +219,7 @@ function createAuditedClient(
|
|
|
155
219
|
client: HyperliquidClient,
|
|
156
220
|
audit: AutomationAuditSink,
|
|
157
221
|
dryRun: boolean,
|
|
222
|
+
observers: AutomationAuditObserver[],
|
|
158
223
|
): HyperliquidClient {
|
|
159
224
|
return new Proxy(client, {
|
|
160
225
|
get(target, prop, receiver) {
|
|
@@ -179,7 +244,8 @@ function createAuditedClient(
|
|
|
179
244
|
result,
|
|
180
245
|
dryRun,
|
|
181
246
|
});
|
|
182
|
-
|
|
247
|
+
fanOutAgentAction(
|
|
248
|
+
observers,
|
|
183
249
|
prop,
|
|
184
250
|
'success',
|
|
185
251
|
{ args: toSerializable(args), result: toSerializable(result), dryRun },
|
|
@@ -193,7 +259,8 @@ function createAuditedClient(
|
|
|
193
259
|
error,
|
|
194
260
|
dryRun,
|
|
195
261
|
});
|
|
196
|
-
|
|
262
|
+
fanOutAgentAction(
|
|
263
|
+
observers,
|
|
197
264
|
prop,
|
|
198
265
|
'error',
|
|
199
266
|
{ args: toSerializable(args), error: String(error), dryRun },
|
|
@@ -282,11 +349,11 @@ function createPublish(
|
|
|
282
349
|
hooksToken?: string,
|
|
283
350
|
): (message: string, options?: PublishOptions) => Promise<boolean> {
|
|
284
351
|
return async (message: string, options?: PublishOptions): Promise<boolean> => {
|
|
285
|
-
const token = hooksToken
|
|
286
|
-
const port = gatewayPort ||
|
|
352
|
+
const token = hooksToken;
|
|
353
|
+
const port = gatewayPort || 18789;
|
|
287
354
|
|
|
288
355
|
if (!token) {
|
|
289
|
-
log.debug('publish() skipped — no hooks token configured (set OPENCLAW_HOOKS_TOKEN
|
|
356
|
+
log.debug('publish() skipped — no hooks token configured (pass --hooks-token, set OPENCLAW_HOOKS_TOKEN before invoking the CLI, or configure plugin.hooksToken)');
|
|
290
357
|
return false;
|
|
291
358
|
}
|
|
292
359
|
|
|
@@ -425,8 +492,9 @@ export async function startAutomation(options: RuntimeOptions): Promise<RunningA
|
|
|
425
492
|
stateController.attachAudit(audit);
|
|
426
493
|
|
|
427
494
|
const log = createLogger(id, verbose, audit);
|
|
495
|
+
const observers = await loadConventionObservers(log);
|
|
428
496
|
const baseClient = dryRun ? createDryClient(rawClient, log) : rawClient;
|
|
429
|
-
const client = createAuditedClient(baseClient, audit, dryRun);
|
|
497
|
+
const client = createAuditedClient(baseClient, audit, dryRun, observers);
|
|
430
498
|
|
|
431
499
|
const startHooks: Array<() => void | Promise<void>> = [];
|
|
432
500
|
const stopHooks: Array<() => void | Promise<void>> = [];
|
|
@@ -435,10 +503,16 @@ export async function startAutomation(options: RuntimeOptions): Promise<RunningA
|
|
|
435
503
|
|
|
436
504
|
// Build the API object
|
|
437
505
|
const publish = createAuditedPublish(createPublish(id, log, gatewayPort, hooksToken), audit);
|
|
438
|
-
const auditApi: AutomationAudit =
|
|
439
|
-
record: (kind: string, payload?: unknown) =>
|
|
440
|
-
|
|
441
|
-
|
|
506
|
+
const auditApi: AutomationAudit = {
|
|
507
|
+
record: (kind: string, payload?: unknown) => {
|
|
508
|
+
audit.recordNote(kind, payload);
|
|
509
|
+
fanOutNote(observers, kind, payload);
|
|
510
|
+
},
|
|
511
|
+
metric: (name: string, value: number, tags?: Record<string, unknown>) => {
|
|
512
|
+
audit.recordMetric(name, value, tags);
|
|
513
|
+
fanOutMetric(observers, name, value, tags);
|
|
514
|
+
},
|
|
515
|
+
};
|
|
442
516
|
const api: AutomationAPI = {
|
|
443
517
|
client,
|
|
444
518
|
utils: { roundPrice, roundSize, sleep, normalizeCoin, formatUsd, formatPercent, annualizeFundingRate },
|
package/scripts/auto/types.ts
CHANGED
|
@@ -116,6 +116,26 @@ export interface AutomationAudit {
|
|
|
116
116
|
metric(name: string, value: number, tags?: Record<string, unknown>): void;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Hook for external monitoring packages (e.g. `openbroker-monitoring`) to
|
|
121
|
+
* receive audit events. The runtime fans out every note, metric, and
|
|
122
|
+
* audited write-method call to every observer it has loaded.
|
|
123
|
+
*
|
|
124
|
+
* Observers are auto-loaded via convention dynamic-import — the runtime
|
|
125
|
+
* tries `await import('openbroker-monitoring')` at startup and uses the
|
|
126
|
+
* default export (a factory returning an observer or null).
|
|
127
|
+
*/
|
|
128
|
+
export interface AutomationAuditObserver {
|
|
129
|
+
onNote?(kind: string, payload?: unknown): void;
|
|
130
|
+
onMetric?(name: string, value: number, tags?: Record<string, unknown>): void;
|
|
131
|
+
onAgentAction?(
|
|
132
|
+
action: string,
|
|
133
|
+
status: 'success' | 'error',
|
|
134
|
+
details: Record<string, unknown>,
|
|
135
|
+
txHash?: string,
|
|
136
|
+
): void;
|
|
137
|
+
}
|
|
138
|
+
|
|
119
139
|
// ── Publish (webhook) ───────────────────────────────────────────────
|
|
120
140
|
|
|
121
141
|
export interface PublishOptions {
|
package/scripts/core/client.ts
CHANGED
|
@@ -56,7 +56,7 @@ export class HyperliquidClient {
|
|
|
56
56
|
constructor(config?: OpenBrokerConfig) {
|
|
57
57
|
this.config = config ?? loadConfig();
|
|
58
58
|
this.account = privateKeyToAccount(this.config.privateKey);
|
|
59
|
-
this.verbose =
|
|
59
|
+
this.verbose = this.config.verbose;
|
|
60
60
|
|
|
61
61
|
// Initialize SDK clients
|
|
62
62
|
this.transport = new HttpTransport({ isTestnet: !isMainnet() });
|
package/scripts/core/config.ts
CHANGED
|
@@ -142,6 +142,8 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
142
142
|
// Standard API wallets (approved via approveAgent) do NOT need this.
|
|
143
143
|
const vaultAddress = process.env.HYPERLIQUID_VAULT_ADDRESS?.toLowerCase();
|
|
144
144
|
|
|
145
|
+
const verbose = process.env.VERBOSE === '1' || process.env.VERBOSE === 'true';
|
|
146
|
+
|
|
145
147
|
return {
|
|
146
148
|
baseUrl,
|
|
147
149
|
privateKey: privateKey as `0x${string}`,
|
|
@@ -153,6 +155,7 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
153
155
|
builderFee,
|
|
154
156
|
slippageBps,
|
|
155
157
|
vaultAddress,
|
|
158
|
+
verbose,
|
|
156
159
|
};
|
|
157
160
|
}
|
|
158
161
|
|
package/scripts/core/types.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface OpenBrokerConfig {
|
|
|
13
13
|
builderFee: number; // tenths of bps (10 = 1 bps)
|
|
14
14
|
slippageBps: number;
|
|
15
15
|
vaultAddress?: string; // Explicit vault address for vault trading (ERC4626 / native vaults via CoreWriter)
|
|
16
|
+
verbose: boolean; // Debug logging — set from VERBOSE env var by loadConfig()
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
// ============ Builder ============
|
package/scripts/lib.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Public library API for `openbroker`.
|
|
2
|
+
//
|
|
3
|
+
// External packages — notably `openbroker-plugin` — import from here to
|
|
4
|
+
// drive the CLI's functionality in-process (no `child_process` dispatch).
|
|
5
|
+
//
|
|
6
|
+
// Stability: every symbol re-exported here is a public API. Renames or
|
|
7
|
+
// removals are breaking changes and require a major version bump.
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
HyperliquidClient,
|
|
11
|
+
getClient,
|
|
12
|
+
} from './core/client.js';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
loadConfig,
|
|
16
|
+
isConfigured,
|
|
17
|
+
getNetwork,
|
|
18
|
+
isMainnet,
|
|
19
|
+
ensureConfigDir,
|
|
20
|
+
getConfigPath,
|
|
21
|
+
GLOBAL_CONFIG_DIR,
|
|
22
|
+
GLOBAL_ENV_PATH,
|
|
23
|
+
OPEN_BROKER_BUILDER_ADDRESS,
|
|
24
|
+
} from './core/config.js';
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
roundPrice,
|
|
28
|
+
roundSize,
|
|
29
|
+
sleep,
|
|
30
|
+
normalizeCoin,
|
|
31
|
+
formatUsd,
|
|
32
|
+
formatPercent,
|
|
33
|
+
annualizeFundingRate,
|
|
34
|
+
parseArgs,
|
|
35
|
+
getSlippagePrice,
|
|
36
|
+
getTimestampMs,
|
|
37
|
+
generateCloid,
|
|
38
|
+
orderToWire,
|
|
39
|
+
checkBuilderFeeApproval,
|
|
40
|
+
} from './core/utils.js';
|
|
41
|
+
|
|
42
|
+
export type * from './core/types.js';
|
|
43
|
+
|
|
44
|
+
// ── Operations (in-process callable) ────────────────────────────────
|
|
45
|
+
|
|
46
|
+
export { runBracket } from './operations/bracket.js';
|
|
47
|
+
export type { BracketOptions, BracketResult } from './operations/bracket.js';
|
|
48
|
+
|
|
49
|
+
export { runChase } from './operations/chase.js';
|
|
50
|
+
export type { ChaseOptions, ChaseResult } from './operations/chase.js';
|
|
51
|
+
|
|
52
|
+
// ── Automation runtime ──────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
startAutomation,
|
|
56
|
+
getRunningAutomations,
|
|
57
|
+
getAutomation,
|
|
58
|
+
getRegisteredAutomations,
|
|
59
|
+
} from './auto/runtime.js';
|
|
60
|
+
export type { RuntimeOptions } from './auto/runtime.js';
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
resolveScriptPath,
|
|
64
|
+
resolveExamplePath,
|
|
65
|
+
listAutomations,
|
|
66
|
+
listExamples,
|
|
67
|
+
loadExampleConfigs,
|
|
68
|
+
ensureAutomationsDir,
|
|
69
|
+
loadAutomation,
|
|
70
|
+
} from './auto/loader.js';
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
registerAutomation,
|
|
74
|
+
unregisterAutomation,
|
|
75
|
+
cleanRegistry,
|
|
76
|
+
getAutomationsToRestart,
|
|
77
|
+
markAutomationError,
|
|
78
|
+
} from './auto/registry.js';
|
|
79
|
+
|
|
80
|
+
export type * from './auto/types.js';
|