@dimcool/dimclaw 0.1.30 → 0.1.32

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 (2) hide show
  1. package/dist/index.js +295 -149
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -32218,7 +32218,7 @@ var require_money = __commonJS2({
32218
32218
  "../utils/dist/money.js"(exports) {
32219
32219
  "use strict";
32220
32220
  Object.defineProperty(exports, "__esModule", { value: true });
32221
- exports.SOL_LAMPORTS = exports.MICRO_UNITS = void 0;
32221
+ exports.SOL_MINT = exports.SOL_LAMPORTS = exports.MICRO_UNITS = void 0;
32222
32222
  exports.isSolMint = isSolMint;
32223
32223
  exports.formatMoneyMinor = formatMoneyMinor2;
32224
32224
  exports.toMajor = toMajor2;
@@ -32231,12 +32231,12 @@ var require_money = __commonJS2({
32231
32231
  exports.formatMoneyMinorCompact = formatMoneyMinorCompact;
32232
32232
  exports.MICRO_UNITS = 1e6;
32233
32233
  exports.SOL_LAMPORTS = 1e9;
32234
- var SOL_MINT_LABEL = "SOL";
32234
+ exports.SOL_MINT = "SOL";
32235
32235
  var SOL_SYSTEM_PROGRAM = "11111111111111111111111111111111";
32236
32236
  function isSolMint(mint) {
32237
32237
  if (!mint)
32238
32238
  return false;
32239
- return mint === SOL_MINT_LABEL || mint === SOL_SYSTEM_PROGRAM;
32239
+ return mint === exports.SOL_MINT || mint === SOL_SYSTEM_PROGRAM;
32240
32240
  }
32241
32241
  function formatMoneyMinor2(amountMinor, fractionDigits = 2) {
32242
32242
  return (amountMinor / exports.MICRO_UNITS).toFixed(fractionDigits);
@@ -32340,7 +32340,7 @@ var require_dist = __commonJS2({
32340
32340
  "../utils/dist/index.js"(exports) {
32341
32341
  "use strict";
32342
32342
  Object.defineProperty(exports, "__esModule", { value: true });
32343
- exports.formatMoneyMinorCompact = exports.formatCurrencyPartsForToken = exports.formatCurrencyParts = exports.kformatMoney = exports.kformat = exports.formatSolMinor = exports.toSolMajor = exports.toMajor = exports.formatMoneyMinor = exports.isSolMint = exports.SOL_LAMPORTS = exports.MICRO_UNITS = void 0;
32343
+ exports.formatMoneyMinorCompact = exports.formatCurrencyPartsForToken = exports.formatCurrencyParts = exports.kformatMoney = exports.kformat = exports.formatSolMinor = exports.toSolMajor = exports.toMajor = exports.formatMoneyMinor = exports.isSolMint = exports.SOL_MINT = exports.SOL_LAMPORTS = exports.MICRO_UNITS = void 0;
32344
32344
  var money_1 = require_money();
32345
32345
  Object.defineProperty(exports, "MICRO_UNITS", { enumerable: true, get: function() {
32346
32346
  return money_1.MICRO_UNITS;
@@ -32348,6 +32348,9 @@ var require_dist = __commonJS2({
32348
32348
  Object.defineProperty(exports, "SOL_LAMPORTS", { enumerable: true, get: function() {
32349
32349
  return money_1.SOL_LAMPORTS;
32350
32350
  } });
32351
+ Object.defineProperty(exports, "SOL_MINT", { enumerable: true, get: function() {
32352
+ return money_1.SOL_MINT;
32353
+ } });
32351
32354
  Object.defineProperty(exports, "isSolMint", { enumerable: true, get: function() {
32352
32355
  return money_1.isSolMint;
32353
32356
  } });
@@ -32884,6 +32887,9 @@ var Admin = class {
32884
32887
  this.http = http2;
32885
32888
  this.logger = logger2;
32886
32889
  }
32890
+ async getInternalBots() {
32891
+ return this.http.get("/admin/internal-bots");
32892
+ }
32887
32893
  async getUserById(id) {
32888
32894
  return this.http.get(`/admin/users/${id}`);
32889
32895
  }
@@ -33235,6 +33241,13 @@ var Lobbies = class {
33235
33241
  setLobbyStore(store) {
33236
33242
  this.lobbyStore = store;
33237
33243
  }
33244
+ /**
33245
+ * Create a new game lobby.
33246
+ *
33247
+ * **Important:** Creating a lobby automatically closes and refunds any
33248
+ * existing open or queued lobby for this user. You do not need to manually
33249
+ * leave or cancel a previous lobby before calling this.
33250
+ */
33238
33251
  async createLobby(gameType, betAmount) {
33239
33252
  return this.http.post("/lobbies", { gameType, betAmount });
33240
33253
  }
@@ -33298,21 +33311,6 @@ var Lobbies = class {
33298
33311
  async deleteLobby(lobbyId) {
33299
33312
  return this.http.delete(`/lobbies/admin/${lobbyId}`);
33300
33313
  }
33301
- /**
33302
- * Play again: Create a new lobby and prepare deposit in one flow.
33303
- * Returns the lobby and unsigned transaction that needs to be signed.
33304
- * @param gameType - The game type to play again
33305
- * @param betAmount - The bet amount (same as previous game)
33306
- * @param escrow - The escrow service instance (from sdk.escrow)
33307
- */
33308
- async playAgain(gameType, betAmount, escrow) {
33309
- const lobby = await this.createLobby(gameType, betAmount);
33310
- const { transaction } = await escrow.prepareAndStartDeposit(lobby.id);
33311
- return {
33312
- lobby,
33313
- unsignedTransaction: transaction
33314
- };
33315
- }
33316
33314
  };
33317
33315
  var Games = class {
33318
33316
  constructor(http2, wallet, logger2) {
@@ -33832,6 +33830,7 @@ var Achievements = class {
33832
33830
  return this.http.get(`/achievements/users/${userId}`);
33833
33831
  }
33834
33832
  };
33833
+ var import_utils14 = __toESM2(require_dist(), 1);
33835
33834
  var MIN_TRANSFER_AMOUNT = 5e4;
33836
33835
  var MIN_SOL_TRANSFER_AMOUNT = 1e6;
33837
33836
  var Wallet = class {
@@ -35507,8 +35506,21 @@ function createGameActionsStore(transport) {
35507
35506
  });
35508
35507
  };
35509
35508
  const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
35509
+ const pendingEvents = /* @__PURE__ */ new Map();
35510
+ function enqueue(gameId, event) {
35511
+ const q = pendingEvents.get(gameId) ?? [];
35512
+ q.push(event);
35513
+ pendingEvents.set(gameId, q);
35514
+ }
35515
+ function drainQueue(gameId) {
35516
+ const q = pendingEvents.get(gameId);
35517
+ if (!q?.length) return;
35518
+ pendingEvents.delete(gameId);
35519
+ for (const ev of q) applyWsEvent(ev);
35520
+ }
35510
35521
  const setBaseState = (gameId, state) => {
35511
35522
  updateState(gameId, state);
35523
+ drainQueue(gameId);
35512
35524
  };
35513
35525
  const clearState = (gameId) => {
35514
35526
  store.updateState((state) => {
@@ -35518,6 +35530,7 @@ function createGameActionsStore(transport) {
35518
35530
  const { [gameId]: _, ...rest } = state.statesByGameId;
35519
35531
  return { ...state, statesByGameId: rest };
35520
35532
  });
35533
+ pendingEvents.delete(gameId);
35521
35534
  };
35522
35535
  const applyWsEvent = (event) => {
35523
35536
  switch (event.event) {
@@ -35536,7 +35549,10 @@ function createGameActionsStore(transport) {
35536
35549
  }
35537
35550
  case "game:rps:starting": {
35538
35551
  const current = store.getState().statesByGameId[event.payload.gameId];
35539
- if (!current || !isRpsState(current)) return;
35552
+ if (!current || !isRpsState(current)) {
35553
+ enqueue(event.payload.gameId, event);
35554
+ return;
35555
+ }
35540
35556
  const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
35541
35557
  const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
35542
35558
  const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
@@ -35557,7 +35573,10 @@ function createGameActionsStore(transport) {
35557
35573
  }
35558
35574
  case "game:rps:round:started": {
35559
35575
  const current = store.getState().statesByGameId[event.payload.gameId];
35560
- if (!current || !isRpsState(current)) return;
35576
+ if (!current || !isRpsState(current)) {
35577
+ enqueue(event.payload.gameId, event);
35578
+ return;
35579
+ }
35561
35580
  const actions = {};
35562
35581
  const baseUsers = /* @__PURE__ */ new Set();
35563
35582
  Object.keys(current.roundState.actions).forEach(
@@ -35591,7 +35610,10 @@ function createGameActionsStore(transport) {
35591
35610
  }
35592
35611
  case "game:rps:action:received": {
35593
35612
  const current = store.getState().statesByGameId[event.payload.gameId];
35594
- if (!current || !isRpsState(current)) return;
35613
+ if (!current || !isRpsState(current)) {
35614
+ enqueue(event.payload.gameId, event);
35615
+ return;
35616
+ }
35595
35617
  const updated = {
35596
35618
  ...current,
35597
35619
  roundState: {
@@ -35610,7 +35632,10 @@ function createGameActionsStore(transport) {
35610
35632
  }
35611
35633
  case "game:rps:timer:cutoff": {
35612
35634
  const current = store.getState().statesByGameId[event.payload.gameId];
35613
- if (!current || !isRpsState(current)) return;
35635
+ if (!current || !isRpsState(current)) {
35636
+ enqueue(event.payload.gameId, event);
35637
+ return;
35638
+ }
35614
35639
  const updated = {
35615
35640
  ...current,
35616
35641
  roundState: {
@@ -35624,7 +35649,10 @@ function createGameActionsStore(transport) {
35624
35649
  }
35625
35650
  case "game:rps:round:reveal": {
35626
35651
  const current = store.getState().statesByGameId[event.payload.gameId];
35627
- if (!current || !isRpsState(current)) return;
35652
+ if (!current || !isRpsState(current)) {
35653
+ enqueue(event.payload.gameId, event);
35654
+ return;
35655
+ }
35628
35656
  const actions = {};
35629
35657
  const payloadActions = event.payload.actions;
35630
35658
  Object.keys(payloadActions || {}).forEach((userId) => {
@@ -35648,7 +35676,10 @@ function createGameActionsStore(transport) {
35648
35676
  }
35649
35677
  case "game:rps:round:completed": {
35650
35678
  const current = store.getState().statesByGameId[event.payload.gameId];
35651
- if (!current || !isRpsState(current)) return;
35679
+ if (!current || !isRpsState(current)) {
35680
+ enqueue(event.payload.gameId, event);
35681
+ return;
35682
+ }
35652
35683
  const roundHistory = [
35653
35684
  ...current.roundHistory || [],
35654
35685
  {
@@ -35673,7 +35704,10 @@ function createGameActionsStore(transport) {
35673
35704
  }
35674
35705
  case "game:rps:timeout": {
35675
35706
  const current = store.getState().statesByGameId[event.payload.gameId];
35676
- if (!current || !isRpsState(current)) return;
35707
+ if (!current || !isRpsState(current)) {
35708
+ enqueue(event.payload.gameId, event);
35709
+ return;
35710
+ }
35677
35711
  const timedOutUser = event.payload.playerId;
35678
35712
  const action = event.payload.action;
35679
35713
  const updated = {
@@ -35698,7 +35732,10 @@ function createGameActionsStore(transport) {
35698
35732
  const payload = event.payload;
35699
35733
  const { gameId } = payload;
35700
35734
  const current = store.getState().statesByGameId[gameId];
35701
- if (!current) return;
35735
+ if (!current) {
35736
+ enqueue(gameId, event);
35737
+ return;
35738
+ }
35702
35739
  const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
35703
35740
  ...current,
35704
35741
  status: "completed",
@@ -35747,11 +35784,15 @@ function createGameActionsStore(transport) {
35747
35784
  const current = store.getState().statesByGameId[gameId];
35748
35785
  const updated = current ? { ...current, ...incoming } : incoming;
35749
35786
  updateState(gameId, updated);
35787
+ drainQueue(gameId);
35750
35788
  break;
35751
35789
  }
35752
35790
  case "game:rematch:requested": {
35753
35791
  const current = store.getState().statesByGameId[event.payload.gameId];
35754
- if (!current) return;
35792
+ if (!current) {
35793
+ enqueue(event.payload.gameId, event);
35794
+ return;
35795
+ }
35755
35796
  const requestedBy = event.payload.requestedBy;
35756
35797
  const userId = event.payload.userId;
35757
35798
  const requested = new Set(
@@ -35767,7 +35808,10 @@ function createGameActionsStore(transport) {
35767
35808
  }
35768
35809
  case "game:rematch:cancelled": {
35769
35810
  const current = store.getState().statesByGameId[event.payload.gameId];
35770
- if (!current) return;
35811
+ if (!current) {
35812
+ enqueue(event.payload.gameId, event);
35813
+ return;
35814
+ }
35771
35815
  const requestedBy = event.payload.requestedBy ?? [];
35772
35816
  const updated = {
35773
35817
  ...current,
@@ -35778,7 +35822,10 @@ function createGameActionsStore(transport) {
35778
35822
  }
35779
35823
  case "game:rematch:started": {
35780
35824
  const current = store.getState().statesByGameId[event.payload.gameId];
35781
- if (!current) return;
35825
+ if (!current) {
35826
+ enqueue(event.payload.gameId, event);
35827
+ return;
35828
+ }
35782
35829
  const updated = {
35783
35830
  ...current,
35784
35831
  rematchRequestedBy: event.payload.playerIds ?? []
@@ -35789,7 +35836,10 @@ function createGameActionsStore(transport) {
35789
35836
  case "game:pot:updated": {
35790
35837
  const { gameId, totalPotMinor } = event.payload;
35791
35838
  const current = store.getState().statesByGameId[gameId];
35792
- if (!current) return;
35839
+ if (!current) {
35840
+ enqueue(gameId, event);
35841
+ return;
35842
+ }
35793
35843
  const updated = {
35794
35844
  ...current,
35795
35845
  totalPotMinor
@@ -35802,12 +35852,123 @@ function createGameActionsStore(transport) {
35802
35852
  }
35803
35853
  };
35804
35854
  const joinGame = (gameId) => transport.joinRoom(`game:${gameId}`);
35855
+ const getCountdownDigit = (gameId, nowMs) => {
35856
+ const state = store.getState().statesByGameId[gameId];
35857
+ if (!state) return null;
35858
+ if (isRpsState(state)) {
35859
+ if (state.roundState.phase !== "starting") return null;
35860
+ const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
35861
+ if (remaining <= 0) return null;
35862
+ return Math.ceil(remaining / 1e3);
35863
+ }
35864
+ const bufferEndsAt = state.bufferEndsAt;
35865
+ if (bufferEndsAt) {
35866
+ const remaining = new Date(bufferEndsAt).getTime() - nowMs;
35867
+ if (remaining <= 0) return null;
35868
+ return Math.ceil(remaining / 1e3);
35869
+ }
35870
+ return null;
35871
+ };
35872
+ const getChessClockTimes = (gameId, nowMs) => {
35873
+ const state = store.getState().statesByGameId[gameId];
35874
+ if (!state || state.gameType !== "chess") return null;
35875
+ const s = state;
35876
+ let whiteMs = s.whiteTimeMs ?? 0;
35877
+ let blackMs = s.blackTimeMs ?? 0;
35878
+ if (s.status === "active" && s.currentPlayerId) {
35879
+ const startedAt = Date.parse(s.turnStartedAt);
35880
+ if (!Number.isNaN(startedAt)) {
35881
+ const elapsed = Math.max(0, nowMs - startedAt);
35882
+ if (s.currentPlayerId === s.whitePlayerId) {
35883
+ whiteMs = Math.max(0, whiteMs - elapsed);
35884
+ } else if (s.currentPlayerId === s.blackPlayerId) {
35885
+ blackMs = Math.max(0, blackMs - elapsed);
35886
+ }
35887
+ }
35888
+ }
35889
+ return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
35890
+ };
35891
+ const getChessCapturedPieces = (gameId) => {
35892
+ const state = store.getState().statesByGameId[gameId];
35893
+ if (!state || state.gameType !== "chess") return null;
35894
+ const fen = state.fen;
35895
+ if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
35896
+ const placement = fen.split(" ")[0];
35897
+ const white = {};
35898
+ const black = {};
35899
+ for (const char of placement) {
35900
+ if (char === "/" || char >= "1" && char <= "8") continue;
35901
+ const lower = char.toLowerCase();
35902
+ if (char === lower) {
35903
+ black[lower] = (black[lower] ?? 0) + 1;
35904
+ } else {
35905
+ white[lower] = (white[lower] ?? 0) + 1;
35906
+ }
35907
+ }
35908
+ const INITIAL = {
35909
+ p: 8,
35910
+ r: 2,
35911
+ n: 2,
35912
+ b: 2,
35913
+ q: 1,
35914
+ k: 1
35915
+ };
35916
+ const capturedByWhite = [];
35917
+ const capturedByBlack = [];
35918
+ for (const [type2, initial] of Object.entries(INITIAL)) {
35919
+ const missingBlack = initial - (black[type2] ?? 0);
35920
+ for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type2}`);
35921
+ const missingWhite = initial - (white[type2] ?? 0);
35922
+ for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type2}`);
35923
+ }
35924
+ return { capturedByWhite, capturedByBlack };
35925
+ };
35926
+ const getTicTacToeClockTimes = (gameId, nowMs) => {
35927
+ const state = store.getState().statesByGameId[gameId];
35928
+ if (!state || state.gameType !== "tic-tac-toe") return null;
35929
+ const s = state;
35930
+ let xMs = s.xTimeMs ?? 0;
35931
+ let oMs = s.oTimeMs ?? 0;
35932
+ if (s.status === "active" && s.currentPlayerId) {
35933
+ const currentMark = s.playerMarks[s.currentPlayerId];
35934
+ const startedAt = Date.parse(s.turnStartedAt);
35935
+ if (!Number.isNaN(startedAt)) {
35936
+ const elapsed = Math.max(0, nowMs - startedAt);
35937
+ if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
35938
+ else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
35939
+ }
35940
+ }
35941
+ return { xTimeMs: xMs, oTimeMs: oMs };
35942
+ };
35943
+ const getConnect4ClockTimes = (gameId, nowMs) => {
35944
+ const state = store.getState().statesByGameId[gameId];
35945
+ if (!state || state.gameType !== "connect-four") return null;
35946
+ const s = state;
35947
+ let redMs = s.redTimeMs ?? 0;
35948
+ let yellowMs = s.yellowTimeMs ?? 0;
35949
+ if (s.status === "active" && s.currentPlayerId) {
35950
+ const currentColor = s.playerColors[s.currentPlayerId];
35951
+ const startedAt = Date.parse(s.turnStartedAt);
35952
+ if (!Number.isNaN(startedAt)) {
35953
+ const elapsed = Math.max(0, nowMs - startedAt);
35954
+ if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
35955
+ else if (currentColor === "YELLOW")
35956
+ yellowMs = Math.max(0, yellowMs - elapsed);
35957
+ }
35958
+ }
35959
+ return { redTimeMs: redMs, yellowTimeMs: yellowMs };
35960
+ };
35805
35961
  return {
35806
35962
  store,
35807
35963
  setBaseState,
35808
35964
  clearState,
35809
35965
  applyWsEvent,
35810
- joinGame
35966
+ joinGame,
35967
+ getCountdownDigit,
35968
+ getChessClockTimes,
35969
+ getChessCapturedPieces,
35970
+ getTicTacToeClockTimes,
35971
+ getConnect4ClockTimes
35811
35972
  };
35812
35973
  }
35813
35974
  function isRpsState(state) {
@@ -36465,12 +36626,14 @@ var WsRouter = class {
36465
36626
  }
36466
36627
  const decoded = decodeWsEvent(eventName, payload);
36467
36628
  if (!decoded) return;
36468
- this.deps.lobbyStore.applyWsEvent(decoded);
36469
- this.deps.gameStore.applyWsEvent(decoded);
36470
- this.deps.gameActionsStore.applyWsEvent(decoded);
36471
- this.deps.chatStore.applyWsEvent(decoded);
36472
- this.deps.dmThreadsStore.applyWsEvent(decoded);
36473
- this.deps.notificationsStore.applyWsEvent(decoded);
36629
+ const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
36630
+ const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
36631
+ this.deps.lobbyStore.applyWsEvent(event);
36632
+ this.deps.gameStore.applyWsEvent(event);
36633
+ this.deps.gameActionsStore.applyWsEvent(event);
36634
+ this.deps.chatStore.applyWsEvent(event);
36635
+ this.deps.dmThreadsStore.applyWsEvent(event);
36636
+ this.deps.notificationsStore.applyWsEvent(event);
36474
36637
  });
36475
36638
  }
36476
36639
  stop() {
@@ -36482,12 +36645,14 @@ var WsRouter = class {
36482
36645
  this.transport.subscribeEvent(eventName, (payload) => {
36483
36646
  const decoded = decodeWsEvent(eventName, payload);
36484
36647
  if (!decoded) return;
36485
- this.deps.lobbyStore.applyWsEvent(decoded);
36486
- this.deps.gameStore.applyWsEvent(decoded);
36487
- this.deps.gameActionsStore.applyWsEvent(decoded);
36488
- this.deps.chatStore.applyWsEvent(decoded);
36489
- this.deps.dmThreadsStore.applyWsEvent(decoded);
36490
- this.deps.notificationsStore.applyWsEvent(decoded);
36648
+ const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
36649
+ const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
36650
+ this.deps.lobbyStore.applyWsEvent(event);
36651
+ this.deps.gameStore.applyWsEvent(event);
36652
+ this.deps.gameActionsStore.applyWsEvent(event);
36653
+ this.deps.chatStore.applyWsEvent(event);
36654
+ this.deps.dmThreadsStore.applyWsEvent(event);
36655
+ this.deps.notificationsStore.applyWsEvent(event);
36491
36656
  });
36492
36657
  }
36493
36658
  }
@@ -36625,10 +36790,11 @@ var SDK = class {
36625
36790
  return { ...result, lobby };
36626
36791
  }
36627
36792
  };
36628
- var import_utils14 = __toESM2(require_dist(), 1);
36629
- var export_MICRO_UNITS = import_utils14.MICRO_UNITS;
36630
- var export_formatMoneyMinor = import_utils14.formatMoneyMinor;
36631
- var export_toMajor = import_utils14.toMajor;
36793
+ var import_utils22 = __toESM2(require_dist(), 1);
36794
+ var export_MICRO_UNITS = import_utils22.MICRO_UNITS;
36795
+ var export_SOL_MINT = import_utils14.SOL_MINT;
36796
+ var export_formatMoneyMinor = import_utils22.formatMoneyMinor;
36797
+ var export_toMajor = import_utils22.toMajor;
36632
36798
 
36633
36799
  // ../dim-agent-core/node_modules/bs58/node_modules/base-x/src/esm/index.js
36634
36800
  function base2(ALPHABET3) {
@@ -36767,6 +36933,28 @@ var esm_default4 = esm_default3(ALPHABET2);
36767
36933
 
36768
36934
  // ../dim-agent-core/src/client.ts
36769
36935
  var import_tweetnacl = __toESM(require_nacl_fast(), 1);
36936
+ function decodeJwtSub(token) {
36937
+ try {
36938
+ const parts2 = token.split(".");
36939
+ if (parts2.length < 2) return null;
36940
+ const base64 = parts2[1].replace(/-/g, "+").replace(/_/g, "/");
36941
+ const json = Buffer.from(base64, "base64").toString("utf-8");
36942
+ const parsed = JSON.parse(json);
36943
+ return typeof parsed.sub === "string" ? parsed.sub : null;
36944
+ } catch {
36945
+ return null;
36946
+ }
36947
+ }
36948
+ function signatureToBytes(sig) {
36949
+ if (sig.includes("-") || sig.includes("_")) {
36950
+ const padded = sig.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(sig.length % 4 === 0 ? 4 : sig.length % 4);
36951
+ return new Uint8Array(Buffer.from(padded, "base64"));
36952
+ }
36953
+ if (/[+/=]/.test(sig)) {
36954
+ return new Uint8Array(Buffer.from(sig, "base64"));
36955
+ }
36956
+ return esm_default4.decode(sig);
36957
+ }
36770
36958
  var DimAgentClient = class {
36771
36959
  sdk;
36772
36960
  agentConfig;
@@ -36793,10 +36981,19 @@ var DimAgentClient = class {
36793
36981
  } else {
36794
36982
  this.keypair = null;
36795
36983
  }
36984
+ const storage = new NodeStorage();
36985
+ if (config.accessToken) {
36986
+ storage.set(TOKEN_KEY, config.accessToken);
36987
+ const sub = decodeJwtSub(config.accessToken);
36988
+ if (sub) {
36989
+ this.authenticated = true;
36990
+ this.userId = sub;
36991
+ }
36992
+ }
36796
36993
  this.sdk = new SDK({
36797
36994
  appId: "dim-agents",
36798
36995
  baseUrl: config.apiUrl || "https://api.dim.cool",
36799
- storage: new NodeStorage(),
36996
+ storage,
36800
36997
  autoPay: {
36801
36998
  enabled: !this.externalSignerMode,
36802
36999
  maxAmountMinor: 25e3,
@@ -36900,10 +37097,10 @@ var DimAgentClient = class {
36900
37097
  /**
36901
37098
  * Complete authentication using an externally provided signature (external signer mode).
36902
37099
  * @param address - Solana wallet address (base58)
36903
- * @param signatureBase58 - Detached ed25519 signature in base58 (from sign_solana_message)
37100
+ * @param signature - Detached ed25519 signature in base58, base64, or base64url (from sign_solana_message)
36904
37101
  */
36905
- async completeAuth(address, signatureBase58) {
36906
- const signatureBytes = esm_default4.decode(signatureBase58);
37102
+ async completeAuth(address, signature) {
37103
+ const signatureBytes = signatureToBytes(signature);
36907
37104
  const signedMessage = Buffer.from(signatureBytes).toString("base64");
36908
37105
  const response = await this.sdk.auth.loginWithExternalSignature(
36909
37106
  address,
@@ -37057,7 +37254,7 @@ async function requestAuthMessage(client, args) {
37057
37254
  data: {
37058
37255
  message,
37059
37256
  address: args.address,
37060
- nextStep: 'Sign this message with sign_solana_message (networkId: "solana:mainnet"), then call dim_complete_login with the address and the base58 signature.'
37257
+ nextStep: 'Sign this message with sign_solana_message (networkId: "solana:mainnet"), then call dim_complete_login with the address and the signature. Accepted formats: base58, base64, or base64url (all formats returned by wallet MCPs are accepted).'
37061
37258
  }
37062
37259
  };
37063
37260
  } catch (error) {
@@ -38303,58 +38500,6 @@ function raceTimeout(promise, ms) {
38303
38500
  });
38304
38501
  }
38305
38502
 
38306
- // ../dim-agent-core/src/tools/challenges.ts
38307
- async function challengeUser(client, args) {
38308
- try {
38309
- if (!args.targetUsername && !args.targetUserId) {
38310
- return {
38311
- error: "Must provide either targetUsername or targetUserId.",
38312
- isError: true
38313
- };
38314
- }
38315
- const spendErr = client.checkSpendLimit(args.amount, true);
38316
- if (spendErr) return { error: spendErr, isError: true };
38317
- const amountMinor = Math.round(args.amount * 1e6);
38318
- const result = await client.sdk.challenges.create({
38319
- gameType: args.gameType,
38320
- amount: amountMinor,
38321
- targetUsername: args.targetUsername,
38322
- targetUserId: args.targetUserId
38323
- });
38324
- client.recordSpend(amountMinor);
38325
- return {
38326
- data: {
38327
- ...result,
38328
- amountFormatted: `$${args.amount.toFixed(2)}`,
38329
- hint: "The challenged user will receive a notification. When they accept, a lobby is created automatically."
38330
- }
38331
- };
38332
- } catch (error) {
38333
- return {
38334
- error: `Failed to create challenge: ${error instanceof Error ? error.message : String(error)}`,
38335
- isError: true
38336
- };
38337
- }
38338
- }
38339
- async function acceptChallenge(client, args) {
38340
- try {
38341
- const result = await client.sdk.challenges.accept(args.challengeId);
38342
- const spectateUrl = result.gameId ? await getSpectateUrl(client) : null;
38343
- return {
38344
- data: {
38345
- ...result,
38346
- ...spectateUrl && { spectateUrl },
38347
- hint: result.gameId ? `Game started! Call dim_game_loop with gameId "${result.gameId}".${spectateUrl ? ` Tell the user they can spectate at ${spectateUrl}` : ""}` : `Lobby created: ${result.lobbyId}. Use dim_join_queue to start matchmaking.`
38348
- }
38349
- };
38350
- } catch (error) {
38351
- return {
38352
- error: `Failed to accept challenge: ${error instanceof Error ? error.message : String(error)}`,
38353
- isError: true
38354
- };
38355
- }
38356
- }
38357
-
38358
38503
  // ../dim-agent-core/src/tools/referrals.ts
38359
38504
  async function getReferralSummary(client) {
38360
38505
  try {
@@ -39231,6 +39376,22 @@ async function getGameHistory(client, args) {
39231
39376
  };
39232
39377
  }
39233
39378
  }
39379
+ async function reportUser(client, args) {
39380
+ try {
39381
+ await client.sdk.reports.create(args.userId, args.reason);
39382
+ return {
39383
+ data: {
39384
+ success: true,
39385
+ hint: "Report submitted. The DIM moderation team will review it."
39386
+ }
39387
+ };
39388
+ } catch (error) {
39389
+ return {
39390
+ error: `Failed to report user: ${error instanceof Error ? error.message : String(error)}`,
39391
+ isError: true
39392
+ };
39393
+ }
39394
+ }
39234
39395
  async function getMyStats(client) {
39235
39396
  try {
39236
39397
  if (!client.currentUserId) {
@@ -39276,7 +39437,7 @@ var TOOL_DEFINITIONS = [
39276
39437
  },
39277
39438
  {
39278
39439
  name: "dim_complete_login",
39279
- description: "External wallet login step 2: provide the wallet address and base58 signature from sign_solana_message to complete authentication with DIM.",
39440
+ description: "External wallet login step 2: provide the wallet address and signature from sign_solana_message to complete authentication with DIM.",
39280
39441
  params: {
39281
39442
  address: {
39282
39443
  type: "string",
@@ -39285,7 +39446,7 @@ var TOOL_DEFINITIONS = [
39285
39446
  },
39286
39447
  signature: {
39287
39448
  type: "string",
39288
- description: "Base58-encoded signature returned by sign_solana_message",
39449
+ description: "Signature from sign_solana_message \u2014 base58, base64, or base64url accepted",
39289
39450
  required: true
39290
39451
  }
39291
39452
  },
@@ -39729,9 +39890,26 @@ var TOOL_DEFINITIONS = [
39729
39890
  params: {},
39730
39891
  execute: (c) => getMyStats(c)
39731
39892
  },
39893
+ {
39894
+ name: "dim_report_user",
39895
+ description: "Report a user for cheating, harassment, or other violations. Rate limited to 5 reports per hour.",
39896
+ params: {
39897
+ userId: {
39898
+ type: "string",
39899
+ description: "The ID of the user to report",
39900
+ required: true
39901
+ },
39902
+ reason: {
39903
+ type: "string",
39904
+ description: "Reason for the report (max 300 characters)",
39905
+ required: true
39906
+ }
39907
+ },
39908
+ execute: (c, a) => reportUser(c, a)
39909
+ },
39732
39910
  {
39733
39911
  name: "dim_create_lobby",
39734
- description: "Create a new game lobby. Bet amount is in USDC dollars (e.g., 1.00 for $1.00). A 1% fee (min 1 cent) is charged per player.",
39912
+ description: "Create a new game lobby. Bet amount is in USDC dollars (e.g., 1.00 for $1.00). A 1% fee (min 1 cent) is charged per player. NOTE: Creating a lobby automatically closes and refunds any previous open or queued lobby for this user \u2014 you do not need to clean up manually.",
39735
39913
  params: {
39736
39914
  gameType: {
39737
39915
  type: "string",
@@ -39945,45 +40123,6 @@ var TOOL_DEFINITIONS = [
39945
40123
  },
39946
40124
  execute: (c, a) => inviteToLobby(c, a)
39947
40125
  },
39948
- // ── Challenges ───────────────────────────────────────────────────────
39949
- {
39950
- name: "dim_challenge_user",
39951
- description: "Challenge a specific user to a game. This is the best way to invite someone you are chatting with \u2014 they get a notification and can accept directly. Prefer this over creating a lobby when you know who you want to play. Amount must be between $1 and $1000.",
39952
- params: {
39953
- gameType: { type: "string", description: "Game type", required: true },
39954
- amount: {
39955
- type: "number",
39956
- description: "Challenge amount in USDC dollars",
39957
- required: true,
39958
- min: 1,
39959
- max: 1e3
39960
- },
39961
- targetUsername: {
39962
- type: "string",
39963
- description: "Username of the user to challenge"
39964
- },
39965
- targetUserId: {
39966
- type: "string",
39967
- description: "User ID of the user to challenge"
39968
- }
39969
- },
39970
- execute: (c, a) => challengeUser(
39971
- c,
39972
- a
39973
- )
39974
- },
39975
- {
39976
- name: "dim_accept_challenge",
39977
- description: "Accept a challenge that was sent to you.",
39978
- params: {
39979
- challengeId: {
39980
- type: "string",
39981
- description: "The challenge ID to accept",
39982
- required: true
39983
- }
39984
- },
39985
- execute: (c, a) => acceptChallenge(c, a)
39986
- },
39987
40126
  // ── Referrals ────────────────────────────────────────────────────────
39988
40127
  {
39989
40128
  name: "dim_get_referral_summary",
@@ -40256,7 +40395,7 @@ var TOOL_DEFINITIONS = [
40256
40395
  // ── Notifications & Events ───────────────────────────────────────────
40257
40396
  {
40258
40397
  name: "dim_get_pending_events",
40259
- description: "Drain buffered real-time events (DMs, challenges, game turns, match notifications). Call regularly to stay aware.",
40398
+ description: "Drain buffered real-time events (DMs, game turns, match notifications). Call regularly to stay aware.",
40260
40399
  params: {},
40261
40400
  execute: (c) => getPendingEvents(c)
40262
40401
  },
@@ -40293,11 +40432,18 @@ async function executeWithAuthRetry(client, tool, params) {
40293
40432
  if (tool.name === "dim_login") {
40294
40433
  return tool.execute(client, params);
40295
40434
  }
40435
+ if (client.externalSignerMode) {
40436
+ return tool.execute(client, params);
40437
+ }
40296
40438
  try {
40297
40439
  const result = await tool.execute(client, params);
40298
40440
  if (!result.error && !result.isError) return result;
40299
40441
  if (!isUnauthorizedResult(result)) return result;
40300
- await client.authenticate();
40442
+ try {
40443
+ await client.authenticate();
40444
+ } catch {
40445
+ return { error: UNAUTHORIZED_MESSAGE, isError: true };
40446
+ }
40301
40447
  const retryResult = await tool.execute(client, params);
40302
40448
  if (retryResult.isError || retryResult.error) {
40303
40449
  if (isUnauthorizedResult(retryResult)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dimcool/dimclaw",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "OpenClaw plugin for DIM — play games, chat, send USDC, and earn on DIM using the SDK directly (no MCP subprocess).",
5
5
  "type": "module",
6
6
  "openclaw": {