@dimcool/mcp 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.
package/dist/index.js CHANGED
@@ -10964,7 +10964,7 @@ var require_money = __commonJS2({
10964
10964
  "../utils/dist/money.js"(exports) {
10965
10965
  "use strict";
10966
10966
  Object.defineProperty(exports, "__esModule", { value: true });
10967
- exports.SOL_LAMPORTS = exports.MICRO_UNITS = void 0;
10967
+ exports.SOL_MINT = exports.SOL_LAMPORTS = exports.MICRO_UNITS = void 0;
10968
10968
  exports.isSolMint = isSolMint;
10969
10969
  exports.formatMoneyMinor = formatMoneyMinor2;
10970
10970
  exports.toMajor = toMajor2;
@@ -10977,12 +10977,12 @@ var require_money = __commonJS2({
10977
10977
  exports.formatMoneyMinorCompact = formatMoneyMinorCompact;
10978
10978
  exports.MICRO_UNITS = 1e6;
10979
10979
  exports.SOL_LAMPORTS = 1e9;
10980
- var SOL_MINT_LABEL = "SOL";
10980
+ exports.SOL_MINT = "SOL";
10981
10981
  var SOL_SYSTEM_PROGRAM = "11111111111111111111111111111111";
10982
10982
  function isSolMint(mint) {
10983
10983
  if (!mint)
10984
10984
  return false;
10985
- return mint === SOL_MINT_LABEL || mint === SOL_SYSTEM_PROGRAM;
10985
+ return mint === exports.SOL_MINT || mint === SOL_SYSTEM_PROGRAM;
10986
10986
  }
10987
10987
  function formatMoneyMinor2(amountMinor, fractionDigits = 2) {
10988
10988
  return (amountMinor / exports.MICRO_UNITS).toFixed(fractionDigits);
@@ -11086,7 +11086,7 @@ var require_dist = __commonJS2({
11086
11086
  "../utils/dist/index.js"(exports) {
11087
11087
  "use strict";
11088
11088
  Object.defineProperty(exports, "__esModule", { value: true });
11089
- 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;
11089
+ 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;
11090
11090
  var money_1 = require_money();
11091
11091
  Object.defineProperty(exports, "MICRO_UNITS", { enumerable: true, get: function() {
11092
11092
  return money_1.MICRO_UNITS;
@@ -11094,6 +11094,9 @@ var require_dist = __commonJS2({
11094
11094
  Object.defineProperty(exports, "SOL_LAMPORTS", { enumerable: true, get: function() {
11095
11095
  return money_1.SOL_LAMPORTS;
11096
11096
  } });
11097
+ Object.defineProperty(exports, "SOL_MINT", { enumerable: true, get: function() {
11098
+ return money_1.SOL_MINT;
11099
+ } });
11097
11100
  Object.defineProperty(exports, "isSolMint", { enumerable: true, get: function() {
11098
11101
  return money_1.isSolMint;
11099
11102
  } });
@@ -11630,6 +11633,9 @@ var Admin = class {
11630
11633
  this.http = http;
11631
11634
  this.logger = logger2;
11632
11635
  }
11636
+ async getInternalBots() {
11637
+ return this.http.get("/admin/internal-bots");
11638
+ }
11633
11639
  async getUserById(id) {
11634
11640
  return this.http.get(`/admin/users/${id}`);
11635
11641
  }
@@ -11981,6 +11987,13 @@ var Lobbies = class {
11981
11987
  setLobbyStore(store) {
11982
11988
  this.lobbyStore = store;
11983
11989
  }
11990
+ /**
11991
+ * Create a new game lobby.
11992
+ *
11993
+ * **Important:** Creating a lobby automatically closes and refunds any
11994
+ * existing open or queued lobby for this user. You do not need to manually
11995
+ * leave or cancel a previous lobby before calling this.
11996
+ */
11984
11997
  async createLobby(gameType, betAmount) {
11985
11998
  return this.http.post("/lobbies", { gameType, betAmount });
11986
11999
  }
@@ -12044,21 +12057,6 @@ var Lobbies = class {
12044
12057
  async deleteLobby(lobbyId) {
12045
12058
  return this.http.delete(`/lobbies/admin/${lobbyId}`);
12046
12059
  }
12047
- /**
12048
- * Play again: Create a new lobby and prepare deposit in one flow.
12049
- * Returns the lobby and unsigned transaction that needs to be signed.
12050
- * @param gameType - The game type to play again
12051
- * @param betAmount - The bet amount (same as previous game)
12052
- * @param escrow - The escrow service instance (from sdk.escrow)
12053
- */
12054
- async playAgain(gameType, betAmount, escrow) {
12055
- const lobby = await this.createLobby(gameType, betAmount);
12056
- const { transaction } = await escrow.prepareAndStartDeposit(lobby.id);
12057
- return {
12058
- lobby,
12059
- unsignedTransaction: transaction
12060
- };
12061
- }
12062
12060
  };
12063
12061
  var Games = class {
12064
12062
  constructor(http, wallet, logger2) {
@@ -12578,6 +12576,7 @@ var Achievements = class {
12578
12576
  return this.http.get(`/achievements/users/${userId}`);
12579
12577
  }
12580
12578
  };
12579
+ var import_utils = __toESM2(require_dist(), 1);
12581
12580
  var MIN_TRANSFER_AMOUNT = 5e4;
12582
12581
  var MIN_SOL_TRANSFER_AMOUNT = 1e6;
12583
12582
  var Wallet = class {
@@ -14253,8 +14252,21 @@ function createGameActionsStore(transport2) {
14253
14252
  });
14254
14253
  };
14255
14254
  const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
14255
+ const pendingEvents = /* @__PURE__ */ new Map();
14256
+ function enqueue(gameId, event) {
14257
+ const q = pendingEvents.get(gameId) ?? [];
14258
+ q.push(event);
14259
+ pendingEvents.set(gameId, q);
14260
+ }
14261
+ function drainQueue(gameId) {
14262
+ const q = pendingEvents.get(gameId);
14263
+ if (!q?.length) return;
14264
+ pendingEvents.delete(gameId);
14265
+ for (const ev of q) applyWsEvent(ev);
14266
+ }
14256
14267
  const setBaseState = (gameId, state) => {
14257
14268
  updateState(gameId, state);
14269
+ drainQueue(gameId);
14258
14270
  };
14259
14271
  const clearState = (gameId) => {
14260
14272
  store.updateState((state) => {
@@ -14264,6 +14276,7 @@ function createGameActionsStore(transport2) {
14264
14276
  const { [gameId]: _, ...rest } = state.statesByGameId;
14265
14277
  return { ...state, statesByGameId: rest };
14266
14278
  });
14279
+ pendingEvents.delete(gameId);
14267
14280
  };
14268
14281
  const applyWsEvent = (event) => {
14269
14282
  switch (event.event) {
@@ -14282,7 +14295,10 @@ function createGameActionsStore(transport2) {
14282
14295
  }
14283
14296
  case "game:rps:starting": {
14284
14297
  const current = store.getState().statesByGameId[event.payload.gameId];
14285
- if (!current || !isRpsState(current)) return;
14298
+ if (!current || !isRpsState(current)) {
14299
+ enqueue(event.payload.gameId, event);
14300
+ return;
14301
+ }
14286
14302
  const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
14287
14303
  const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
14288
14304
  const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
@@ -14303,7 +14319,10 @@ function createGameActionsStore(transport2) {
14303
14319
  }
14304
14320
  case "game:rps:round:started": {
14305
14321
  const current = store.getState().statesByGameId[event.payload.gameId];
14306
- if (!current || !isRpsState(current)) return;
14322
+ if (!current || !isRpsState(current)) {
14323
+ enqueue(event.payload.gameId, event);
14324
+ return;
14325
+ }
14307
14326
  const actions = {};
14308
14327
  const baseUsers = /* @__PURE__ */ new Set();
14309
14328
  Object.keys(current.roundState.actions).forEach(
@@ -14337,7 +14356,10 @@ function createGameActionsStore(transport2) {
14337
14356
  }
14338
14357
  case "game:rps:action:received": {
14339
14358
  const current = store.getState().statesByGameId[event.payload.gameId];
14340
- if (!current || !isRpsState(current)) return;
14359
+ if (!current || !isRpsState(current)) {
14360
+ enqueue(event.payload.gameId, event);
14361
+ return;
14362
+ }
14341
14363
  const updated = {
14342
14364
  ...current,
14343
14365
  roundState: {
@@ -14356,7 +14378,10 @@ function createGameActionsStore(transport2) {
14356
14378
  }
14357
14379
  case "game:rps:timer:cutoff": {
14358
14380
  const current = store.getState().statesByGameId[event.payload.gameId];
14359
- if (!current || !isRpsState(current)) return;
14381
+ if (!current || !isRpsState(current)) {
14382
+ enqueue(event.payload.gameId, event);
14383
+ return;
14384
+ }
14360
14385
  const updated = {
14361
14386
  ...current,
14362
14387
  roundState: {
@@ -14370,7 +14395,10 @@ function createGameActionsStore(transport2) {
14370
14395
  }
14371
14396
  case "game:rps:round:reveal": {
14372
14397
  const current = store.getState().statesByGameId[event.payload.gameId];
14373
- if (!current || !isRpsState(current)) return;
14398
+ if (!current || !isRpsState(current)) {
14399
+ enqueue(event.payload.gameId, event);
14400
+ return;
14401
+ }
14374
14402
  const actions = {};
14375
14403
  const payloadActions = event.payload.actions;
14376
14404
  Object.keys(payloadActions || {}).forEach((userId) => {
@@ -14394,7 +14422,10 @@ function createGameActionsStore(transport2) {
14394
14422
  }
14395
14423
  case "game:rps:round:completed": {
14396
14424
  const current = store.getState().statesByGameId[event.payload.gameId];
14397
- if (!current || !isRpsState(current)) return;
14425
+ if (!current || !isRpsState(current)) {
14426
+ enqueue(event.payload.gameId, event);
14427
+ return;
14428
+ }
14398
14429
  const roundHistory = [
14399
14430
  ...current.roundHistory || [],
14400
14431
  {
@@ -14419,7 +14450,10 @@ function createGameActionsStore(transport2) {
14419
14450
  }
14420
14451
  case "game:rps:timeout": {
14421
14452
  const current = store.getState().statesByGameId[event.payload.gameId];
14422
- if (!current || !isRpsState(current)) return;
14453
+ if (!current || !isRpsState(current)) {
14454
+ enqueue(event.payload.gameId, event);
14455
+ return;
14456
+ }
14423
14457
  const timedOutUser = event.payload.playerId;
14424
14458
  const action = event.payload.action;
14425
14459
  const updated = {
@@ -14444,7 +14478,10 @@ function createGameActionsStore(transport2) {
14444
14478
  const payload = event.payload;
14445
14479
  const { gameId } = payload;
14446
14480
  const current = store.getState().statesByGameId[gameId];
14447
- if (!current) return;
14481
+ if (!current) {
14482
+ enqueue(gameId, event);
14483
+ return;
14484
+ }
14448
14485
  const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
14449
14486
  ...current,
14450
14487
  status: "completed",
@@ -14493,11 +14530,15 @@ function createGameActionsStore(transport2) {
14493
14530
  const current = store.getState().statesByGameId[gameId];
14494
14531
  const updated = current ? { ...current, ...incoming } : incoming;
14495
14532
  updateState(gameId, updated);
14533
+ drainQueue(gameId);
14496
14534
  break;
14497
14535
  }
14498
14536
  case "game:rematch:requested": {
14499
14537
  const current = store.getState().statesByGameId[event.payload.gameId];
14500
- if (!current) return;
14538
+ if (!current) {
14539
+ enqueue(event.payload.gameId, event);
14540
+ return;
14541
+ }
14501
14542
  const requestedBy = event.payload.requestedBy;
14502
14543
  const userId = event.payload.userId;
14503
14544
  const requested = new Set(
@@ -14513,7 +14554,10 @@ function createGameActionsStore(transport2) {
14513
14554
  }
14514
14555
  case "game:rematch:cancelled": {
14515
14556
  const current = store.getState().statesByGameId[event.payload.gameId];
14516
- if (!current) return;
14557
+ if (!current) {
14558
+ enqueue(event.payload.gameId, event);
14559
+ return;
14560
+ }
14517
14561
  const requestedBy = event.payload.requestedBy ?? [];
14518
14562
  const updated = {
14519
14563
  ...current,
@@ -14524,7 +14568,10 @@ function createGameActionsStore(transport2) {
14524
14568
  }
14525
14569
  case "game:rematch:started": {
14526
14570
  const current = store.getState().statesByGameId[event.payload.gameId];
14527
- if (!current) return;
14571
+ if (!current) {
14572
+ enqueue(event.payload.gameId, event);
14573
+ return;
14574
+ }
14528
14575
  const updated = {
14529
14576
  ...current,
14530
14577
  rematchRequestedBy: event.payload.playerIds ?? []
@@ -14535,7 +14582,10 @@ function createGameActionsStore(transport2) {
14535
14582
  case "game:pot:updated": {
14536
14583
  const { gameId, totalPotMinor } = event.payload;
14537
14584
  const current = store.getState().statesByGameId[gameId];
14538
- if (!current) return;
14585
+ if (!current) {
14586
+ enqueue(gameId, event);
14587
+ return;
14588
+ }
14539
14589
  const updated = {
14540
14590
  ...current,
14541
14591
  totalPotMinor
@@ -14548,12 +14598,123 @@ function createGameActionsStore(transport2) {
14548
14598
  }
14549
14599
  };
14550
14600
  const joinGame = (gameId) => transport2.joinRoom(`game:${gameId}`);
14601
+ const getCountdownDigit = (gameId, nowMs) => {
14602
+ const state = store.getState().statesByGameId[gameId];
14603
+ if (!state) return null;
14604
+ if (isRpsState(state)) {
14605
+ if (state.roundState.phase !== "starting") return null;
14606
+ const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
14607
+ if (remaining <= 0) return null;
14608
+ return Math.ceil(remaining / 1e3);
14609
+ }
14610
+ const bufferEndsAt = state.bufferEndsAt;
14611
+ if (bufferEndsAt) {
14612
+ const remaining = new Date(bufferEndsAt).getTime() - nowMs;
14613
+ if (remaining <= 0) return null;
14614
+ return Math.ceil(remaining / 1e3);
14615
+ }
14616
+ return null;
14617
+ };
14618
+ const getChessClockTimes = (gameId, nowMs) => {
14619
+ const state = store.getState().statesByGameId[gameId];
14620
+ if (!state || state.gameType !== "chess") return null;
14621
+ const s = state;
14622
+ let whiteMs = s.whiteTimeMs ?? 0;
14623
+ let blackMs = s.blackTimeMs ?? 0;
14624
+ if (s.status === "active" && s.currentPlayerId) {
14625
+ const startedAt = Date.parse(s.turnStartedAt);
14626
+ if (!Number.isNaN(startedAt)) {
14627
+ const elapsed = Math.max(0, nowMs - startedAt);
14628
+ if (s.currentPlayerId === s.whitePlayerId) {
14629
+ whiteMs = Math.max(0, whiteMs - elapsed);
14630
+ } else if (s.currentPlayerId === s.blackPlayerId) {
14631
+ blackMs = Math.max(0, blackMs - elapsed);
14632
+ }
14633
+ }
14634
+ }
14635
+ return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
14636
+ };
14637
+ const getChessCapturedPieces = (gameId) => {
14638
+ const state = store.getState().statesByGameId[gameId];
14639
+ if (!state || state.gameType !== "chess") return null;
14640
+ const fen = state.fen;
14641
+ if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
14642
+ const placement = fen.split(" ")[0];
14643
+ const white = {};
14644
+ const black = {};
14645
+ for (const char of placement) {
14646
+ if (char === "/" || char >= "1" && char <= "8") continue;
14647
+ const lower = char.toLowerCase();
14648
+ if (char === lower) {
14649
+ black[lower] = (black[lower] ?? 0) + 1;
14650
+ } else {
14651
+ white[lower] = (white[lower] ?? 0) + 1;
14652
+ }
14653
+ }
14654
+ const INITIAL = {
14655
+ p: 8,
14656
+ r: 2,
14657
+ n: 2,
14658
+ b: 2,
14659
+ q: 1,
14660
+ k: 1
14661
+ };
14662
+ const capturedByWhite = [];
14663
+ const capturedByBlack = [];
14664
+ for (const [type, initial] of Object.entries(INITIAL)) {
14665
+ const missingBlack = initial - (black[type] ?? 0);
14666
+ for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type}`);
14667
+ const missingWhite = initial - (white[type] ?? 0);
14668
+ for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type}`);
14669
+ }
14670
+ return { capturedByWhite, capturedByBlack };
14671
+ };
14672
+ const getTicTacToeClockTimes = (gameId, nowMs) => {
14673
+ const state = store.getState().statesByGameId[gameId];
14674
+ if (!state || state.gameType !== "tic-tac-toe") return null;
14675
+ const s = state;
14676
+ let xMs = s.xTimeMs ?? 0;
14677
+ let oMs = s.oTimeMs ?? 0;
14678
+ if (s.status === "active" && s.currentPlayerId) {
14679
+ const currentMark = s.playerMarks[s.currentPlayerId];
14680
+ const startedAt = Date.parse(s.turnStartedAt);
14681
+ if (!Number.isNaN(startedAt)) {
14682
+ const elapsed = Math.max(0, nowMs - startedAt);
14683
+ if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
14684
+ else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
14685
+ }
14686
+ }
14687
+ return { xTimeMs: xMs, oTimeMs: oMs };
14688
+ };
14689
+ const getConnect4ClockTimes = (gameId, nowMs) => {
14690
+ const state = store.getState().statesByGameId[gameId];
14691
+ if (!state || state.gameType !== "connect-four") return null;
14692
+ const s = state;
14693
+ let redMs = s.redTimeMs ?? 0;
14694
+ let yellowMs = s.yellowTimeMs ?? 0;
14695
+ if (s.status === "active" && s.currentPlayerId) {
14696
+ const currentColor = s.playerColors[s.currentPlayerId];
14697
+ const startedAt = Date.parse(s.turnStartedAt);
14698
+ if (!Number.isNaN(startedAt)) {
14699
+ const elapsed = Math.max(0, nowMs - startedAt);
14700
+ if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
14701
+ else if (currentColor === "YELLOW")
14702
+ yellowMs = Math.max(0, yellowMs - elapsed);
14703
+ }
14704
+ }
14705
+ return { redTimeMs: redMs, yellowTimeMs: yellowMs };
14706
+ };
14551
14707
  return {
14552
14708
  store,
14553
14709
  setBaseState,
14554
14710
  clearState,
14555
14711
  applyWsEvent,
14556
- joinGame
14712
+ joinGame,
14713
+ getCountdownDigit,
14714
+ getChessClockTimes,
14715
+ getChessCapturedPieces,
14716
+ getTicTacToeClockTimes,
14717
+ getConnect4ClockTimes
14557
14718
  };
14558
14719
  }
14559
14720
  function isRpsState(state) {
@@ -15211,12 +15372,14 @@ var WsRouter = class {
15211
15372
  }
15212
15373
  const decoded = decodeWsEvent(eventName, payload);
15213
15374
  if (!decoded) return;
15214
- this.deps.lobbyStore.applyWsEvent(decoded);
15215
- this.deps.gameStore.applyWsEvent(decoded);
15216
- this.deps.gameActionsStore.applyWsEvent(decoded);
15217
- this.deps.chatStore.applyWsEvent(decoded);
15218
- this.deps.dmThreadsStore.applyWsEvent(decoded);
15219
- this.deps.notificationsStore.applyWsEvent(decoded);
15375
+ const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
15376
+ const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
15377
+ this.deps.lobbyStore.applyWsEvent(event);
15378
+ this.deps.gameStore.applyWsEvent(event);
15379
+ this.deps.gameActionsStore.applyWsEvent(event);
15380
+ this.deps.chatStore.applyWsEvent(event);
15381
+ this.deps.dmThreadsStore.applyWsEvent(event);
15382
+ this.deps.notificationsStore.applyWsEvent(event);
15220
15383
  });
15221
15384
  }
15222
15385
  stop() {
@@ -15228,12 +15391,14 @@ var WsRouter = class {
15228
15391
  this.transport.subscribeEvent(eventName, (payload) => {
15229
15392
  const decoded = decodeWsEvent(eventName, payload);
15230
15393
  if (!decoded) return;
15231
- this.deps.lobbyStore.applyWsEvent(decoded);
15232
- this.deps.gameStore.applyWsEvent(decoded);
15233
- this.deps.gameActionsStore.applyWsEvent(decoded);
15234
- this.deps.chatStore.applyWsEvent(decoded);
15235
- this.deps.dmThreadsStore.applyWsEvent(decoded);
15236
- this.deps.notificationsStore.applyWsEvent(decoded);
15394
+ const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
15395
+ const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
15396
+ this.deps.lobbyStore.applyWsEvent(event);
15397
+ this.deps.gameStore.applyWsEvent(event);
15398
+ this.deps.gameActionsStore.applyWsEvent(event);
15399
+ this.deps.chatStore.applyWsEvent(event);
15400
+ this.deps.dmThreadsStore.applyWsEvent(event);
15401
+ this.deps.notificationsStore.applyWsEvent(event);
15237
15402
  });
15238
15403
  }
15239
15404
  }
@@ -15371,15 +15536,38 @@ var SDK = class {
15371
15536
  return { ...result, lobby };
15372
15537
  }
15373
15538
  };
15374
- var import_utils = __toESM2(require_dist(), 1);
15375
- var export_MICRO_UNITS = import_utils.MICRO_UNITS;
15376
- var export_formatMoneyMinor = import_utils.formatMoneyMinor;
15377
- var export_toMajor = import_utils.toMajor;
15539
+ var import_utils2 = __toESM2(require_dist(), 1);
15540
+ var export_MICRO_UNITS = import_utils2.MICRO_UNITS;
15541
+ var export_SOL_MINT = import_utils.SOL_MINT;
15542
+ var export_formatMoneyMinor = import_utils2.formatMoneyMinor;
15543
+ var export_toMajor = import_utils2.toMajor;
15378
15544
 
15379
15545
  // ../dim-agent-core/src/client.ts
15380
15546
  var import_tweetnacl = __toESM(require_nacl_fast(), 1);
15381
15547
  import { Keypair } from "@solana/web3.js";
15382
15548
  import bs58 from "bs58";
15549
+ function decodeJwtSub(token) {
15550
+ try {
15551
+ const parts2 = token.split(".");
15552
+ if (parts2.length < 2) return null;
15553
+ const base64 = parts2[1].replace(/-/g, "+").replace(/_/g, "/");
15554
+ const json = Buffer.from(base64, "base64").toString("utf-8");
15555
+ const parsed = JSON.parse(json);
15556
+ return typeof parsed.sub === "string" ? parsed.sub : null;
15557
+ } catch {
15558
+ return null;
15559
+ }
15560
+ }
15561
+ function signatureToBytes(sig) {
15562
+ if (sig.includes("-") || sig.includes("_")) {
15563
+ const padded = sig.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(sig.length % 4 === 0 ? 4 : sig.length % 4);
15564
+ return new Uint8Array(Buffer.from(padded, "base64"));
15565
+ }
15566
+ if (/[+/=]/.test(sig)) {
15567
+ return new Uint8Array(Buffer.from(sig, "base64"));
15568
+ }
15569
+ return bs58.decode(sig);
15570
+ }
15383
15571
  var DimAgentClient = class {
15384
15572
  sdk;
15385
15573
  agentConfig;
@@ -15406,10 +15594,19 @@ var DimAgentClient = class {
15406
15594
  } else {
15407
15595
  this.keypair = null;
15408
15596
  }
15597
+ const storage = new NodeStorage();
15598
+ if (config.accessToken) {
15599
+ storage.set(TOKEN_KEY, config.accessToken);
15600
+ const sub = decodeJwtSub(config.accessToken);
15601
+ if (sub) {
15602
+ this.authenticated = true;
15603
+ this.userId = sub;
15604
+ }
15605
+ }
15409
15606
  this.sdk = new SDK({
15410
15607
  appId: "dim-agents",
15411
15608
  baseUrl: config.apiUrl || "https://api.dim.cool",
15412
- storage: new NodeStorage(),
15609
+ storage,
15413
15610
  autoPay: {
15414
15611
  enabled: !this.externalSignerMode,
15415
15612
  maxAmountMinor: 25e3,
@@ -15513,10 +15710,10 @@ var DimAgentClient = class {
15513
15710
  /**
15514
15711
  * Complete authentication using an externally provided signature (external signer mode).
15515
15712
  * @param address - Solana wallet address (base58)
15516
- * @param signatureBase58 - Detached ed25519 signature in base58 (from sign_solana_message)
15713
+ * @param signature - Detached ed25519 signature in base58, base64, or base64url (from sign_solana_message)
15517
15714
  */
15518
- async completeAuth(address, signatureBase58) {
15519
- const signatureBytes = bs58.decode(signatureBase58);
15715
+ async completeAuth(address, signature) {
15716
+ const signatureBytes = signatureToBytes(signature);
15520
15717
  const signedMessage = Buffer.from(signatureBytes).toString("base64");
15521
15718
  const response = await this.sdk.auth.loginWithExternalSignature(
15522
15719
  address,
@@ -15670,7 +15867,7 @@ async function requestAuthMessage(client, args) {
15670
15867
  data: {
15671
15868
  message,
15672
15869
  address: args.address,
15673
- nextStep: 'Sign this message with sign_solana_message (networkId: "solana:mainnet"), then call dim_complete_login with the address and the base58 signature.'
15870
+ 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).'
15674
15871
  }
15675
15872
  };
15676
15873
  } catch (error) {
@@ -16916,58 +17113,6 @@ function raceTimeout(promise, ms) {
16916
17113
  });
16917
17114
  }
16918
17115
 
16919
- // ../dim-agent-core/src/tools/challenges.ts
16920
- async function challengeUser(client, args) {
16921
- try {
16922
- if (!args.targetUsername && !args.targetUserId) {
16923
- return {
16924
- error: "Must provide either targetUsername or targetUserId.",
16925
- isError: true
16926
- };
16927
- }
16928
- const spendErr = client.checkSpendLimit(args.amount, true);
16929
- if (spendErr) return { error: spendErr, isError: true };
16930
- const amountMinor = Math.round(args.amount * 1e6);
16931
- const result = await client.sdk.challenges.create({
16932
- gameType: args.gameType,
16933
- amount: amountMinor,
16934
- targetUsername: args.targetUsername,
16935
- targetUserId: args.targetUserId
16936
- });
16937
- client.recordSpend(amountMinor);
16938
- return {
16939
- data: {
16940
- ...result,
16941
- amountFormatted: `$${args.amount.toFixed(2)}`,
16942
- hint: "The challenged user will receive a notification. When they accept, a lobby is created automatically."
16943
- }
16944
- };
16945
- } catch (error) {
16946
- return {
16947
- error: `Failed to create challenge: ${error instanceof Error ? error.message : String(error)}`,
16948
- isError: true
16949
- };
16950
- }
16951
- }
16952
- async function acceptChallenge(client, args) {
16953
- try {
16954
- const result = await client.sdk.challenges.accept(args.challengeId);
16955
- const spectateUrl = result.gameId ? await getSpectateUrl(client) : null;
16956
- return {
16957
- data: {
16958
- ...result,
16959
- ...spectateUrl && { spectateUrl },
16960
- 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.`
16961
- }
16962
- };
16963
- } catch (error) {
16964
- return {
16965
- error: `Failed to accept challenge: ${error instanceof Error ? error.message : String(error)}`,
16966
- isError: true
16967
- };
16968
- }
16969
- }
16970
-
16971
17116
  // ../dim-agent-core/src/tools/referrals.ts
16972
17117
  async function getReferralSummary(client) {
16973
17118
  try {
@@ -17844,6 +17989,22 @@ async function getGameHistory(client, args) {
17844
17989
  };
17845
17990
  }
17846
17991
  }
17992
+ async function reportUser(client, args) {
17993
+ try {
17994
+ await client.sdk.reports.create(args.userId, args.reason);
17995
+ return {
17996
+ data: {
17997
+ success: true,
17998
+ hint: "Report submitted. The DIM moderation team will review it."
17999
+ }
18000
+ };
18001
+ } catch (error) {
18002
+ return {
18003
+ error: `Failed to report user: ${error instanceof Error ? error.message : String(error)}`,
18004
+ isError: true
18005
+ };
18006
+ }
18007
+ }
17847
18008
  async function getMyStats(client) {
17848
18009
  try {
17849
18010
  if (!client.currentUserId) {
@@ -17889,7 +18050,7 @@ var TOOL_DEFINITIONS = [
17889
18050
  },
17890
18051
  {
17891
18052
  name: "dim_complete_login",
17892
- description: "External wallet login step 2: provide the wallet address and base58 signature from sign_solana_message to complete authentication with DIM.",
18053
+ description: "External wallet login step 2: provide the wallet address and signature from sign_solana_message to complete authentication with DIM.",
17893
18054
  params: {
17894
18055
  address: {
17895
18056
  type: "string",
@@ -17898,7 +18059,7 @@ var TOOL_DEFINITIONS = [
17898
18059
  },
17899
18060
  signature: {
17900
18061
  type: "string",
17901
- description: "Base58-encoded signature returned by sign_solana_message",
18062
+ description: "Signature from sign_solana_message \u2014 base58, base64, or base64url accepted",
17902
18063
  required: true
17903
18064
  }
17904
18065
  },
@@ -18342,9 +18503,26 @@ var TOOL_DEFINITIONS = [
18342
18503
  params: {},
18343
18504
  execute: (c) => getMyStats(c)
18344
18505
  },
18506
+ {
18507
+ name: "dim_report_user",
18508
+ description: "Report a user for cheating, harassment, or other violations. Rate limited to 5 reports per hour.",
18509
+ params: {
18510
+ userId: {
18511
+ type: "string",
18512
+ description: "The ID of the user to report",
18513
+ required: true
18514
+ },
18515
+ reason: {
18516
+ type: "string",
18517
+ description: "Reason for the report (max 300 characters)",
18518
+ required: true
18519
+ }
18520
+ },
18521
+ execute: (c, a) => reportUser(c, a)
18522
+ },
18345
18523
  {
18346
18524
  name: "dim_create_lobby",
18347
- 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.",
18525
+ 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.",
18348
18526
  params: {
18349
18527
  gameType: {
18350
18528
  type: "string",
@@ -18558,45 +18736,6 @@ var TOOL_DEFINITIONS = [
18558
18736
  },
18559
18737
  execute: (c, a) => inviteToLobby(c, a)
18560
18738
  },
18561
- // ── Challenges ───────────────────────────────────────────────────────
18562
- {
18563
- name: "dim_challenge_user",
18564
- 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.",
18565
- params: {
18566
- gameType: { type: "string", description: "Game type", required: true },
18567
- amount: {
18568
- type: "number",
18569
- description: "Challenge amount in USDC dollars",
18570
- required: true,
18571
- min: 1,
18572
- max: 1e3
18573
- },
18574
- targetUsername: {
18575
- type: "string",
18576
- description: "Username of the user to challenge"
18577
- },
18578
- targetUserId: {
18579
- type: "string",
18580
- description: "User ID of the user to challenge"
18581
- }
18582
- },
18583
- execute: (c, a) => challengeUser(
18584
- c,
18585
- a
18586
- )
18587
- },
18588
- {
18589
- name: "dim_accept_challenge",
18590
- description: "Accept a challenge that was sent to you.",
18591
- params: {
18592
- challengeId: {
18593
- type: "string",
18594
- description: "The challenge ID to accept",
18595
- required: true
18596
- }
18597
- },
18598
- execute: (c, a) => acceptChallenge(c, a)
18599
- },
18600
18739
  // ── Referrals ────────────────────────────────────────────────────────
18601
18740
  {
18602
18741
  name: "dim_get_referral_summary",
@@ -18869,7 +19008,7 @@ var TOOL_DEFINITIONS = [
18869
19008
  // ── Notifications & Events ───────────────────────────────────────────
18870
19009
  {
18871
19010
  name: "dim_get_pending_events",
18872
- description: "Drain buffered real-time events (DMs, challenges, game turns, match notifications). Call regularly to stay aware.",
19011
+ description: "Drain buffered real-time events (DMs, game turns, match notifications). Call regularly to stay aware.",
18873
19012
  params: {},
18874
19013
  execute: (c) => getPendingEvents(c)
18875
19014
  },
@@ -18906,11 +19045,18 @@ async function executeWithAuthRetry(client, tool, params) {
18906
19045
  if (tool.name === "dim_login") {
18907
19046
  return tool.execute(client, params);
18908
19047
  }
19048
+ if (client.externalSignerMode) {
19049
+ return tool.execute(client, params);
19050
+ }
18909
19051
  try {
18910
19052
  const result = await tool.execute(client, params);
18911
19053
  if (!result.error && !result.isError) return result;
18912
19054
  if (!isUnauthorizedResult(result)) return result;
18913
- await client.authenticate();
19055
+ try {
19056
+ await client.authenticate();
19057
+ } catch {
19058
+ return { error: UNAUTHORIZED_MESSAGE, isError: true };
19059
+ }
18914
19060
  const retryResult = await tool.execute(client, params);
18915
19061
  if (retryResult.isError || retryResult.error) {
18916
19062
  if (isUnauthorizedResult(retryResult)) {
@@ -19113,6 +19259,10 @@ function registerResources(server2, client) {
19113
19259
  }
19114
19260
 
19115
19261
  // src/server.ts
19262
+ var HOSTED_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
19263
+ "dim_game_loop",
19264
+ "dim_get_pending_events"
19265
+ ]);
19116
19266
  function paramToZod(p) {
19117
19267
  let schema;
19118
19268
  if (p.enum) {
@@ -19133,14 +19283,14 @@ function paramToZod(p) {
19133
19283
  if (!p.required) schema = schema.optional();
19134
19284
  return schema.describe(p.description);
19135
19285
  }
19136
- function createDimMcpServer(config) {
19286
+ function buildMcpServer(client, mode) {
19137
19287
  const server2 = new McpServer({
19138
19288
  name: "dim",
19139
19289
  version: "1.0.0",
19140
- description: "DIM Gaming Platform \u2014 Play games, chat, send USDC, challenge users, and earn referral income. DIM is noncustodial; your Solana address is returned by dim_get_profile and dim_get_balance. Start by calling dim_login. After login: set a username with dim_set_username, check balance with dim_get_balance, list games with dim_list_games, or apply a referral code with dim_apply_referral_code for a 10% fee discount."
19290
+ description: "DIM Gaming Platform \u2014 Play games, chat, send USDC, and earn referral income. DIM is noncustodial; your Solana address is returned by dim_get_profile and dim_get_balance. Start by calling dim_login. After login: set a username with dim_set_username, check balance with dim_get_balance, list games with dim_list_games, or apply a referral code with dim_apply_referral_code for a 10% fee discount."
19141
19291
  });
19142
- const client = new DimAgentClient(config);
19143
19292
  for (const tool of TOOL_DEFINITIONS) {
19293
+ if (mode === "hosted" && HOSTED_EXCLUDED_TOOLS.has(tool.name)) continue;
19144
19294
  const zodParams = {};
19145
19295
  for (const [key, param] of Object.entries(tool.params)) {
19146
19296
  zodParams[key] = paramToZod(param);
@@ -19170,6 +19320,7 @@ function createDimMcpServer(config) {
19170
19320
  }
19171
19321
  );
19172
19322
  }
19323
+ const gameHint = mode === "hosted" ? "Game flow: dim_create_lobby \u2192 dim_deposit_for_lobby (returns unsignedTx \u2014 sign with Phantom MCP) \u2192 dim_join_queue \u2192 poll dim_get_lobby until matched \u2192 dim_get_game_state \u2192 dim_submit_action \u2192 repeat. For real-time game_loop, use the DIM CLI (npx @dimcool/cli)." : "Game flow: For paid lobbies, dim_create_lobby \u2192 dim_deposit_for_lobby \u2192 dim_join_queue. For free lobbies, dim_create_lobby \u2192 dim_join_queue. Then dim_game_loop (blocks until your turn) \u2192 dim_submit_action \u2192 dim_game_loop \u2192 repeat. Use dim_leave_lobby to exit a lobby.";
19173
19324
  server2.tool(
19174
19325
  "dim_list_instructions",
19175
19326
  "List all available DIM tools with short descriptions.",
@@ -19183,8 +19334,8 @@ function createDimMcpServer(config) {
19183
19334
  wallet_linkage: WALLET_LINKAGE_NOTE,
19184
19335
  wallet_address: "Your DIM Solana address is returned by dim_get_profile, dim_get_balance, dim_get_wallet_activity, and dim_get_wallet_address. Use it to confirm which address is tied to DIM.",
19185
19336
  wallet_activity: "For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.",
19186
- hint: "Game flow: For paid lobbies, dim_create_lobby \u2192 dim_deposit_for_lobby \u2192 dim_join_queue. For free lobbies, dim_create_lobby \u2192 dim_join_queue. Then dim_game_loop (blocks until your turn) \u2192 dim_submit_action \u2192 dim_game_loop \u2192 repeat. Use dim_leave_lobby to exit a lobby.",
19187
- spectate_on_game_start: "When a game starts (dim_join_queue or dim_accept_challenge returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].",
19337
+ hint: gameHint,
19338
+ spectate_on_game_start: "When a game starts (dim_join_queue returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].",
19188
19339
  daily_summary: "At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.",
19189
19340
  move_formats: {
19190
19341
  "rock-paper-scissors": 'action: "play", payload: { action: "rock"|"paper"|"scissors" }',
@@ -19204,6 +19355,15 @@ function createDimMcpServer(config) {
19204
19355
  })
19205
19356
  );
19206
19357
  registerResources(server2, client);
19358
+ return server2;
19359
+ }
19360
+ function createDimMcpServer(clientOrConfig, options) {
19361
+ const mode = options?.mode ?? "local";
19362
+ if (clientOrConfig instanceof DimAgentClient) {
19363
+ return buildMcpServer(clientOrConfig, mode);
19364
+ }
19365
+ const client = new DimAgentClient(clientOrConfig);
19366
+ const server2 = buildMcpServer(client, mode);
19207
19367
  return { server: server2, client };
19208
19368
  }
19209
19369