@reefclaw/connect 0.1.34 → 0.1.36

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.
@@ -96,22 +96,40 @@ export async function hlProvisionAgentWalletTool(args, deps) {
96
96
  const derived = await deriveAddressFromPrivateKey(existingKey);
97
97
  if (derived.ok) {
98
98
  const masterMatches = existingMaster != null && existingMaster.toLowerCase() === walletAddress.toLowerCase();
99
- // ★ Honour a CHANGED network on the resume path. The same keypair is
100
- // valid on both Hyperliquid networks, so switching mainnet<->testnet
101
- // must not require regenerating the wallet — but the stored flag has to
102
- // follow, or the box boots against the network the operator did NOT
103
- // pick. Before this, unticking "Use Hyperliquid testnet" after
104
- // provisioning silently kept testnet:true, and the approval was signed
105
- // for Testnet while the operator believed they were on mainnet
106
- // (observed live 2026-08-03).
107
- if (masterMatches && existingTestnet !== testnet) {
99
+ // ★ Re-ACTIVATE the venue on resume (E2E audit 2026-08-11 #6) and honour
100
+ // a CHANGED network in the same write.
101
+ //
102
+ // Venue: the resume path used to write NOTHING unless the network flag
103
+ // moved so a box whose active venue was still binance (HL provisioned
104
+ // earlier and switched back, or a live-Binance box that just clicked
105
+ // through the confirm-switch dialog above) kept `venue:'binance'` on
106
+ // disk. hl_agent_wallet_status only reports configured when
107
+ // `exchange.venue==='hyperliquid'`, so the guided flow dead-ended
108
+ // polling configured:false forever, and the confirm dialog "switched"
109
+ // nothing. By this point the venue-switch confirm gate has already run,
110
+ // so the activation write is authorized. buildVenueExchangeConfig with
111
+ // empty fields activates the venue while keeping BOTH venues'
112
+ // credentials (the merge contract).
113
+ //
114
+ // Network: the same keypair is valid on both Hyperliquid networks, so
115
+ // mainnet<->testnet must not require regenerating — but the stored flag
116
+ // has to follow, or the box boots against the network the operator did
117
+ // NOT pick (observed live 2026-08-03: approval signed for Testnet while
118
+ // the operator believed mainnet).
119
+ const needsVenueActivation = fromVenue !== 'hyperliquid';
120
+ if (masterMatches && (needsVenueActivation || existingTestnet !== testnet)) {
108
121
  try {
109
122
  updatePluginConfig({ exchange: buildVenueExchangeConfig(existingExchange, 'hyperliquid', {}, testnet) }, deps.configPath);
123
+ if (needsVenueActivation) {
124
+ logger.info(TAG, 'venue re-activated: hyperliquid (existing agent wallet resumed)');
125
+ }
126
+ if (existingTestnet !== testnet) {
127
+ logger.info(TAG, `network switched to ${testnet ? 'TESTNET' : 'MAINNET'} (same agent wallet)`);
128
+ }
110
129
  existingTestnet = testnet;
111
- logger.info(TAG, `network switched to ${testnet ? 'TESTNET' : 'MAINNET'} (same agent wallet)`);
112
130
  }
113
131
  catch (err) {
114
- logger.warn(TAG, `could not persist network change: ${err instanceof Error ? err.message : String(err)}`);
132
+ logger.warn(TAG, `could not persist venue/network change: ${err instanceof Error ? err.message : String(err)}`);
115
133
  }
116
134
  }
117
135
  return {
@@ -34,12 +34,31 @@ import { parseVenue } from '../venues/registry.js';
34
34
  import { unsealCredentials, SealedEnvelopeError } from '../security/sealed-credentials.js';
35
35
  import { logger } from '../logger.js';
36
36
  const TAG = 'set-exchange-credentials';
37
+ /** Non-secret CONTROL flags honored from the PLAINTEXT SIBLINGS of a sealed
38
+ * envelope (E2E audit 2026-08-11 #5). The envelope's anti-smuggle property —
39
+ * "plaintext siblings are ignored" — exists so a mixed payload can't override
40
+ * sealed CREDENTIAL values. But the dashboard sends `confirm_venue_switch`
41
+ * as a sibling NEXT TO `sealed` (it is not a secret and the sealer encrypts
42
+ * only the per-venue credential payload), so dropping every sibling made the
43
+ * confirm retry look unconfirmed forever: any live venue-switcher on a box
44
+ * with a transport key hit an infinite confirm loop. The flags below are
45
+ * safe to honor from plaintext because they cannot change WHAT gets stored
46
+ * (the sealed payload alone decides that) — they only acknowledge a warn
47
+ * step for the request the operator themselves sealed. A value INSIDE the
48
+ * envelope still wins (sealed-beats-plaintext is preserved). */
49
+ const SEALED_SIBLING_CONTROL_FLAGS = ['confirm_venue_switch'];
37
50
  /** Open a sealed envelope into plain args, or return a renderable error. */
38
51
  export function resolveSealedArgs(args, configDir) {
39
52
  if (args?.sealed == null)
40
53
  return { args, sealed: false };
41
54
  try {
42
55
  const plain = unsealCredentials(args.sealed, configDir);
56
+ for (const flag of SEALED_SIBLING_CONTROL_FLAGS) {
57
+ const sibling = args[flag];
58
+ if (plain[flag] === undefined && sibling !== undefined) {
59
+ plain[flag] = sibling;
60
+ }
61
+ }
43
62
  return { args: plain, sealed: true };
44
63
  }
45
64
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reefclaw/connect",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "One-command installer that connects your OpenClaw agent to ReefClaw (paper trading, no exchange keys).",
5
5
  "type": "module",
6
6
  "bin": {