@reefclaw/openclaw-plugin 0.1.20 → 0.1.22
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/bridge/bridge.d.ts +10 -0
- package/bridge/bridge.js +167 -23
- package/bridge/gateway/tool-discovery.d.ts +1 -1
- package/bridge/gateway/tool-discovery.js +11 -0
- package/bridge/provider.d.ts +85 -6
- package/bridge/providers/gateway.d.ts +13 -3
- package/bridge/providers/gateway.js +18 -5
- package/bridge/providers/onboarding-commands.d.ts +38 -7
- package/bridge/providers/onboarding-commands.js +113 -13
- package/bridge/types.d.ts +1 -1
- package/bridge/types.js +5 -0
- package/ccxt/binance-private.js +6 -0
- package/config/plugin-config-io.d.ts +26 -0
- package/config/plugin-config-io.js +47 -0
- package/config/tool-gate.js +4 -0
- package/index.js +79 -12
- package/ingest/readiness-reporter.js +25 -1
- package/openclaw.plugin.json +3 -1
- package/package.json +1 -1
- package/security/sealed-credentials.d.ts +38 -0
- package/security/sealed-credentials.js +180 -0
- package/tools/clear-exchange-credentials.js +16 -3
- package/tools/hl-agent-wallet-status.d.ts +29 -0
- package/tools/hl-agent-wallet-status.js +100 -0
- package/tools/hl-provision-agent-wallet.d.ts +43 -0
- package/tools/hl-provision-agent-wallet.js +173 -0
- package/tools/set-exchange-credentials.d.ts +42 -3
- package/tools/set-exchange-credentials.js +208 -27
- package/tools/set-trading-mode.d.ts +7 -0
- package/tools/set-trading-mode.js +15 -0
- package/tools/test-exchange-credentials.d.ts +30 -3
- package/tools/test-exchange-credentials.js +173 -35
- package/types.d.ts +16 -11
- package/venues/hyperliquid/hl-agent-wallet.d.ts +29 -0
- package/venues/hyperliquid/hl-agent-wallet.js +118 -0
- package/venues/hyperliquid/hl-preflight.d.ts +28 -0
- package/venues/hyperliquid/hl-preflight.js +116 -0
package/bridge/bridge.d.ts
CHANGED
|
@@ -92,6 +92,12 @@ export declare class Bridge {
|
|
|
92
92
|
* frame. Ladder validation runs here for fast dashboard feedback and
|
|
93
93
|
* again in the plugin (defense in depth). */
|
|
94
94
|
private handleSetTradingMode;
|
|
95
|
+
/** Parse + validate the per-venue credential fields shared by the set/test
|
|
96
|
+
* handlers. Secrets (Binance secret / HL agent key) pass through once and
|
|
97
|
+
* are never cached or logged — the returned fingerprint is the Binance
|
|
98
|
+
* apiKey prefix or the PUBLIC HL master address, never secret material.
|
|
99
|
+
* Returns null after writing a 400 response when validation fails. */
|
|
100
|
+
private parseExchangeCredentialParams;
|
|
95
101
|
/** Handle a set_exchange_credentials request. The secret value passes
|
|
96
102
|
* through once and is never cached or logged; only a redacted fingerprint
|
|
97
103
|
* appears in the audit trail. */
|
|
@@ -100,6 +106,10 @@ export declare class Bridge {
|
|
|
100
106
|
* verification, nothing persisted. Same input validation + secret
|
|
101
107
|
* redaction rules as set_exchange_credentials. */
|
|
102
108
|
private handleTestExchangeCredentials;
|
|
109
|
+
/** Handle an hl_provision_agent_wallet request — the guided HL onboarding's
|
|
110
|
+
* key-minting step. Nothing secret transits in either direction: request =
|
|
111
|
+
* public master address, response = public agent address. */
|
|
112
|
+
private handleProvisionHlAgentWallet;
|
|
103
113
|
/** Handle a clear_exchange_credentials request. No input — the method
|
|
104
114
|
* itself is the "yes I want this" signal; the webapp is expected to
|
|
105
115
|
* gate this behind a confirmation dialog before calling. */
|
package/bridge/bridge.js
CHANGED
|
@@ -498,6 +498,27 @@ export class Bridge {
|
|
|
498
498
|
this.connector.sendResponse(id, true, result);
|
|
499
499
|
return;
|
|
500
500
|
}
|
|
501
|
+
if (method === 'hl_provision_agent_wallet') {
|
|
502
|
+
const result = await this.handleProvisionHlAgentWallet(id, params);
|
|
503
|
+
if (result !== 'responded') {
|
|
504
|
+
this.connector.sendResponse(id, true, result);
|
|
505
|
+
}
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (method === 'hl_agent_wallet_status') {
|
|
509
|
+
const provider = this.provider;
|
|
510
|
+
if (!provider.getHlAgentWalletStatus) {
|
|
511
|
+
this.connector.sendResponse(id, true, {
|
|
512
|
+
ok: false,
|
|
513
|
+
message: 'Provider does not support hl_agent_wallet_status (likely mock provider)',
|
|
514
|
+
configured: false,
|
|
515
|
+
});
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const result = await provider.getHlAgentWalletStatus();
|
|
519
|
+
this.connector.sendResponse(id, true, result);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
501
522
|
if (method === 'get_bracket_config') {
|
|
502
523
|
if (!this.provider.getBracketConfig) {
|
|
503
524
|
this.connector.sendResponse(id, false, undefined, {
|
|
@@ -671,21 +692,95 @@ export class Bridge {
|
|
|
671
692
|
}
|
|
672
693
|
return outcome;
|
|
673
694
|
}
|
|
674
|
-
/**
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
|
|
695
|
+
/** Parse + validate the per-venue credential fields shared by the set/test
|
|
696
|
+
* handlers. Secrets (Binance secret / HL agent key) pass through once and
|
|
697
|
+
* are never cached or logged — the returned fingerprint is the Binance
|
|
698
|
+
* apiKey prefix or the PUBLIC HL master address, never secret material.
|
|
699
|
+
* Returns null after writing a 400 response when validation fails. */
|
|
700
|
+
parseExchangeCredentialParams(id, params) {
|
|
701
|
+
// Sealed envelope: the browser encrypted the credential payload to the
|
|
702
|
+
// BOX's transport key — the bridge cannot read it and must not try.
|
|
703
|
+
// Forward opaquely; the plugin decrypts and runs the full validation.
|
|
704
|
+
if (params?.sealed != null) {
|
|
705
|
+
const sealed = params.sealed;
|
|
706
|
+
if (typeof sealed !== 'object' || Array.isArray(sealed)) {
|
|
707
|
+
this.connector.sendResponse(id, false, undefined, {
|
|
708
|
+
code: 400,
|
|
709
|
+
message: 'sealed must be an envelope object',
|
|
710
|
+
});
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
const kid = sealed.kid;
|
|
714
|
+
return {
|
|
715
|
+
req: {
|
|
716
|
+
sealed: sealed,
|
|
717
|
+
...(params?.confirm_venue_switch === true ? { confirm_venue_switch: true } : {}),
|
|
718
|
+
},
|
|
719
|
+
fp: `sealed:${typeof kid === 'string' ? kid.slice(0, 16) : 'unknown'}`,
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
const testnet = params?.testnet === true;
|
|
723
|
+
if (params?.venue === 'hyperliquid') {
|
|
724
|
+
const walletAddress = typeof params?.walletAddress === 'string' ? params.walletAddress.trim() : '';
|
|
725
|
+
const agentPrivateKey = typeof params?.agentPrivateKey === 'string' ? params.agentPrivateKey.trim() : '';
|
|
726
|
+
// Activation-only (both fields absent) = "switch back to Hyperliquid,
|
|
727
|
+
// my credentials are already stored" — the plugin resolves it against
|
|
728
|
+
// what is on disk and errors if nothing is stored.
|
|
729
|
+
const activationOnly = walletAddress === '' && agentPrivateKey === '';
|
|
730
|
+
if (!activationOnly) {
|
|
731
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(walletAddress)) {
|
|
732
|
+
this.connector.sendResponse(id, false, undefined, {
|
|
733
|
+
code: 400,
|
|
734
|
+
message: 'walletAddress must be your MASTER account address: 0x followed by 40 hex characters',
|
|
735
|
+
});
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
if (!/^(0x)?[0-9a-fA-F]{64}$/.test(agentPrivateKey)) {
|
|
739
|
+
this.connector.sendResponse(id, false, undefined, {
|
|
740
|
+
code: 400,
|
|
741
|
+
message: 'agentPrivateKey must be an agent (API) wallet private key: 64 hex characters (0x prefix optional)',
|
|
742
|
+
});
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
req: {
|
|
748
|
+
venue: 'hyperliquid',
|
|
749
|
+
...(activationOnly ? {} : { walletAddress, agentPrivateKey }),
|
|
750
|
+
testnet,
|
|
751
|
+
...(params?.confirm_venue_switch === true ? { confirm_venue_switch: true } : {}),
|
|
752
|
+
},
|
|
753
|
+
fp: activationOnly ? '(stored)' : fingerprint(walletAddress),
|
|
754
|
+
};
|
|
755
|
+
}
|
|
678
756
|
const apiKey = typeof params?.apiKey === 'string' ? params.apiKey : '';
|
|
679
757
|
const secret = typeof params?.secret === 'string' ? params.secret : '';
|
|
680
|
-
const
|
|
681
|
-
if (apiKey.length < 8 || secret.length < 8) {
|
|
758
|
+
const activationOnly = apiKey === '' && secret === '' && params?.venue === 'binance';
|
|
759
|
+
if (!activationOnly && (apiKey.length < 8 || secret.length < 8)) {
|
|
682
760
|
this.connector.sendResponse(id, false, undefined, {
|
|
683
761
|
code: 400,
|
|
684
762
|
message: 'apiKey and secret must be at least 8 characters each',
|
|
685
763
|
});
|
|
686
|
-
return
|
|
764
|
+
return null;
|
|
687
765
|
}
|
|
688
|
-
|
|
766
|
+
return {
|
|
767
|
+
req: {
|
|
768
|
+
venue: 'binance',
|
|
769
|
+
...(activationOnly ? {} : { apiKey, secret }),
|
|
770
|
+
testnet,
|
|
771
|
+
...(params?.confirm_venue_switch === true ? { confirm_venue_switch: true } : {}),
|
|
772
|
+
},
|
|
773
|
+
fp: activationOnly ? '(stored)' : fingerprint(apiKey),
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
/** Handle a set_exchange_credentials request. The secret value passes
|
|
777
|
+
* through once and is never cached or logged; only a redacted fingerprint
|
|
778
|
+
* appears in the audit trail. */
|
|
779
|
+
async handleSetExchangeCredentials(id, params) {
|
|
780
|
+
const parsed = this.parseExchangeCredentialParams(id, params);
|
|
781
|
+
if (!parsed)
|
|
782
|
+
return 'responded';
|
|
783
|
+
const { req, fp } = parsed;
|
|
689
784
|
const provider = this.provider;
|
|
690
785
|
if (!provider.setExchangeCredentials) {
|
|
691
786
|
return {
|
|
@@ -697,13 +792,20 @@ export class Bridge {
|
|
|
697
792
|
readiness: 'UNKNOWN',
|
|
698
793
|
};
|
|
699
794
|
}
|
|
700
|
-
audit('set_exchange_credentials.start', {
|
|
701
|
-
|
|
795
|
+
audit('set_exchange_credentials.start', {
|
|
796
|
+
id,
|
|
797
|
+
venue: 'sealed' in req ? 'sealed' : req.venue,
|
|
798
|
+
fingerprint: fp,
|
|
799
|
+
testnet: 'testnet' in req ? req.testnet === true : undefined,
|
|
800
|
+
});
|
|
801
|
+
const outcome = await provider.setExchangeCredentials(req);
|
|
702
802
|
audit('set_exchange_credentials.complete', {
|
|
703
803
|
id,
|
|
704
804
|
ok: outcome.ok,
|
|
805
|
+
venue: outcome.venue,
|
|
705
806
|
fingerprint: outcome.fingerprint,
|
|
706
807
|
reconnected: outcome.reconnected,
|
|
808
|
+
restartRequired: outcome.restartRequired === true,
|
|
707
809
|
mode: outcome.mode,
|
|
708
810
|
});
|
|
709
811
|
return outcome;
|
|
@@ -712,17 +814,10 @@ export class Bridge {
|
|
|
712
814
|
* verification, nothing persisted. Same input validation + secret
|
|
713
815
|
* redaction rules as set_exchange_credentials. */
|
|
714
816
|
async handleTestExchangeCredentials(id, params) {
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
const testnet = params?.testnet === true;
|
|
718
|
-
if (apiKey.length < 8 || secret.length < 8) {
|
|
719
|
-
this.connector.sendResponse(id, false, undefined, {
|
|
720
|
-
code: 400,
|
|
721
|
-
message: 'apiKey and secret must be at least 8 characters each',
|
|
722
|
-
});
|
|
817
|
+
const parsed = this.parseExchangeCredentialParams(id, params);
|
|
818
|
+
if (!parsed)
|
|
723
819
|
return 'responded';
|
|
724
|
-
}
|
|
725
|
-
const fp = fingerprint(apiKey);
|
|
820
|
+
const { req, fp } = parsed;
|
|
726
821
|
const provider = this.provider;
|
|
727
822
|
if (!provider.testExchangeCredentials) {
|
|
728
823
|
return {
|
|
@@ -732,19 +827,68 @@ export class Bridge {
|
|
|
732
827
|
canReadBalance: false,
|
|
733
828
|
canReadPositions: false,
|
|
734
829
|
balanceUSDT: null,
|
|
735
|
-
testnet,
|
|
830
|
+
testnet: 'testnet' in req ? req.testnet === true : false,
|
|
736
831
|
errors: ['provider-unsupported'],
|
|
737
832
|
};
|
|
738
833
|
}
|
|
739
|
-
audit('test_exchange_credentials.start', {
|
|
740
|
-
|
|
834
|
+
audit('test_exchange_credentials.start', {
|
|
835
|
+
id,
|
|
836
|
+
venue: 'sealed' in req ? 'sealed' : req.venue,
|
|
837
|
+
fingerprint: fp,
|
|
838
|
+
testnet: 'testnet' in req ? req.testnet === true : undefined,
|
|
839
|
+
});
|
|
840
|
+
const outcome = await provider.testExchangeCredentials(req);
|
|
741
841
|
audit('test_exchange_credentials.complete', {
|
|
742
842
|
id,
|
|
743
843
|
ok: outcome.ok,
|
|
844
|
+
venue: outcome.venue,
|
|
744
845
|
fingerprint: outcome.fingerprint,
|
|
745
846
|
canReadBalance: outcome.canReadBalance,
|
|
746
847
|
canReadPositions: outcome.canReadPositions,
|
|
747
848
|
balanceUSDT: outcome.balanceUSDT,
|
|
849
|
+
agentApproved: outcome.agentApproved,
|
|
850
|
+
});
|
|
851
|
+
return outcome;
|
|
852
|
+
}
|
|
853
|
+
/** Handle an hl_provision_agent_wallet request — the guided HL onboarding's
|
|
854
|
+
* key-minting step. Nothing secret transits in either direction: request =
|
|
855
|
+
* public master address, response = public agent address. */
|
|
856
|
+
async handleProvisionHlAgentWallet(id, params) {
|
|
857
|
+
const walletAddress = typeof params?.walletAddress === 'string' ? params.walletAddress.trim() : '';
|
|
858
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(walletAddress)) {
|
|
859
|
+
this.connector.sendResponse(id, false, undefined, {
|
|
860
|
+
code: 400,
|
|
861
|
+
message: 'walletAddress must be your MASTER account address: 0x followed by 40 hex characters',
|
|
862
|
+
});
|
|
863
|
+
return 'responded';
|
|
864
|
+
}
|
|
865
|
+
const provider = this.provider;
|
|
866
|
+
if (!provider.provisionHlAgentWallet) {
|
|
867
|
+
return {
|
|
868
|
+
ok: false,
|
|
869
|
+
message: 'Provider does not support hl_provision_agent_wallet (likely mock provider)',
|
|
870
|
+
venue: 'hyperliquid',
|
|
871
|
+
mode: this.lastKnownTradingMode ?? 'PAPER',
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
audit('hl_provision_agent_wallet.start', {
|
|
875
|
+
id,
|
|
876
|
+
master: fingerprint(walletAddress),
|
|
877
|
+
testnet: params?.testnet === true,
|
|
878
|
+
regenerate: params?.regenerate === true,
|
|
879
|
+
});
|
|
880
|
+
const outcome = await provider.provisionHlAgentWallet({
|
|
881
|
+
walletAddress,
|
|
882
|
+
testnet: params?.testnet === true,
|
|
883
|
+
regenerate: params?.regenerate === true,
|
|
884
|
+
confirm_venue_switch: params?.confirm_venue_switch === true,
|
|
885
|
+
});
|
|
886
|
+
audit('hl_provision_agent_wallet.complete', {
|
|
887
|
+
id,
|
|
888
|
+
ok: outcome.ok,
|
|
889
|
+
agentAddress: outcome.agentAddress, // public — the point of the flow
|
|
890
|
+
existing: outcome.existing === true,
|
|
891
|
+
restartRequired: outcome.restartRequired === true,
|
|
748
892
|
});
|
|
749
893
|
return outcome;
|
|
750
894
|
}
|
|
@@ -3,7 +3,7 @@ import { GatewayHttpClient, GatewayHttpError } from './gateway-http-client.js';
|
|
|
3
3
|
* Logical tool names used by ReefClaw internally.
|
|
4
4
|
* Each maps to one or more candidate actual tool names on the gateway.
|
|
5
5
|
*/
|
|
6
|
-
export type LogicalTool = 'fetch_ticker' | 'fetch_balance' | 'fetch_ohlcv' | 'fetch_positions' | 'fetch_open_orders' | 'cancel_all_orders' | 'cancel_order' | 'close_position' | 'create_order' | 'get_market_structure' | 'get_crypto_metrics' | 'get_market_intel' | 'set_trading_mode' | 'set_exchange_credentials' | 'test_exchange_credentials' | 'clear_exchange_credentials' | 'get_bracket_config' | 'set_bracket_requirement';
|
|
6
|
+
export type LogicalTool = 'fetch_ticker' | 'fetch_balance' | 'fetch_ohlcv' | 'fetch_positions' | 'fetch_open_orders' | 'cancel_all_orders' | 'cancel_order' | 'close_position' | 'create_order' | 'get_market_structure' | 'get_crypto_metrics' | 'get_market_intel' | 'set_trading_mode' | 'set_exchange_credentials' | 'test_exchange_credentials' | 'clear_exchange_credentials' | 'hl_provision_agent_wallet' | 'hl_agent_wallet_status' | 'get_bracket_config' | 'set_bracket_requirement';
|
|
7
7
|
/**
|
|
8
8
|
* Build the test args used when probing each logical tool.
|
|
9
9
|
*
|
|
@@ -20,6 +20,8 @@ const OPTIONAL_TOOLS = [
|
|
|
20
20
|
'set_exchange_credentials',
|
|
21
21
|
'test_exchange_credentials',
|
|
22
22
|
'clear_exchange_credentials',
|
|
23
|
+
'hl_provision_agent_wallet',
|
|
24
|
+
'hl_agent_wallet_status',
|
|
23
25
|
'get_bracket_config',
|
|
24
26
|
'set_bracket_requirement',
|
|
25
27
|
];
|
|
@@ -44,6 +46,8 @@ const CANDIDATES = {
|
|
|
44
46
|
set_exchange_credentials: ['set_exchange_credentials'],
|
|
45
47
|
test_exchange_credentials: ['test_exchange_credentials'],
|
|
46
48
|
clear_exchange_credentials: ['clear_exchange_credentials'],
|
|
49
|
+
hl_provision_agent_wallet: ['hl_provision_agent_wallet'],
|
|
50
|
+
hl_agent_wallet_status: ['hl_agent_wallet_status'],
|
|
47
51
|
get_bracket_config: ['get_bracket_config'],
|
|
48
52
|
set_bracket_requirement: ['set_bracket_requirement'],
|
|
49
53
|
};
|
|
@@ -108,6 +112,13 @@ export function buildProbeArgs(symbol = DEFAULT_PROBE_SYMBOL) {
|
|
|
108
112
|
// clear_exchange_credentials: tool requires confirm:true. Probing without
|
|
109
113
|
// it triggers the safety gate early-return — tool exists, nothing wiped.
|
|
110
114
|
clear_exchange_credentials: {},
|
|
115
|
+
// Provision: empty walletAddress fails the 0x-hex validator before any
|
|
116
|
+
// key is minted or written. Status: `probe:true` is the tool's dedicated
|
|
117
|
+
// discovery path — it returns before touching config or the network (a
|
|
118
|
+
// real status call runs HL /info reads that could outlive the 8s probe
|
|
119
|
+
// timeout and mark the tool missing forever).
|
|
120
|
+
hl_provision_agent_wallet: { walletAddress: '' },
|
|
121
|
+
hl_agent_wallet_status: { probe: true },
|
|
111
122
|
// Bracket-config probes: no-arg read; set with an invalid flag triggers early-return.
|
|
112
123
|
get_bracket_config: {},
|
|
113
124
|
set_bracket_requirement: { flag: '__probe__', value: false },
|
package/bridge/provider.d.ts
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
import type { EmergencyAction, ReconciliationSnapshot, TickerData, CandleData, OrderUpdatePayload, AgentStateData, RiskUpdatePayload, ChatMessage, MarketStructureData, CryptoMetricsData, VolumeAnalysisData, TradeJournalEntry, RegimeData, SignalData, MissionData, AnalyticsData, ShadowComparisonData, TradingModeData, DecisionTraceData } from './types.js';
|
|
2
2
|
import type { TradingMode } from '@reefclaw/shared';
|
|
3
|
+
/** Per-venue credential shapes for the operator set/test RPCs. Binance is an
|
|
4
|
+
* HMAC key pair; Hyperliquid is a MASTER account address (public — queries)
|
|
5
|
+
* plus an approved AGENT wallet private key (signs only, cannot withdraw).
|
|
6
|
+
* The `sealed` variant carries an end-to-end encrypted envelope the browser
|
|
7
|
+
* sealed to the box's transport key — the bridge forwards it OPAQUELY (it
|
|
8
|
+
* cannot and must not read it); all validation happens plugin-side after
|
|
9
|
+
* decryption. A discriminated union so a caller can never mix the shapes. */
|
|
10
|
+
export type ExchangeCredentialsRequest = {
|
|
11
|
+
venue: 'binance';
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
secret?: string;
|
|
14
|
+
testnet?: boolean;
|
|
15
|
+
confirm_venue_switch?: boolean;
|
|
16
|
+
} | {
|
|
17
|
+
venue: 'hyperliquid';
|
|
18
|
+
walletAddress?: string;
|
|
19
|
+
agentPrivateKey?: string;
|
|
20
|
+
testnet?: boolean;
|
|
21
|
+
confirm_venue_switch?: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
sealed: Record<string, unknown>;
|
|
24
|
+
confirm_venue_switch?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/** Result of hl_provision_agent_wallet — the box-generated agent wallet. */
|
|
27
|
+
export interface HlProvisionOutcome {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
message: string;
|
|
30
|
+
venue: 'hyperliquid';
|
|
31
|
+
agentAddress?: string;
|
|
32
|
+
masterAddress?: string;
|
|
33
|
+
testnet?: boolean;
|
|
34
|
+
existing?: boolean;
|
|
35
|
+
masterMatches?: boolean;
|
|
36
|
+
restartRequired?: boolean;
|
|
37
|
+
mode: string;
|
|
38
|
+
requiresVenueSwitchConfirm?: boolean;
|
|
39
|
+
currentVenue?: string;
|
|
40
|
+
currentMode?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Result of hl_agent_wallet_status — approval polling for the guided flow. */
|
|
43
|
+
export interface HlAgentWalletStatusOutcome {
|
|
44
|
+
ok: boolean;
|
|
45
|
+
message: string;
|
|
46
|
+
configured: boolean;
|
|
47
|
+
masterAddress?: string;
|
|
48
|
+
agentAddress?: string;
|
|
49
|
+
testnet?: boolean;
|
|
50
|
+
approved?: boolean | null;
|
|
51
|
+
validUntil?: number | null;
|
|
52
|
+
balanceUSDC?: number | null;
|
|
53
|
+
warnings?: string[];
|
|
54
|
+
restartRequired?: boolean;
|
|
55
|
+
}
|
|
3
56
|
/** Fill error data emitted when a limit fill fails */
|
|
4
57
|
export interface FillErrorData {
|
|
5
58
|
orderId: string;
|
|
@@ -95,19 +148,29 @@ export interface OpenClawProvider {
|
|
|
95
148
|
readiness: string;
|
|
96
149
|
reason?: string;
|
|
97
150
|
}>;
|
|
98
|
-
/** Operator-only: set
|
|
99
|
-
* to the plugin-local config file and never retained
|
|
100
|
-
|
|
151
|
+
/** Operator-only: set exchange credentials, per venue. The raw secret /
|
|
152
|
+
* agent key is written to the plugin-local config file and never retained
|
|
153
|
+
* by the skill. */
|
|
154
|
+
setExchangeCredentials?(req: ExchangeCredentialsRequest): Promise<{
|
|
101
155
|
ok: boolean;
|
|
102
156
|
message: string;
|
|
103
157
|
fingerprint: string;
|
|
104
158
|
reconnected: boolean;
|
|
105
159
|
mode: TradingMode;
|
|
106
160
|
readiness: string;
|
|
161
|
+
venue?: string;
|
|
162
|
+
/** True when the stored venue differs from the booted venue — the
|
|
163
|
+
* operator must restart the agent before the change takes effect. */
|
|
164
|
+
restartRequired?: boolean;
|
|
165
|
+
/** ok:false + this ⇒ the venue switch needs acknowledgement; retry with
|
|
166
|
+
* confirm_venue_switch:true. */
|
|
167
|
+
requiresVenueSwitchConfirm?: boolean;
|
|
168
|
+
currentVenue?: string;
|
|
169
|
+
currentMode?: string;
|
|
107
170
|
}>;
|
|
108
|
-
/** Operator-only: verify
|
|
109
|
-
*
|
|
110
|
-
testExchangeCredentials?(
|
|
171
|
+
/** Operator-only: verify exchange credentials with transient read-only
|
|
172
|
+
* calls, per venue. Nothing is persisted. */
|
|
173
|
+
testExchangeCredentials?(req: ExchangeCredentialsRequest): Promise<{
|
|
111
174
|
ok: boolean;
|
|
112
175
|
message: string;
|
|
113
176
|
fingerprint: string;
|
|
@@ -116,7 +179,23 @@ export interface OpenClawProvider {
|
|
|
116
179
|
balanceUSDT: number | null;
|
|
117
180
|
testnet: boolean;
|
|
118
181
|
errors: string[];
|
|
182
|
+
venue?: string;
|
|
183
|
+
agentAddress?: string;
|
|
184
|
+
agentApproved?: boolean | null;
|
|
185
|
+
agentValidUntil?: number | null;
|
|
186
|
+
warnings?: string[];
|
|
119
187
|
}>;
|
|
188
|
+
/** Operator-only: generate a Hyperliquid agent wallet ON the trader's box
|
|
189
|
+
* (the private key never leaves it) and return the public address. */
|
|
190
|
+
provisionHlAgentWallet?(args: {
|
|
191
|
+
walletAddress: string;
|
|
192
|
+
testnet?: boolean;
|
|
193
|
+
regenerate?: boolean;
|
|
194
|
+
confirm_venue_switch?: boolean;
|
|
195
|
+
}): Promise<HlProvisionOutcome>;
|
|
196
|
+
/** Operator-only: approval/balance status of the box's provisioned HL agent
|
|
197
|
+
* wallet (polled by the dashboard's guided flow after the wallet signature). */
|
|
198
|
+
getHlAgentWalletStatus?(): Promise<HlAgentWalletStatusOutcome>;
|
|
120
199
|
/** Operator-only: remove stored credentials from the plugin config and
|
|
121
200
|
* de-escalate to PAPER mode if currently running in a live mode. */
|
|
122
201
|
clearExchangeCredentials?(): Promise<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawProvider, ProviderEvents, ProviderEventName } from '../provider.js';
|
|
1
|
+
import type { OpenClawProvider, ProviderEvents, ProviderEventName, ExchangeCredentialsRequest, HlProvisionOutcome, HlAgentWalletStatusOutcome } from '../provider.js';
|
|
2
2
|
import type { EmergencyAction, ReconciliationSnapshot, TradingMode } from '../types.js';
|
|
3
3
|
import type { GatewayConfig } from '../gateway/gateway-config.js';
|
|
4
4
|
import { type GetBracketConfigOutcome, type SetBracketRequirementOutcome, type BracketRequirementFlag, type SetTradingModeOutcome, type SetExchangeCredentialsOutcome, type TestExchangeCredentialsOutcome, type ClearExchangeCredentialsOutcome } from './onboarding-commands.js';
|
|
@@ -218,9 +218,19 @@ export declare class GatewayProvider implements OpenClawProvider {
|
|
|
218
218
|
error?: string;
|
|
219
219
|
}>;
|
|
220
220
|
setTradingMode(mode: TradingMode, acknowledged?: boolean): Promise<SetTradingModeOutcome>;
|
|
221
|
-
setExchangeCredentials(
|
|
222
|
-
testExchangeCredentials(
|
|
221
|
+
setExchangeCredentials(req: ExchangeCredentialsRequest): Promise<SetExchangeCredentialsOutcome>;
|
|
222
|
+
testExchangeCredentials(req: ExchangeCredentialsRequest): Promise<TestExchangeCredentialsOutcome>;
|
|
223
223
|
clearExchangeCredentials(): Promise<ClearExchangeCredentialsOutcome>;
|
|
224
|
+
/** Operator-only. Mint an HL agent wallet on the box; returns the public
|
|
225
|
+
* address only (guided onboarding). */
|
|
226
|
+
provisionHlAgentWallet(args: {
|
|
227
|
+
walletAddress: string;
|
|
228
|
+
testnet?: boolean;
|
|
229
|
+
regenerate?: boolean;
|
|
230
|
+
confirm_venue_switch?: boolean;
|
|
231
|
+
}): Promise<HlProvisionOutcome>;
|
|
232
|
+
/** Operator-only. Approval/balance status of the provisioned HL wallet. */
|
|
233
|
+
getHlAgentWalletStatus(): Promise<HlAgentWalletStatusOutcome>;
|
|
224
234
|
/** Operator-only. Read the current bracket-orders config from the plugin. */
|
|
225
235
|
getBracketConfig(): Promise<GetBracketConfigOutcome>;
|
|
226
236
|
/** Operator-only. Flip requireStopLoss or requireTakeProfit. */
|
|
@@ -17,7 +17,7 @@ import { ensureHeartbeatCron, isHeartbeatLikeName } from '../gateway/heartbeat-c
|
|
|
17
17
|
import { parseIdentityName } from '../utils/identity-name.js';
|
|
18
18
|
import { computeEquity as _computeEquity, computePositionNotional as _computePositionNotional, computeRiskMetrics as _computeRiskMetrics, DEFAULT_RISK_LIMITS, DRAWDOWN_ZONE_THRESHOLDS, TENANT_LIMIT_BOUNDS, boundedNum, validateDrawdownLadder, } from './risk-calculator.js';
|
|
19
19
|
import { executeKill as _executeKill, executeFlatten as _executeFlatten, executePause as _executePause, executeResume as _executeResume, } from './emergency-commands.js';
|
|
20
|
-
import { executeSetTradingMode, executeGetBracketConfig, executeSetBracketRequirement, executeSetExchangeCredentials, executeTestExchangeCredentials, executeClearExchangeCredentials, } from './onboarding-commands.js';
|
|
20
|
+
import { executeSetTradingMode, executeGetBracketConfig, executeSetBracketRequirement, executeSetExchangeCredentials, executeTestExchangeCredentials, executeClearExchangeCredentials, executeProvisionHlAgentWallet, executeHlAgentWalletStatus, } from './onboarding-commands.js';
|
|
21
21
|
const TAG = 'gateway';
|
|
22
22
|
// ---- Day-start NAV persistence ----
|
|
23
23
|
// Persists sessionStartNav (the UTC-day P&L anchor) per date so Day P&L
|
|
@@ -684,14 +684,14 @@ export class GatewayProvider {
|
|
|
684
684
|
// four state-mutating operator tools without it (audit F12).
|
|
685
685
|
{ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken }, mode, acknowledged);
|
|
686
686
|
}
|
|
687
|
-
async setExchangeCredentials(
|
|
688
|
-
return executeSetExchangeCredentials({ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken },
|
|
687
|
+
async setExchangeCredentials(req) {
|
|
688
|
+
return executeSetExchangeCredentials({ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken }, req);
|
|
689
689
|
}
|
|
690
|
-
async testExchangeCredentials(
|
|
690
|
+
async testExchangeCredentials(req) {
|
|
691
691
|
return executeTestExchangeCredentials(
|
|
692
692
|
// operatorToken required since the SkillSpector hardening: the plugin
|
|
693
693
|
// refuses the credential-validation call without dashboard provenance.
|
|
694
|
-
{ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken },
|
|
694
|
+
{ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken }, req);
|
|
695
695
|
}
|
|
696
696
|
async clearExchangeCredentials() {
|
|
697
697
|
return executeClearExchangeCredentials({
|
|
@@ -700,6 +700,19 @@ export class GatewayProvider {
|
|
|
700
700
|
operatorToken: this.config.connectionToken,
|
|
701
701
|
});
|
|
702
702
|
}
|
|
703
|
+
/** Operator-only. Mint an HL agent wallet on the box; returns the public
|
|
704
|
+
* address only (guided onboarding). */
|
|
705
|
+
async provisionHlAgentWallet(args) {
|
|
706
|
+
return executeProvisionHlAgentWallet({ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken }, args);
|
|
707
|
+
}
|
|
708
|
+
/** Operator-only. Approval/balance status of the provisioned HL wallet. */
|
|
709
|
+
async getHlAgentWalletStatus() {
|
|
710
|
+
return executeHlAgentWalletStatus({
|
|
711
|
+
http: this.http,
|
|
712
|
+
toolMap: this.toolMap,
|
|
713
|
+
operatorToken: this.config.connectionToken,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
703
716
|
/** Operator-only. Read the current bracket-orders config from the plugin. */
|
|
704
717
|
async getBracketConfig() {
|
|
705
718
|
return executeGetBracketConfig({ http: this.http, toolMap: this.toolMap });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GatewayHttpClient } from '../gateway/gateway-http-client.js';
|
|
2
2
|
import type { ToolMap } from '../gateway/tool-discovery.js';
|
|
3
|
+
import type { ExchangeCredentialsRequest, HlProvisionOutcome, HlAgentWalletStatusOutcome } from '../provider.js';
|
|
3
4
|
import type { TradingMode } from '@reefclaw/shared';
|
|
4
5
|
export interface SetTradingModeOutcome {
|
|
5
6
|
ok: boolean;
|
|
@@ -16,6 +17,15 @@ export interface SetExchangeCredentialsOutcome {
|
|
|
16
17
|
reconnected: boolean;
|
|
17
18
|
mode: TradingMode;
|
|
18
19
|
readiness: string;
|
|
20
|
+
/** Venue the credentials were stored for (plugin ≥ HL product surface). */
|
|
21
|
+
venue?: string;
|
|
22
|
+
/** True when the stored venue differs from the booted venue — the operator
|
|
23
|
+
* must restart the agent before the change takes effect. */
|
|
24
|
+
restartRequired?: boolean;
|
|
25
|
+
/** ok:false + this ⇒ retry with confirm_venue_switch:true after warning. */
|
|
26
|
+
requiresVenueSwitchConfirm?: boolean;
|
|
27
|
+
currentVenue?: string;
|
|
28
|
+
currentMode?: string;
|
|
19
29
|
}
|
|
20
30
|
export interface TestExchangeCredentialsOutcome {
|
|
21
31
|
ok: boolean;
|
|
@@ -23,9 +33,18 @@ export interface TestExchangeCredentialsOutcome {
|
|
|
23
33
|
fingerprint: string;
|
|
24
34
|
canReadBalance: boolean;
|
|
25
35
|
canReadPositions: boolean;
|
|
36
|
+
/** Quote-currency balance (USDT on Binance, USDC on HL); name is historical. */
|
|
26
37
|
balanceUSDT: number | null;
|
|
27
38
|
testnet: boolean;
|
|
28
39
|
errors: string[];
|
|
40
|
+
venue?: string;
|
|
41
|
+
/** HL only: address the tested agent key controls (public). */
|
|
42
|
+
agentAddress?: string;
|
|
43
|
+
/** HL only: definitive approval verdict, or null when unverifiable. */
|
|
44
|
+
agentApproved?: boolean | null;
|
|
45
|
+
/** HL only: approval expiry (epoch ms) when the exchange reports one. */
|
|
46
|
+
agentValidUntil?: number | null;
|
|
47
|
+
warnings?: string[];
|
|
29
48
|
}
|
|
30
49
|
export interface ClearExchangeCredentialsOutcome {
|
|
31
50
|
ok: boolean;
|
|
@@ -71,10 +90,10 @@ export interface SetBracketRequirementOutcome {
|
|
|
71
90
|
/** Forward a set_trading_mode invocation to the plugin via HTTP. */
|
|
72
91
|
export declare function executeSetTradingMode(ctx: OnboardingContext, mode: TradingMode, acknowledged: boolean | undefined): Promise<SetTradingModeOutcome>;
|
|
73
92
|
/** Forward a test_exchange_credentials invocation to the plugin. The plugin
|
|
74
|
-
* does
|
|
75
|
-
*
|
|
76
|
-
* (same approach as the set path). */
|
|
77
|
-
export declare function executeTestExchangeCredentials(ctx: OnboardingContext,
|
|
93
|
+
* does throwaway read-only calls to verify the credentials without
|
|
94
|
+
* persisting them. Error messages are scrubbed of the raw secret / agent key
|
|
95
|
+
* before logging (same approach as the set path). */
|
|
96
|
+
export declare function executeTestExchangeCredentials(ctx: OnboardingContext, req: ExchangeCredentialsRequest): Promise<TestExchangeCredentialsOutcome>;
|
|
78
97
|
/** Forward a clear_exchange_credentials invocation to the plugin. Sends
|
|
79
98
|
* `confirm: true` unconditionally — the skill RPC only accepts the call
|
|
80
99
|
* at all when the bridge has decided it's legitimate, so the plugin-side
|
|
@@ -87,8 +106,20 @@ export declare function executeGetBracketConfig(ctx: OnboardingContext): Promise
|
|
|
87
106
|
/** Forward a set_bracket_requirement invocation to the plugin. The plugin
|
|
88
107
|
* side enforces flag + boolean validation; the skill layer just forwards. */
|
|
89
108
|
export declare function executeSetBracketRequirement(ctx: OnboardingContext, flag: BracketRequirementFlag, value: boolean): Promise<SetBracketRequirementOutcome>;
|
|
109
|
+
/** Forward an hl_provision_agent_wallet invocation to the plugin. No secrets
|
|
110
|
+
* transit this path in EITHER direction — the request carries a public
|
|
111
|
+
* master address; the response carries a public agent address (the private
|
|
112
|
+
* key is minted and stays on the box). */
|
|
113
|
+
export declare function executeProvisionHlAgentWallet(ctx: OnboardingContext, args: {
|
|
114
|
+
walletAddress: string;
|
|
115
|
+
testnet?: boolean;
|
|
116
|
+
regenerate?: boolean;
|
|
117
|
+
confirm_venue_switch?: boolean;
|
|
118
|
+
}): Promise<HlProvisionOutcome>;
|
|
119
|
+
/** Forward an hl_agent_wallet_status invocation to the plugin. Read-only. */
|
|
120
|
+
export declare function executeHlAgentWalletStatus(ctx: OnboardingContext): Promise<HlAgentWalletStatusOutcome>;
|
|
90
121
|
/** Forward a set_exchange_credentials invocation to the plugin via HTTP.
|
|
91
|
-
* The raw secret is sent once over TLS and never retained here —
|
|
92
|
-
* caching, no log lines with the raw value. Errors are scrubbed of the
|
|
122
|
+
* The raw secret / agent key is sent once over TLS and never retained here —
|
|
123
|
+
* no caching, no log lines with the raw value. Errors are scrubbed of the
|
|
93
124
|
* secret before logging in case it ever appears in an error chain. */
|
|
94
|
-
export declare function executeSetExchangeCredentials(ctx: OnboardingContext,
|
|
125
|
+
export declare function executeSetExchangeCredentials(ctx: OnboardingContext, req: ExchangeCredentialsRequest): Promise<SetExchangeCredentialsOutcome>;
|