traderclaw-cli 1.0.61 → 1.0.63

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.
@@ -941,6 +941,11 @@ function configureGatewayScheduling(modeConfig, configPath = CONFIG_FILE) {
941
941
  config.channels.defaults.heartbeat.showOk = true;
942
942
  }
943
943
 
944
+ if (!config.agents.defaults || typeof config.agents.defaults !== "object") {
945
+ config.agents.defaults = {};
946
+ }
947
+ config.agents.defaults.heartbeat = { ...defaultHeartbeat };
948
+
944
949
  ensureAgentsDefaultsSchemaCompat(config);
945
950
  mkdirSync(CONFIG_DIR, { recursive: true });
946
951
  writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
@@ -1498,6 +1503,7 @@ export class InstallerStepEngine {
1498
1503
  xAccessTokenCtoSecret: typeof options.xAccessTokenCtoSecret === "string" ? options.xAccessTokenCtoSecret : "",
1499
1504
  xAccessTokenIntern: typeof options.xAccessTokenIntern === "string" ? options.xAccessTokenIntern : "",
1500
1505
  xAccessTokenInternSecret: typeof options.xAccessTokenInternSecret === "string" ? options.xAccessTokenInternSecret : "",
1506
+ referralCode: typeof options.referralCode === "string" ? options.referralCode.trim() : "",
1501
1507
  };
1502
1508
  this.hooks = {
1503
1509
  onStepEvent: typeof hooks.onStepEvent === "function" ? hooks.onStepEvent : () => {},
@@ -1783,6 +1789,11 @@ export class InstallerStepEngine {
1783
1789
  args.push("--gateway-base-url", gatewayBaseUrl);
1784
1790
  }
1785
1791
 
1792
+ const ref = String(this.options.referralCode || "").trim();
1793
+ if (ref) {
1794
+ args.push("--referral-code", ref);
1795
+ }
1796
+
1786
1797
  const command = [this.modeConfig.cliName, ...args].join(" ");
1787
1798
  const docs =
1788
1799
  "https://docs.traderclaw.ai/docs/installation#troubleshooting-session-expired-auth-errors-or-the-agent-logged-out";
@@ -520,15 +520,22 @@ function signChallengeLocally(challengeText, privateKeyBase58) {
520
520
  return b58Encode(new Uint8Array(sig));
521
521
  }
522
522
 
523
- async function doSignup(orchestratorUrl, externalUserId) {
523
+ async function doSignup(orchestratorUrl, externalUserId, referralCode = "") {
524
524
  printInfo(` Signing up as: ${externalUserId}`);
525
+ const body = { externalUserId };
526
+ const ref = String(referralCode || "").trim();
527
+ if (ref) body.referralCode = ref;
525
528
  const res = await httpRequest(`${orchestratorUrl}/api/auth/signup`, {
526
529
  method: "POST",
527
- body: { externalUserId },
530
+ body,
528
531
  });
529
532
 
530
533
  if (!res.ok) {
531
- throw new Error(`Signup failed (HTTP ${res.status}): ${JSON.stringify(res.data)}`);
534
+ const payload = res.data && typeof res.data === "object" ? res.data : {};
535
+ const apiMsg = typeof payload.message === "string" ? payload.message : JSON.stringify(res.data);
536
+ const err = new Error(`Signup failed (HTTP ${res.status}): ${apiMsg}`);
537
+ if (typeof payload.code === "string") err.code = payload.code;
538
+ throw err;
532
539
  }
533
540
 
534
541
  return res.data;
@@ -700,6 +707,8 @@ async function cmdSetup(args) {
700
707
  let noEnsureGatewayPersistent = false;
701
708
  let signupRecoverySecret = undefined;
702
709
  let forwardTelegramRecipientArg = "";
710
+ let referralCodeArg = "";
711
+ let referralInvalidRetries = 0;
703
712
 
704
713
  for (let i = 0; i < args.length; i++) {
705
714
  if ((args[i] === "--api-key" || args[i] === "-k") && args[i + 1]) {
@@ -746,6 +755,9 @@ async function cmdSetup(args) {
746
755
  ) {
747
756
  forwardTelegramRecipientArg = args[++i];
748
757
  }
758
+ if ((args[i] === "--referral-code" || args[i] === "-r") && args[i + 1]) {
759
+ referralCodeArg = args[++i];
760
+ }
749
761
  }
750
762
  const runtimeWalletPrivateKey = getRuntimeWalletPrivateKey(walletPrivateKey);
751
763
 
@@ -763,6 +775,16 @@ async function cmdSetup(args) {
763
775
  }
764
776
  }
765
777
 
778
+ if (doSignupFlow && !referralCodeArg) {
779
+ printInfo(
780
+ "\n Optional: enter a referral code for bonus access time (24h extra when valid). Press Enter to skip.\n",
781
+ );
782
+ printInfo(
783
+ " Benefits: extra trial time now; referring others later earns +8h per user who completes at least one trade with the agent.\n",
784
+ );
785
+ referralCodeArg = await prompt("Referral code (optional)", "");
786
+ }
787
+
766
788
  if (doSignupFlow) {
767
789
  print("\n Signing up for a new account...\n");
768
790
  if (!externalUserId) {
@@ -771,7 +793,7 @@ async function cmdSetup(args) {
771
793
 
772
794
  for (let signupAttempt = 0; ; signupAttempt++) {
773
795
  try {
774
- const signupResult = await doSignup(orchestratorUrl, externalUserId);
796
+ const signupResult = await doSignup(orchestratorUrl, externalUserId, referralCodeArg);
775
797
  apiKey = signupResult.apiKey;
776
798
  if (signupResult.recoverySecret) {
777
799
  signupRecoverySecret = signupResult.recoverySecret;
@@ -783,6 +805,22 @@ async function cmdSetup(args) {
783
805
  break;
784
806
  } catch (err) {
785
807
  const msg = err.message || String(err);
808
+ const apiCode = err.code;
809
+ const referralRejected =
810
+ apiCode === "REFERRAL_CODE_INVALID" ||
811
+ msg.includes("REFERRAL_CODE_INVALID") ||
812
+ msg.includes("Unknown referral code");
813
+ if (referralRejected) {
814
+ referralInvalidRetries += 1;
815
+ if (referralInvalidRetries > 30) {
816
+ printError("Too many invalid referral attempts. Aborting.");
817
+ process.exit(1);
818
+ }
819
+ printWarn(" That referral code was not accepted (unknown or invalid).");
820
+ printInfo(" Leave it blank or enter a different code, then try again.");
821
+ referralCodeArg = await prompt("Referral code (optional, leave empty to skip)", "");
822
+ continue;
823
+ }
786
824
  if (msg.includes("SIGNUP_ALREADY_COMPLETED") || msg.includes("409")) {
787
825
  printWarn(` User "${externalUserId}" is already registered.`);
788
826
  if (signupAttempt >= 2) {
@@ -2176,6 +2214,7 @@ function wizardHtml(defaults) {
2176
2214
  .spinner { width:14px; height:14px; border:2px solid #334a87; border-top-color:#8daeff; border-radius:50%; animation:spin 0.8s linear infinite; flex:0 0 auto; }
2177
2215
  .muted a { color:#9fd3ff; }
2178
2216
  .muted a:hover { color:#c5e5ff; }
2217
+ .info-dot { display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:50%; background:#22315a; color:#9cb0de; font-size:11px; font-weight:700; cursor:help; flex-shrink:0; }
2179
2218
  @keyframes spin { to { transform:rotate(360deg); } }
2180
2219
  </style>
2181
2220
  </head>
@@ -2253,6 +2292,13 @@ function wizardHtml(defaults) {
2253
2292
  <p class="muted">Already have a TraderClaw account? Paste your API key here. New users: leave empty.</p>
2254
2293
  </div>
2255
2294
  </div>
2295
+ <div style="margin-top:12px;">
2296
+ <label style="display:flex;align-items:center;gap:8px;">Referral code (optional)
2297
+ <span class="info-dot" title="Included access: 24 hours for every new account. Add a valid referral code for an extra 24 hours. Refer others: when they complete at least one trade with the agent, you earn +8 hours per active referral. When your access window ends, you will need to stake or keep referring to continue.">i</span>
2298
+ </label>
2299
+ <input id="referralCode" type="text" maxlength="16" autocomplete="off" placeholder="e.g. ABCD1234" />
2300
+ <p class="muted">If you have a friend’s code, enter it here. The setup command below will include it for <code>traderclaw setup</code>. If the server rejects the code, clear this field or fix it and copy the updated command — or run <code>traderclaw setup</code> again and enter a valid code or leave referral blank when prompted.</p>
2301
+ </div>
2256
2302
  <button id="start" disabled>Start Installation</button>
2257
2303
  </div>
2258
2304
  <div class="card" id="statusCard">
@@ -2361,6 +2407,7 @@ function wizardHtml(defaults) {
2361
2407
  let llmLoadStartedAt = 0;
2362
2408
  let announcedTailscaleUrl = "";
2363
2409
  let announcedFunnelAdminUrl = "";
2410
+ let lastReferralFailFingerprint = "";
2364
2411
  let pollTimer = null;
2365
2412
  let pollIntervalMs = 1200;
2366
2413
  let installLocked = false;
@@ -2555,6 +2602,7 @@ function wizardHtml(defaults) {
2555
2602
  llmCredential: llmCredentialEl.value.trim(),
2556
2603
  apiKey: document.getElementById("apiKey").value.trim(),
2557
2604
  telegramToken: document.getElementById("telegramToken").value.trim(),
2605
+ referralCode: document.getElementById("referralCode").value.trim(),
2558
2606
  xConsumerKey: xConsumerKeyEl.value.trim(),
2559
2607
  xConsumerSecret: xConsumerSecretEl.value.trim(),
2560
2608
  xAccessTokenMain: xAccessTokenMainEl.value.trim(),
@@ -2716,9 +2764,30 @@ function wizardHtml(defaults) {
2716
2764
  }
2717
2765
 
2718
2766
  const errors = data.errors || [];
2719
- manualEl.textContent = errors.length > 0
2767
+ let manualText = errors.length > 0
2720
2768
  ? errors.map((e) => "Step " + (e.stepId || "unknown") + ":\\n" + (e.error || "")).join("\\n\\n")
2721
2769
  : "";
2770
+ const combinedErr = errors.map((e) => e.error || "").join("\\n");
2771
+ if (
2772
+ data.status === "failed"
2773
+ && /REFERRAL_CODE_INVALID|Unknown referral code/i.test(combinedErr)
2774
+ ) {
2775
+ manualText +=
2776
+ (manualText ? "\\n\\n" : "") +
2777
+ "Referral code was rejected. Clear or fix the referral field above, then click Start Installation again.";
2778
+ const fp = combinedErr.slice(0, 240);
2779
+ if (fp && fp !== lastReferralFailFingerprint) {
2780
+ lastReferralFailFingerprint = fp;
2781
+ try {
2782
+ window.alert(
2783
+ "Referral code was not accepted. Update or clear the referral field, then click Start Installation again.",
2784
+ );
2785
+ } catch {
2786
+ /* ignore */
2787
+ }
2788
+ }
2789
+ }
2790
+ manualEl.textContent = manualText;
2722
2791
  stepsEl.innerHTML = "";
2723
2792
  steps.forEach((row) => {
2724
2793
  const tr = document.createElement("tr");
@@ -2938,6 +3007,7 @@ async function cmdInstall(args) {
2938
3007
  enableTelegram: true,
2939
3008
  telegramToken: body.telegramToken || defaults.telegramToken,
2940
3009
  autoInstallDeps: true,
3010
+ referralCode: typeof body.referralCode === "string" ? body.referralCode.trim() : "",
2941
3011
  xConsumerKey: body.xConsumerKey ?? defaults.xConsumerKey,
2942
3012
  xConsumerSecret: body.xConsumerSecret ?? defaults.xConsumerSecret,
2943
3013
  xAccessTokenMain: body.xAccessTokenMain ?? defaults.xAccessTokenMain,
@@ -2973,6 +3043,7 @@ async function cmdInstall(args) {
2973
3043
  enableTelegram: true,
2974
3044
  telegramToken: body.telegramToken || defaults.telegramToken,
2975
3045
  autoInstallDeps: true,
3046
+ referralCode: wizardOpts.referralCode,
2976
3047
  xConsumerKey: wizardOpts.xConsumerKey,
2977
3048
  xConsumerSecret: wizardOpts.xConsumerSecret,
2978
3049
  xAccessTokenMain: wizardOpts.xAccessTokenMain,
@@ -3227,7 +3298,14 @@ async function cmdTestSession(args) {
3227
3298
  try {
3228
3299
  if (currentRefreshToken && currentAccessToken) {
3229
3300
  mkdirSync(dataDir, { recursive: true });
3301
+ let existingSidecar = {};
3302
+ try {
3303
+ if (existsSync(sessionTokensPath)) {
3304
+ existingSidecar = JSON.parse(readFileSync(sessionTokensPath, "utf-8")) || {};
3305
+ }
3306
+ } catch { /* ignore */ }
3230
3307
  const payload = {
3308
+ ...existingSidecar,
3231
3309
  refreshToken: currentRefreshToken,
3232
3310
  accessToken: currentAccessToken,
3233
3311
  accessTokenExpiresAt: Date.now() + 900_000,
@@ -3304,6 +3382,7 @@ Setup options:
3304
3382
  --gateway-base-url, -g Gateway public HTTPS URL for orchestrator callbacks
3305
3383
  --gateway-token, -t Gateway bearer token (defaults to API key)
3306
3384
  --telegram-recipient Telegram @username or chat id (aliases: --forward-telegram-chat-id, --telegram-chat-id)
3385
+ --referral-code, -r Optional referral code for new signups (extra trial time when valid)
3307
3386
  --skip-gateway-registration Skip gateway URL registration with orchestrator
3308
3387
  --show-api-key Extra hint after signup (full key is always shown once; confirm with API_KEY_STORED)
3309
3388
  --show-wallet-private-key Reveal full wallet private key in setup output
@@ -3333,7 +3412,7 @@ Examples:
3333
3412
  traderclaw install --wizard
3334
3413
  traderclaw install --wizard --lane quick-local
3335
3414
  traderclaw gateway ensure-persistent
3336
- traderclaw setup --signup --user-id my_agent_001
3415
+ traderclaw setup --signup --user-id my_agent_001 --referral-code ABCD1234
3337
3416
  traderclaw setup --api-key oc_xxx --url https://api.traderclaw.ai
3338
3417
  traderclaw setup --gateway-base-url https://gateway.myhost.ts.net
3339
3418
  traderclaw setup --telegram-recipient @myusername
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traderclaw-cli",
3
- "version": "1.0.61",
3
+ "version": "1.0.63",
4
4
  "description": "Global TraderClaw CLI (install --wizard, setup, precheck). Installs solana-traderclaw as a dependency for OpenClaw plugin files.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,7 +17,7 @@
17
17
  "node": ">=22"
18
18
  },
19
19
  "dependencies": {
20
- "solana-traderclaw": "^1.0.61"
20
+ "solana-traderclaw": "^1.0.63"
21
21
  },
22
22
  "keywords": [
23
23
  "traderclaw",