@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.
Files changed (37) hide show
  1. package/bridge/bridge.d.ts +10 -0
  2. package/bridge/bridge.js +167 -23
  3. package/bridge/gateway/tool-discovery.d.ts +1 -1
  4. package/bridge/gateway/tool-discovery.js +11 -0
  5. package/bridge/provider.d.ts +85 -6
  6. package/bridge/providers/gateway.d.ts +13 -3
  7. package/bridge/providers/gateway.js +18 -5
  8. package/bridge/providers/onboarding-commands.d.ts +38 -7
  9. package/bridge/providers/onboarding-commands.js +113 -13
  10. package/bridge/types.d.ts +1 -1
  11. package/bridge/types.js +5 -0
  12. package/ccxt/binance-private.js +6 -0
  13. package/config/plugin-config-io.d.ts +26 -0
  14. package/config/plugin-config-io.js +47 -0
  15. package/config/tool-gate.js +4 -0
  16. package/index.js +79 -12
  17. package/ingest/readiness-reporter.js +25 -1
  18. package/openclaw.plugin.json +3 -1
  19. package/package.json +1 -1
  20. package/security/sealed-credentials.d.ts +38 -0
  21. package/security/sealed-credentials.js +180 -0
  22. package/tools/clear-exchange-credentials.js +16 -3
  23. package/tools/hl-agent-wallet-status.d.ts +29 -0
  24. package/tools/hl-agent-wallet-status.js +100 -0
  25. package/tools/hl-provision-agent-wallet.d.ts +43 -0
  26. package/tools/hl-provision-agent-wallet.js +173 -0
  27. package/tools/set-exchange-credentials.d.ts +42 -3
  28. package/tools/set-exchange-credentials.js +208 -27
  29. package/tools/set-trading-mode.d.ts +7 -0
  30. package/tools/set-trading-mode.js +15 -0
  31. package/tools/test-exchange-credentials.d.ts +30 -3
  32. package/tools/test-exchange-credentials.js +173 -35
  33. package/types.d.ts +16 -11
  34. package/venues/hyperliquid/hl-agent-wallet.d.ts +29 -0
  35. package/venues/hyperliquid/hl-agent-wallet.js +118 -0
  36. package/venues/hyperliquid/hl-preflight.d.ts +28 -0
  37. package/venues/hyperliquid/hl-preflight.js +116 -0
@@ -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
- /** Handle a set_exchange_credentials request. The secret value passes
675
- * through once and is never cached or logged; only a redacted fingerprint
676
- * appears in the audit trail. */
677
- async handleSetExchangeCredentials(id, params) {
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 testnet = params?.testnet === true;
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 'responded';
764
+ return null;
687
765
  }
688
- const fp = fingerprint(apiKey);
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', { id, fingerprint: fp, testnet });
701
- const outcome = await provider.setExchangeCredentials(apiKey, secret, testnet);
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 apiKey = typeof params?.apiKey === 'string' ? params.apiKey : '';
716
- const secret = typeof params?.secret === 'string' ? params.secret : '';
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', { id, fingerprint: fp, testnet });
740
- const outcome = await provider.testExchangeCredentials(apiKey, secret, testnet);
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 },
@@ -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 Binance API credentials. The raw secret is written
99
- * to the plugin-local config file and never retained by the skill. */
100
- setExchangeCredentials?(apiKey: string, secret: string, testnet?: boolean): Promise<{
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 Binance API credentials with a transient
109
- * read-only call. Nothing is persisted. */
110
- testExchangeCredentials?(apiKey: string, secret: string, testnet?: boolean): Promise<{
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(apiKey: string, secret: string, testnet?: boolean): Promise<SetExchangeCredentialsOutcome>;
222
- testExchangeCredentials(apiKey: string, secret: string, testnet?: boolean): Promise<TestExchangeCredentialsOutcome>;
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(apiKey, secret, testnet) {
688
- return executeSetExchangeCredentials({ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken }, apiKey, secret, testnet);
687
+ async setExchangeCredentials(req) {
688
+ return executeSetExchangeCredentials({ http: this.http, toolMap: this.toolMap, operatorToken: this.config.connectionToken }, req);
689
689
  }
690
- async testExchangeCredentials(apiKey, secret, testnet) {
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 }, apiKey, secret, testnet);
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 a throwaway read-only call to verify permissions without persisting
75
- * the keys. Error messages are scrubbed of the raw secret before logging
76
- * (same approach as the set path). */
77
- export declare function executeTestExchangeCredentials(ctx: OnboardingContext, apiKey: string, secret: string, testnet: boolean | undefined): Promise<TestExchangeCredentialsOutcome>;
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 — no
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, apiKey: string, secret: string, testnet: boolean | undefined): Promise<SetExchangeCredentialsOutcome>;
125
+ export declare function executeSetExchangeCredentials(ctx: OnboardingContext, req: ExchangeCredentialsRequest): Promise<SetExchangeCredentialsOutcome>;