solana-traderclaw 1.0.42 → 1.0.43

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/README.md CHANGED
@@ -44,6 +44,11 @@ Or install directly into OpenClaw:
44
44
  openclaw plugins install solana-traderclaw
45
45
  ```
46
46
 
47
+ Install name and config id are intentionally different:
48
+ - npm package / install command: `solana-traderclaw`
49
+ - OpenClaw plugin id in `openclaw.json`: `solana-trader`
50
+ - OpenClaw allowlist entry: `plugins.allow: ["solana-trader"]`
51
+
47
52
  ### 2. Run setup
48
53
 
49
54
  ```bash
@@ -143,7 +148,7 @@ traderclaw config set <key> <v> # Update a value
143
148
  traderclaw config reset # Remove all plugin config
144
149
  ```
145
150
 
146
- Available config keys: `orchestratorUrl`, `walletId`, `apiKey`, `apiTimeout`, `refreshToken`, `walletPublicKey`, `walletPrivateKey`, `gatewayBaseUrl`, `gatewayToken`, `agentId`
151
+ Available config keys: `orchestratorUrl`, `walletId`, `apiKey`, `apiTimeout`, `refreshToken`, `recoverySecret`, `walletPublicKey`, `walletPrivateKey`, `gatewayBaseUrl`, `gatewayToken`, `agentId`
147
152
 
148
153
  ### `traderclaw --help`
149
154
 
@@ -160,6 +165,7 @@ If you prefer to configure manually instead of using the CLI, add to `~/.opencla
160
165
  ```json5
161
166
  {
162
167
  plugins: {
168
+ allow: ["solana-trader"],
163
169
  entries: {
164
170
  "solana-trader": {
165
171
  enabled: true,
@@ -55,8 +55,105 @@ function getNpmGlobalInstallCwd() {
55
55
  }
56
56
  }
57
57
 
58
- /** Older `plugins.entries` keys / npm-era ids to merge orchestrator URL for. */
59
- const LEGACY_TRADER_PLUGIN_IDS = ["traderclaw-v1", "solana-traderclaw-v1", "solana-trader"];
58
+ /** Older `plugins.entries` keys / npm-era ids for the v1 plugin. */
59
+ const LEGACY_TRADER_PLUGIN_IDS = ["traderclaw-v1", "solana-traderclaw-v1", "solana-traderclaw"];
60
+
61
+ function isRecord(value) {
62
+ return !!value && typeof value === "object" && !Array.isArray(value);
63
+ }
64
+
65
+ function getLegacyTraderPluginIds(pluginId) {
66
+ return pluginId === "solana-trader" ? LEGACY_TRADER_PLUGIN_IDS : [];
67
+ }
68
+
69
+ function normalizeTraderPluginEntries(config, pluginId) {
70
+ if (!isRecord(config)) return false;
71
+ if (!isRecord(config.plugins)) config.plugins = {};
72
+ if (!isRecord(config.plugins.entries)) config.plugins.entries = {};
73
+
74
+ const entries = config.plugins.entries;
75
+ const legacyIds = getLegacyTraderPluginIds(pluginId);
76
+ if (legacyIds.length === 0) return false;
77
+
78
+ let touched = false;
79
+ let hasSource = false;
80
+ let enabledSeen = false;
81
+ let enabledValue = false;
82
+ let mergedConfig = {};
83
+
84
+ for (const sourceId of [...legacyIds, pluginId]) {
85
+ const entry = entries[sourceId];
86
+ if (!isRecord(entry)) continue;
87
+ hasSource = true;
88
+ if (typeof entry.enabled === "boolean") {
89
+ enabledSeen = true;
90
+ enabledValue = enabledValue || entry.enabled;
91
+ }
92
+ if (isRecord(entry.config)) {
93
+ mergedConfig = { ...mergedConfig, ...entry.config };
94
+ }
95
+ }
96
+
97
+ if (!hasSource) return false;
98
+
99
+ const canonicalEntry = isRecord(entries[pluginId]) ? entries[pluginId] : {};
100
+ const nextEntry = {
101
+ ...canonicalEntry,
102
+ enabled: typeof canonicalEntry.enabled === "boolean" ? canonicalEntry.enabled : (enabledSeen ? enabledValue : true),
103
+ config: mergedConfig,
104
+ };
105
+
106
+ if (entries[pluginId] !== nextEntry) {
107
+ entries[pluginId] = nextEntry;
108
+ touched = true;
109
+ }
110
+
111
+ for (const legacyId of legacyIds) {
112
+ if (Object.prototype.hasOwnProperty.call(entries, legacyId)) {
113
+ delete entries[legacyId];
114
+ touched = true;
115
+ }
116
+ }
117
+
118
+ return touched;
119
+ }
120
+
121
+ function normalizeTraderAllowlist(config, pluginId) {
122
+ if (!isRecord(config?.plugins)) return false;
123
+ const legacyIds = new Set(getLegacyTraderPluginIds(pluginId));
124
+ if (legacyIds.size === 0 || !Array.isArray(config.plugins.allow)) return false;
125
+
126
+ const nextAllow = [];
127
+ const seen = new Set();
128
+ let touched = false;
129
+
130
+ for (const id of config.plugins.allow) {
131
+ if (typeof id !== "string") {
132
+ touched = true;
133
+ continue;
134
+ }
135
+ const trimmed = id.trim();
136
+ if (!trimmed) {
137
+ touched = true;
138
+ continue;
139
+ }
140
+ if (legacyIds.has(trimmed)) {
141
+ touched = true;
142
+ continue;
143
+ }
144
+ if (seen.has(trimmed)) {
145
+ touched = true;
146
+ continue;
147
+ }
148
+ seen.add(trimmed);
149
+ nextAllow.push(trimmed);
150
+ }
151
+
152
+ if (touched) {
153
+ config.plugins.allow = nextAllow;
154
+ }
155
+ return touched;
156
+ }
60
157
 
61
158
  function stripAnsi(text) {
62
159
  if (typeof text !== "string") return text;
@@ -433,6 +530,9 @@ function seedPluginConfig(modeConfig, orchestratorUrl, configPath = CONFIG_FILE)
433
530
  if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
434
531
  if (!config.plugins.entries || typeof config.plugins.entries !== "object") config.plugins.entries = {};
435
532
 
533
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
534
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
535
+
436
536
  const entries = config.plugins.entries;
437
537
 
438
538
  const mergeOrchestratorForId = (pluginId) => {
@@ -453,9 +553,6 @@ function seedPluginConfig(modeConfig, orchestratorUrl, configPath = CONFIG_FILE)
453
553
  };
454
554
 
455
555
  mergeOrchestratorForId(modeConfig.pluginId);
456
- for (const legacyId of LEGACY_TRADER_PLUGIN_IDS) {
457
- if (entries[legacyId]) mergeOrchestratorForId(legacyId);
458
- }
459
556
 
460
557
  // Do not set plugins.allow here: OpenClaw validates allow[] against the plugin registry, and
461
558
  // the id is not registered until after `openclaw plugins install`. Pre-seeding allow caused:
@@ -596,6 +693,8 @@ function mergePluginsAllowlist(modeConfig, configPath = CONFIG_FILE) {
596
693
  return;
597
694
  }
598
695
  if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
696
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
697
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
599
698
  const allowSet = new Set(
600
699
  Array.isArray(config.plugins.allow) ? config.plugins.allow.filter((id) => typeof id === "string" && id.trim()) : [],
601
700
  );
@@ -972,6 +1071,8 @@ function seedXConfig(modeConfig, configPath = CONFIG_FILE, wizardOpts = {}) {
972
1071
 
973
1072
  if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
974
1073
  if (!config.plugins.entries || typeof config.plugins.entries !== "object") config.plugins.entries = {};
1074
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
1075
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
975
1076
 
976
1077
  const entry = config.plugins.entries[modeConfig.pluginId];
977
1078
  if (!entry || typeof entry !== "object") return { skipped: true, reason: "plugin entry not found" };
@@ -1047,6 +1148,8 @@ function persistXProfileIdentities(configPath, modeConfig, identities) {
1047
1148
  } catch {
1048
1149
  return { written: 0 };
1049
1150
  }
1151
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
1152
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
1050
1153
  const entry = config?.plugins?.entries?.[modeConfig.pluginId];
1051
1154
  if (!entry?.config?.x?.profiles || typeof entry.config.x.profiles !== "object") return { written: 0 };
1052
1155
 
@@ -13,6 +13,7 @@ const PACKAGE_JSON = JSON.parse(readFileSync(new URL("../package.json", import.m
13
13
  const VERSION = PACKAGE_JSON.version;
14
14
  const NPM_PACKAGE_NAME = typeof PACKAGE_JSON.name === "string" ? PACKAGE_JSON.name : "solana-traderclaw";
15
15
  const PLUGIN_ID = "solana-trader";
16
+ const LEGACY_PLUGIN_IDS = ["traderclaw-v1", "solana-traderclaw-v1", "solana-traderclaw"];
16
17
  const CONFIG_DIR = join(homedir(), ".openclaw");
17
18
  const CONFIG_FILE = join(CONFIG_DIR, "openclaw.json");
18
19
  const WALLET_PRIVATE_KEY_ENV = "TRADERCLAW_WALLET_PRIVATE_KEY";
@@ -166,11 +167,69 @@ function readConfig() {
166
167
  }
167
168
 
168
169
  function writeConfig(config) {
170
+ normalizePluginConfigShape(config);
169
171
  mkdirSync(CONFIG_DIR, { recursive: true });
170
172
  writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
171
173
  }
172
174
 
175
+ function isRecord(value) {
176
+ return !!value && typeof value === "object" && !Array.isArray(value);
177
+ }
178
+
179
+ function normalizePluginConfigShape(config) {
180
+ if (!isRecord(config)) return config;
181
+ if (!isRecord(config.plugins)) config.plugins = {};
182
+ const plugins = config.plugins;
183
+ if (!isRecord(plugins.entries)) plugins.entries = {};
184
+ const entries = plugins.entries;
185
+
186
+ let enabledSeen = false;
187
+ let enabledValue = false;
188
+ let mergedConfig = {};
189
+ let found = false;
190
+
191
+ for (const sourceId of [...LEGACY_PLUGIN_IDS, PLUGIN_ID]) {
192
+ const entry = entries[sourceId];
193
+ if (!isRecord(entry)) continue;
194
+ found = true;
195
+ if (typeof entry.enabled === "boolean") {
196
+ enabledSeen = true;
197
+ enabledValue = enabledValue || entry.enabled;
198
+ }
199
+ if (isRecord(entry.config)) {
200
+ mergedConfig = { ...mergedConfig, ...entry.config };
201
+ }
202
+ }
203
+
204
+ if (found) {
205
+ const canonical = isRecord(entries[PLUGIN_ID]) ? entries[PLUGIN_ID] : {};
206
+ entries[PLUGIN_ID] = {
207
+ ...canonical,
208
+ enabled: typeof canonical.enabled === "boolean" ? canonical.enabled : (enabledSeen ? enabledValue : true),
209
+ config: mergedConfig,
210
+ };
211
+ }
212
+
213
+ for (const legacyId of LEGACY_PLUGIN_IDS) {
214
+ delete entries[legacyId];
215
+ }
216
+
217
+ if (Array.isArray(plugins.allow)) {
218
+ const seen = new Set();
219
+ plugins.allow = plugins.allow.filter((id) => {
220
+ if (typeof id !== "string") return false;
221
+ const trimmed = id.trim();
222
+ if (!trimmed || LEGACY_PLUGIN_IDS.includes(trimmed) || seen.has(trimmed)) return false;
223
+ seen.add(trimmed);
224
+ return true;
225
+ });
226
+ }
227
+
228
+ return config;
229
+ }
230
+
173
231
  function getPluginConfig(config) {
232
+ normalizePluginConfigShape(config);
174
233
  const plugins = config.plugins;
175
234
  if (!plugins) return null;
176
235
  const entries = plugins.entries;
@@ -181,6 +240,7 @@ function getPluginConfig(config) {
181
240
  }
182
241
 
183
242
  function setPluginConfig(config, pluginConfig) {
243
+ normalizePluginConfigShape(config);
184
244
  if (!config.plugins) config.plugins = {};
185
245
  if (!config.plugins.entries) config.plugins.entries = {};
186
246
  config.plugins.entries[PLUGIN_ID] = {
@@ -28,6 +28,10 @@
28
28
  "type": "string",
29
29
  "description": "Refresh token from last session (auto-populated after authentication)"
30
30
  },
31
+ "recoverySecret": {
32
+ "type": "string",
33
+ "description": "Consumable recovery secret used to restore a session after refresh-token failure"
34
+ },
31
35
  "walletPublicKey": {
32
36
  "type": "string",
33
37
  "description": "Solana wallet public key (auto-populated after session establishment)"
@@ -128,6 +132,11 @@
128
132
  "sensitive": true,
129
133
  "advanced": true
130
134
  },
135
+ "recoverySecret": {
136
+ "label": "Recovery Secret",
137
+ "sensitive": true,
138
+ "advanced": true
139
+ },
131
140
  "walletPublicKey": {
132
141
  "label": "Wallet Public Key",
133
142
  "advanced": true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-traderclaw",
3
- "version": "1.0.42",
3
+ "version": "1.0.43",
4
4
  "description": "TraderClaw V1-Upgraded — Solana trading for OpenClaw with intelligence lab, tool envelopes, prompt scrubbing, read-only X social intel, and split skill docs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,7 +35,7 @@ https://docs.traderclaw.ai/docs/installation#troubleshooting-session-expired-aut
35
35
  - **Wallet proof** is NOT account signup. It is a cryptographic step proving wallet control.
36
36
  - **`traderclaw login`** reuses the saved refresh token when valid.
37
37
  - **OpenClaw gateway ≠ your SSH shell.** Exporting env vars in SSH does NOT inject them into the gateway service process.
38
- - **Plugin id vs npm name:** `solana-traderclaw-v1` npm package with `solana-trader` plugin id is expected.
38
+ - **Plugin id vs npm name:** `solana-traderclaw` is the npm package name, while `solana-trader` is the OpenClaw plugin id used in `plugins.entries` and `plugins.allow`.
39
39
 
40
40
  ---
41
41