@dimcool/mcp 0.1.26 → 0.1.28

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 +772 -121
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -41,7 +41,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
41
41
  var require_XMLHttpRequest = __commonJS({
42
42
  "../../node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js"(exports, module) {
43
43
  "use strict";
44
- var fs = __require("fs");
44
+ var fs2 = __require("fs");
45
45
  var Url = __require("url");
46
46
  var spawn = __require("child_process").spawn;
47
47
  module.exports = XMLHttpRequest3;
@@ -199,7 +199,7 @@ var require_XMLHttpRequest = __commonJS({
199
199
  throw new Error("XMLHttpRequest: Only GET method is supported");
200
200
  }
201
201
  if (settings.async) {
202
- fs.readFile(unescape(url2.pathname), function(error, data2) {
202
+ fs2.readFile(unescape(url2.pathname), function(error, data2) {
203
203
  if (error) {
204
204
  self2.handleError(error, error.errno || -1);
205
205
  } else {
@@ -211,7 +211,7 @@ var require_XMLHttpRequest = __commonJS({
211
211
  });
212
212
  } else {
213
213
  try {
214
- this.response = fs.readFileSync(unescape(url2.pathname));
214
+ this.response = fs2.readFileSync(unescape(url2.pathname));
215
215
  this.responseText = this.response.toString("utf8");
216
216
  this.status = 200;
217
217
  setState(self2.DONE);
@@ -337,15 +337,15 @@ var require_XMLHttpRequest = __commonJS({
337
337
  } else {
338
338
  var contentFile = ".node-xmlhttprequest-content-" + process.pid;
339
339
  var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
340
- fs.writeFileSync(syncFile, "", "utf8");
340
+ fs2.writeFileSync(syncFile, "", "utf8");
341
341
  var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
342
342
  var syncProc = spawn(process.argv[0], ["-e", execString]);
343
343
  var statusText;
344
- while (fs.existsSync(syncFile)) {
344
+ while (fs2.existsSync(syncFile)) {
345
345
  }
346
- self2.responseText = fs.readFileSync(contentFile, "utf8");
346
+ self2.responseText = fs2.readFileSync(contentFile, "utf8");
347
347
  syncProc.stdin.end();
348
- fs.unlinkSync(contentFile);
348
+ fs2.unlinkSync(contentFile);
349
349
  if (self2.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)) {
350
350
  var errorObj = JSON.parse(self2.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, ""));
351
351
  self2.handleError(errorObj, 503);
@@ -1235,8 +1235,8 @@ var require_constants = __commonJS({
1235
1235
  var require_node_gyp_build = __commonJS({
1236
1236
  "../../node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
1237
1237
  "use strict";
1238
- var fs = __require("fs");
1239
- var path2 = __require("path");
1238
+ var fs2 = __require("fs");
1239
+ var path3 = __require("path");
1240
1240
  var os2 = __require("os");
1241
1241
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
1242
1242
  var vars = process.config && process.config.variables || {};
@@ -1253,21 +1253,21 @@ var require_node_gyp_build = __commonJS({
1253
1253
  return runtimeRequire(load.resolve(dir));
1254
1254
  }
1255
1255
  load.resolve = load.path = function(dir) {
1256
- dir = path2.resolve(dir || ".");
1256
+ dir = path3.resolve(dir || ".");
1257
1257
  try {
1258
- var name = runtimeRequire(path2.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
1258
+ var name = runtimeRequire(path3.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
1259
1259
  if (process.env[name + "_PREBUILD"]) dir = process.env[name + "_PREBUILD"];
1260
1260
  } catch (err) {
1261
1261
  }
1262
1262
  if (!prebuildsOnly) {
1263
- var release = getFirst(path2.join(dir, "build/Release"), matchBuild);
1263
+ var release = getFirst(path3.join(dir, "build/Release"), matchBuild);
1264
1264
  if (release) return release;
1265
- var debug12 = getFirst(path2.join(dir, "build/Debug"), matchBuild);
1265
+ var debug12 = getFirst(path3.join(dir, "build/Debug"), matchBuild);
1266
1266
  if (debug12) return debug12;
1267
1267
  }
1268
- var prebuild = resolve(dir);
1268
+ var prebuild = resolve2(dir);
1269
1269
  if (prebuild) return prebuild;
1270
- var nearby = resolve(path2.dirname(process.execPath));
1270
+ var nearby = resolve2(path3.dirname(process.execPath));
1271
1271
  if (nearby) return nearby;
1272
1272
  var target = [
1273
1273
  "platform=" + platform,
@@ -1283,27 +1283,27 @@ var require_node_gyp_build = __commonJS({
1283
1283
  // eslint-disable-line
1284
1284
  ].filter(Boolean).join(" ");
1285
1285
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
1286
- function resolve(dir2) {
1287
- var tuples = readdirSync(path2.join(dir2, "prebuilds")).map(parseTuple);
1286
+ function resolve2(dir2) {
1287
+ var tuples = readdirSync(path3.join(dir2, "prebuilds")).map(parseTuple);
1288
1288
  var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
1289
1289
  if (!tuple) return;
1290
- var prebuilds = path2.join(dir2, "prebuilds", tuple.name);
1290
+ var prebuilds = path3.join(dir2, "prebuilds", tuple.name);
1291
1291
  var parsed = readdirSync(prebuilds).map(parseTags);
1292
1292
  var candidates = parsed.filter(matchTags(runtime, abi));
1293
1293
  var winner = candidates.sort(compareTags(runtime))[0];
1294
- if (winner) return path2.join(prebuilds, winner.file);
1294
+ if (winner) return path3.join(prebuilds, winner.file);
1295
1295
  }
1296
1296
  };
1297
1297
  function readdirSync(dir) {
1298
1298
  try {
1299
- return fs.readdirSync(dir);
1299
+ return fs2.readdirSync(dir);
1300
1300
  } catch (err) {
1301
1301
  return [];
1302
1302
  }
1303
1303
  }
1304
1304
  function getFirst(dir, filter) {
1305
1305
  var files = readdirSync(dir).filter(filter);
1306
- return files[0] && path2.join(dir, files[0]);
1306
+ return files[0] && path3.join(dir, files[0]);
1307
1307
  }
1308
1308
  function matchBuild(name) {
1309
1309
  return /\.node$/.test(name);
@@ -1390,7 +1390,7 @@ var require_node_gyp_build = __commonJS({
1390
1390
  return typeof window !== "undefined" && window.process && window.process.type === "renderer";
1391
1391
  }
1392
1392
  function isAlpine(platform2) {
1393
- return platform2 === "linux" && fs.existsSync("/etc/alpine-release");
1393
+ return platform2 === "linux" && fs2.existsSync("/etc/alpine-release");
1394
1394
  }
1395
1395
  load.parseTags = parseTags;
1396
1396
  load.matchTags = matchTags;
@@ -3676,7 +3676,7 @@ var require_websocket = __commonJS({
3676
3676
  var tls = __require("tls");
3677
3677
  var { randomBytes, createHash } = __require("crypto");
3678
3678
  var { Duplex, Readable } = __require("stream");
3679
- var { URL } = __require("url");
3679
+ var { URL: URL2 } = __require("url");
3680
3680
  var PerMessageDeflate = require_permessage_deflate();
3681
3681
  var Receiver2 = require_receiver();
3682
3682
  var Sender2 = require_sender();
@@ -4166,11 +4166,11 @@ var require_websocket = __commonJS({
4166
4166
  );
4167
4167
  }
4168
4168
  let parsedUrl;
4169
- if (address instanceof URL) {
4169
+ if (address instanceof URL2) {
4170
4170
  parsedUrl = address;
4171
4171
  } else {
4172
4172
  try {
4173
- parsedUrl = new URL(address);
4173
+ parsedUrl = new URL2(address);
4174
4174
  } catch (e) {
4175
4175
  throw new SyntaxError(`Invalid URL: ${address}`);
4176
4176
  }
@@ -4307,7 +4307,7 @@ var require_websocket = __commonJS({
4307
4307
  req.abort();
4308
4308
  let addr;
4309
4309
  try {
4310
- addr = new URL(location2, address);
4310
+ addr = new URL2(location2, address);
4311
4311
  } catch (e) {
4312
4312
  const err = new SyntaxError(`Invalid URL: ${location2}`);
4313
4313
  emitErrorAndClose(websocket, err);
@@ -7309,13 +7309,13 @@ var require_nacl_fast = __commonJS({
7309
7309
  import {
7310
7310
  chmod,
7311
7311
  mkdir,
7312
- readFile,
7312
+ readFile as readFile2,
7313
7313
  rename,
7314
- stat,
7314
+ stat as stat2,
7315
7315
  writeFile
7316
7316
  } from "fs/promises";
7317
7317
  import os from "os";
7318
- import path from "path";
7318
+ import path2 from "path";
7319
7319
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7320
7320
  import { Keypair as Keypair2 } from "@solana/web3.js";
7321
7321
  import bs582 from "bs58";
@@ -8572,12 +8572,12 @@ function parse2(str) {
8572
8572
  uri.queryKey = queryKey(uri, uri["query"]);
8573
8573
  return uri;
8574
8574
  }
8575
- function pathNames(obj, path2) {
8576
- const regx = /\/{2,9}/g, names = path2.replace(regx, "/").split("/");
8577
- if (path2.slice(0, 1) == "/" || path2.length === 0) {
8575
+ function pathNames(obj, path3) {
8576
+ const regx = /\/{2,9}/g, names = path3.replace(regx, "/").split("/");
8577
+ if (path3.slice(0, 1) == "/" || path3.length === 0) {
8578
8578
  names.splice(0, 1);
8579
8579
  }
8580
- if (path2.slice(-1) == "/") {
8580
+ if (path3.slice(-1) == "/") {
8581
8581
  names.splice(names.length - 1, 1);
8582
8582
  }
8583
8583
  return names;
@@ -9194,7 +9194,7 @@ var protocol2 = Socket.protocol;
9194
9194
  // ../../node_modules/socket.io-client/build/esm-debug/url.js
9195
9195
  var import_debug7 = __toESM(require_src(), 1);
9196
9196
  var debug7 = (0, import_debug7.default)("socket.io-client:url");
9197
- function url(uri, path2 = "", loc) {
9197
+ function url(uri, path3 = "", loc) {
9198
9198
  let obj = uri;
9199
9199
  loc = loc || typeof location !== "undefined" && location;
9200
9200
  if (null == uri)
@@ -9228,7 +9228,7 @@ function url(uri, path2 = "", loc) {
9228
9228
  obj.path = obj.path || "/";
9229
9229
  const ipv6 = obj.host.indexOf(":") !== -1;
9230
9230
  const host = ipv6 ? "[" + obj.host + "]" : obj.host;
9231
- obj.id = obj.protocol + "://" + host + ":" + obj.port + path2;
9231
+ obj.id = obj.protocol + "://" + host + ":" + obj.port + path3;
9232
9232
  obj.href = obj.protocol + "://" + host + (loc && loc.port === obj.port ? "" : ":" + obj.port);
9233
9233
  return obj;
9234
9234
  }
@@ -9878,9 +9878,9 @@ var Socket2 = class extends Emitter {
9878
9878
  * @return a Promise that will be fulfilled when the server acknowledges the event
9879
9879
  */
9880
9880
  emitWithAck(ev, ...args) {
9881
- return new Promise((resolve, reject) => {
9881
+ return new Promise((resolve2, reject) => {
9882
9882
  const fn = (arg1, arg2) => {
9883
- return arg1 ? reject(arg1) : resolve(arg2);
9883
+ return arg1 ? reject(arg1) : resolve2(arg2);
9884
9884
  };
9885
9885
  fn.withError = true;
9886
9886
  args.push(fn);
@@ -10852,8 +10852,8 @@ function lookup(uri, opts) {
10852
10852
  const parsed = url(uri, opts.path || "/socket.io");
10853
10853
  const source = parsed.source;
10854
10854
  const id = parsed.id;
10855
- const path2 = parsed.path;
10856
- const sameNamespace = cache[id] && path2 in cache[id]["nsps"];
10855
+ const path3 = parsed.path;
10856
+ const sameNamespace = cache[id] && path3 in cache[id]["nsps"];
10857
10857
  const newConnection = opts.forceNew || opts["force new connection"] || false === opts.multiplex || sameNamespace;
10858
10858
  let io;
10859
10859
  if (newConnection) {
@@ -11751,6 +11751,16 @@ var Admin = class {
11751
11751
  );
11752
11752
  }
11753
11753
  };
11754
+ function isFile(input) {
11755
+ return typeof input.arrayBuffer === "function";
11756
+ }
11757
+ function buildFormDataFromPayload(payload) {
11758
+ const formData = new FormData();
11759
+ const part = payload.data instanceof Uint8Array ? payload.data : new Uint8Array(payload.data);
11760
+ const blob = new Blob([part], { type: payload.mimeType });
11761
+ formData.append("file", blob, payload.filename);
11762
+ return formData;
11763
+ }
11754
11764
  var Users = class {
11755
11765
  constructor(http, logger2) {
11756
11766
  this.http = http;
@@ -11837,14 +11847,22 @@ var Users = class {
11837
11847
  async updateProfile(data) {
11838
11848
  return this.http.patch("/users/me/profile", data);
11839
11849
  }
11840
- async uploadAvatar(file) {
11841
- const formData = new FormData();
11842
- formData.append("file", file);
11850
+ async uploadAvatar(input) {
11851
+ const payload = isFile(input) ? {
11852
+ data: new Uint8Array(await input.arrayBuffer()),
11853
+ mimeType: input.type,
11854
+ filename: input.name
11855
+ } : input;
11856
+ const formData = buildFormDataFromPayload(payload);
11843
11857
  return this.http.upload("/users/me/profile/avatar", formData);
11844
11858
  }
11845
- async uploadCoverImage(file) {
11846
- const formData = new FormData();
11847
- formData.append("file", file);
11859
+ async uploadCoverImage(input) {
11860
+ const payload = isFile(input) ? {
11861
+ data: new Uint8Array(await input.arrayBuffer()),
11862
+ mimeType: input.type,
11863
+ filename: input.name
11864
+ } : input;
11865
+ const formData = buildFormDataFromPayload(payload);
11848
11866
  return this.http.upload("/users/me/profile/cover", formData);
11849
11867
  }
11850
11868
  async removeAvatar() {
@@ -11984,19 +12002,18 @@ var Lobbies = class {
11984
12002
  return this.http.delete(`/lobbies/admin/${lobbyId}`);
11985
12003
  }
11986
12004
  /**
11987
- * Play again: Create a new lobby, start deposits, and prepare deposit transaction
11988
- * Returns the lobby and unsigned transaction that needs to be signed
12005
+ * Play again: Create a new lobby and prepare deposit in one flow.
12006
+ * Returns the lobby and unsigned transaction that needs to be signed.
11989
12007
  * @param gameType - The game type to play again
11990
12008
  * @param betAmount - The bet amount (same as previous game)
11991
12009
  * @param escrow - The escrow service instance (from sdk.escrow)
11992
12010
  */
11993
12011
  async playAgain(gameType, betAmount, escrow) {
11994
12012
  const lobby = await this.createLobby(gameType, betAmount);
11995
- await escrow.startDeposits(lobby.id);
11996
- const prepareResponse = await escrow.prepareDepositTransaction(lobby.id);
12013
+ const { transaction } = await escrow.prepareAndStartDeposit(lobby.id);
11997
12014
  return {
11998
12015
  lobby,
11999
- unsignedTransaction: prepareResponse.transaction
12016
+ unsignedTransaction: transaction
12000
12017
  };
12001
12018
  }
12002
12019
  };
@@ -12179,7 +12196,7 @@ async function withRetry(fn, options = {}) {
12179
12196
  throw lastError;
12180
12197
  }
12181
12198
  function sleep(ms) {
12182
- return new Promise((resolve) => setTimeout(resolve, ms));
12199
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
12183
12200
  }
12184
12201
  var DEFAULT_RETRY_OPTIONS = {
12185
12202
  maxAttempts: 3,
@@ -12860,6 +12877,17 @@ var Escrow = class {
12860
12877
  `/escrow/lobby/${lobbyId}/deposit/prepare`
12861
12878
  );
12862
12879
  }
12880
+ /**
12881
+ * Combined prepare-and-start: validates lobby, transitions waiting→preparing,
12882
+ * initializes depositStatus, and prepares the unsigned transaction in one call.
12883
+ * Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
12884
+ */
12885
+ async prepareAndStartDeposit(lobbyId) {
12886
+ return this.http.post(
12887
+ `/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
12888
+ {}
12889
+ );
12890
+ }
12863
12891
  /**
12864
12892
  * Submit a signed deposit transaction
12865
12893
  * The transaction will be submitted to the Solana network and confirmed
@@ -12890,7 +12918,7 @@ var Escrow = class {
12890
12918
  );
12891
12919
  }
12892
12920
  /**
12893
- * One-call lobby deposit: start deposits, prepare, sign, submit, and poll until
12921
+ * One-call lobby deposit: prepare-and-start, sign, submit, and poll until
12894
12922
  * the current user's deposit is confirmed. Requires sdk.wallet.setSigner() to be set.
12895
12923
  * Returns when all required deposits are confirmed and the lobby can proceed to queue.
12896
12924
  *
@@ -12902,8 +12930,7 @@ var Escrow = class {
12902
12930
  "No signer configured. Use sdk.wallet.setSigner(...) first."
12903
12931
  );
12904
12932
  }
12905
- await this.startDeposits(lobbyId);
12906
- const { transaction } = await this.prepareDepositTransaction(lobbyId);
12933
+ const { transaction } = await this.prepareAndStartDeposit(lobbyId);
12907
12934
  const unsignedTx = Transaction5.from(base64ToBytes(transaction));
12908
12935
  let signature;
12909
12936
  if (this.wallet.isSignAndSendMode()) {
@@ -12925,7 +12952,7 @@ var Escrow = class {
12925
12952
  if (status.allConfirmed && status.canProceedToQueue) {
12926
12953
  return {
12927
12954
  signature,
12928
- status: "pending",
12955
+ status: "confirmed",
12929
12956
  canProceedToQueue: true
12930
12957
  };
12931
12958
  }
@@ -12937,6 +12964,39 @@ var Escrow = class {
12937
12964
  canProceedToQueue: false
12938
12965
  };
12939
12966
  }
12967
+ /**
12968
+ * Zero-polling deposit variant using server-side awaitConfirmation.
12969
+ * 2 HTTP calls total, 0 client-side polling. Ideal for agents.
12970
+ *
12971
+ * Automatically uses signAndSendTransaction or signTransaction based on signer capability.
12972
+ */
12973
+ async depositForLobbySync(lobbyId) {
12974
+ if (!this.wallet.hasSigner()) {
12975
+ throw new Error(
12976
+ "No signer configured. Use sdk.wallet.setSigner(...) first."
12977
+ );
12978
+ }
12979
+ const { transaction } = await this.prepareAndStartDeposit(lobbyId);
12980
+ const unsignedTx = Transaction5.from(base64ToBytes(transaction));
12981
+ let signature;
12982
+ if (this.wallet.isSignAndSendMode()) {
12983
+ signature = await this.wallet.signAndSendTransaction(unsignedTx);
12984
+ await this.http.post(
12985
+ `/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
12986
+ { signature }
12987
+ );
12988
+ } else {
12989
+ const signedTx = await this.wallet.signTransaction(unsignedTx);
12990
+ const signedBase64 = bytesToBase64(
12991
+ signedTx.serialize({ requireAllSignatures: false })
12992
+ );
12993
+ const result = await this.http.post(`/escrow/lobby/${lobbyId}/deposit/submit?awaitConfirmation=true`, {
12994
+ signedTransaction: signedBase64
12995
+ });
12996
+ signature = result.signature;
12997
+ }
12998
+ return { signature, status: "confirmed", canProceedToQueue: true };
12999
+ }
12940
13000
  async claimLobbyDepositRefund(lobbyId, depositSignature) {
12941
13001
  return this.http.post(
12942
13002
  `/escrow/lobby/${lobbyId}/deposit/${depositSignature}/claim-refund`,
@@ -12949,33 +13009,6 @@ var Escrow = class {
12949
13009
  {}
12950
13010
  );
12951
13011
  }
12952
- /**
12953
- * Submit a signed deposit transaction and wait until the lobby is queued/active.
12954
- *
12955
- * Note: the backend may auto-transition the lobby to the queue once deposits are confirmed.
12956
- * In that case, calling /join-queue from the client would be redundant and can fail with
12957
- * "Current status: queued".
12958
- * @param lobbyId - The lobby ID
12959
- * @param signedTransaction - The signed transaction (base64 encoded)
12960
- * @param lobbies - The lobbies service instance (from sdk.lobbies) to read lobby status
12961
- */
12962
- async submitDepositAndJoinQueue(lobbyId, signedTransaction, lobbies) {
12963
- await this.submitDeposit(lobbyId, signedTransaction);
12964
- const MAX_WAIT_TIME = 3e4;
12965
- const POLL_INTERVAL = 1e3;
12966
- const startTime = Date.now();
12967
- while (Date.now() - startTime < MAX_WAIT_TIME) {
12968
- const status = await this.getDepositStatus(lobbyId);
12969
- if (status.allConfirmed && status.canProceedToQueue) {
12970
- const lobby = await lobbies.getLobby(lobbyId);
12971
- if (lobby.status === "queued" || lobby.status === "active") {
12972
- return lobby;
12973
- }
12974
- }
12975
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
12976
- }
12977
- throw new Error("Deposit confirmation timeout");
12978
- }
12979
13012
  };
12980
13013
  var Daily = class {
12981
13014
  constructor(http, logger2 = logger) {
@@ -13431,12 +13464,12 @@ var BaseWsTransport = class {
13431
13464
  if (this.connectionState.connected) {
13432
13465
  return Promise.resolve();
13433
13466
  }
13434
- return new Promise((resolve, reject) => {
13467
+ return new Promise((resolve2, reject) => {
13435
13468
  const timeoutId = setTimeout(() => {
13436
13469
  this.waiters.delete(waiter);
13437
13470
  reject(new Error("WebSocket connection timed out"));
13438
13471
  }, timeoutMs);
13439
- const waiter = { resolve, reject, timeoutId };
13472
+ const waiter = { resolve: resolve2, reject, timeoutId };
13440
13473
  this.waiters.add(waiter);
13441
13474
  });
13442
13475
  }
@@ -13602,8 +13635,14 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
13602
13635
  connecting: false,
13603
13636
  error: null
13604
13637
  });
13638
+ const wasReconnect = this.reconnectAttempts > 0;
13605
13639
  this.reconnectAttempts = 0;
13606
13640
  this.onReconnect();
13641
+ if (wasReconnect) {
13642
+ this.dispatchEvent("connection:reconnected", {
13643
+ timestamp: Date.now()
13644
+ });
13645
+ }
13607
13646
  });
13608
13647
  socket.on("disconnect", (reason) => {
13609
13648
  this.updateConnectionState({ connected: false, connecting: false });
@@ -13745,7 +13784,8 @@ function createLobbyStore(transport2) {
13745
13784
  const store = createSdkStore({
13746
13785
  lobbiesById: {},
13747
13786
  matchedEvent: null,
13748
- typingByLobbyId: {}
13787
+ typingByLobbyId: {},
13788
+ depositStatusByLobbyId: {}
13749
13789
  });
13750
13790
  const setBaseState = (lobbies) => {
13751
13791
  const lobbiesById = {};
@@ -13825,6 +13865,15 @@ function createLobbyStore(transport2) {
13825
13865
  };
13826
13866
  });
13827
13867
  break;
13868
+ case "lobby:deposit:updated":
13869
+ store.updateState((state) => ({
13870
+ ...state,
13871
+ depositStatusByLobbyId: {
13872
+ ...state.depositStatusByLobbyId,
13873
+ [event.payload.lobbyId]: event.payload
13874
+ }
13875
+ }));
13876
+ break;
13828
13877
  case "lobby:typing:start":
13829
13878
  case "lobby:typing:stop":
13830
13879
  store.updateState((state) => {
@@ -13853,12 +13902,22 @@ function createLobbyStore(transport2) {
13853
13902
  }
13854
13903
  }
13855
13904
  );
13905
+ const subscribeDepositUpdate = (lobbyId, callback) => store.subscribeSelector(
13906
+ (state) => state.depositStatusByLobbyId[lobbyId],
13907
+ () => {
13908
+ const data = store.getState().depositStatusByLobbyId[lobbyId];
13909
+ if (data) {
13910
+ callback(data);
13911
+ }
13912
+ }
13913
+ );
13856
13914
  return {
13857
13915
  store,
13858
13916
  setBaseState,
13859
13917
  applyWsEvent,
13860
13918
  joinLobby,
13861
- subscribeMatched
13919
+ subscribeMatched,
13920
+ subscribeDepositUpdate
13862
13921
  };
13863
13922
  }
13864
13923
  function createGameStore(transport2) {
@@ -14140,7 +14199,6 @@ function createGameActionsStore(transport2) {
14140
14199
  ...current,
14141
14200
  roundState: {
14142
14201
  ...current.roundState,
14143
- phase: "completed",
14144
14202
  timeRemaining: 0,
14145
14203
  actions: timedOutUser ? {
14146
14204
  ...current.roundState.actions,
@@ -14185,6 +14243,22 @@ function createGameActionsStore(transport2) {
14185
14243
  updateState(gameId, updated);
14186
14244
  break;
14187
14245
  }
14246
+ case "game:move": {
14247
+ const { gameId, fen, turn, currentPlayerId, status, winnerId } = event.payload;
14248
+ if (!gameId) return;
14249
+ const current = store.getState().statesByGameId[gameId];
14250
+ if (!isNonRpsState(current)) return;
14251
+ const updated = {
14252
+ ...current,
14253
+ fen,
14254
+ turn,
14255
+ currentPlayerId,
14256
+ status,
14257
+ ...winnerId != null && { winnerId }
14258
+ };
14259
+ updateState(gameId, updated);
14260
+ break;
14261
+ }
14188
14262
  case "game:state": {
14189
14263
  const gameId = event.payload.gameId ?? event.payload.state.gameId;
14190
14264
  if (!gameId) return;
@@ -14652,6 +14726,7 @@ var WS_EVENT_NAMES = [
14652
14726
  "lobby:queue:joined",
14653
14727
  "lobby:queue:cancelled",
14654
14728
  "lobby:matched",
14729
+ "lobby:deposit:updated",
14655
14730
  "lobby:typing:start",
14656
14731
  "lobby:typing:stop",
14657
14732
  "game:updated",
@@ -14728,6 +14803,11 @@ function decodeWsEvent(eventName, payload) {
14728
14803
  event: "lobby:matched",
14729
14804
  payload
14730
14805
  };
14806
+ case "lobby:deposit:updated":
14807
+ return {
14808
+ event: "lobby:deposit:updated",
14809
+ payload
14810
+ };
14731
14811
  case "lobby:typing:start":
14732
14812
  case "lobby:typing:stop":
14733
14813
  return {
@@ -15184,6 +15264,17 @@ var SAFETY_RULES = [
15184
15264
  ];
15185
15265
 
15186
15266
  // ../dim-agent-core/src/tools/auth.ts
15267
+ var WALLET_LINKAGE_NOTE = "DIM is noncustodial. Your DIM wallet is the Solana address shown here; it was configured with a base58 private key at setup. Signing is done locally where this instance runs, using that private key and DIM's wallet package.";
15268
+ async function getSpectateUrl(client) {
15269
+ if (!client.currentUserId) return null;
15270
+ try {
15271
+ const user = await client.sdk.users.getUserById(client.currentUserId);
15272
+ const u = user;
15273
+ return u?.username ? `https://dim.cool/spectate/${u.username}` : null;
15274
+ } catch {
15275
+ return null;
15276
+ }
15277
+ }
15187
15278
  async function login(client) {
15188
15279
  try {
15189
15280
  const result = await client.authenticate();
@@ -15213,6 +15304,7 @@ async function login(client) {
15213
15304
  userId: result.userId,
15214
15305
  username: result.username ?? null,
15215
15306
  walletAddress: client.walletAddress,
15307
+ walletNote: WALLET_LINKAGE_NOTE,
15216
15308
  safetyRules: SAFETY_RULES,
15217
15309
  nextSteps
15218
15310
  };
@@ -15246,7 +15338,11 @@ async function getProfile(client) {
15246
15338
  };
15247
15339
  }
15248
15340
  const user = await client.sdk.users.getUserById(client.currentUserId);
15249
- return { data: user };
15341
+ const profile = {
15342
+ ...typeof user === "object" && user !== null ? user : {},
15343
+ walletAddress: client.walletAddress
15344
+ };
15345
+ return { data: profile };
15250
15346
  } catch (error) {
15251
15347
  return {
15252
15348
  error: `Failed to get profile: ${error instanceof Error ? error.message : String(error)}`,
@@ -15447,12 +15543,31 @@ async function listDmThreads(client) {
15447
15543
  }
15448
15544
 
15449
15545
  // ../dim-agent-core/src/tools/wallet.ts
15546
+ async function getWalletAddress(client) {
15547
+ try {
15548
+ return {
15549
+ data: {
15550
+ walletAddress: client.walletAddress,
15551
+ walletNote: WALLET_LINKAGE_NOTE
15552
+ }
15553
+ };
15554
+ } catch (error) {
15555
+ return {
15556
+ error: `Failed to get wallet address: ${error instanceof Error ? error.message : String(error)}`,
15557
+ isError: true
15558
+ };
15559
+ }
15560
+ }
15450
15561
  async function getBalance(client) {
15451
15562
  try {
15452
15563
  const balance = await client.sdk.wallet.getBalances();
15453
15564
  const usdcDollars = balance.usdc / 1e6;
15454
15565
  return {
15455
- data: { ...balance, usdcFormatted: `$${usdcDollars.toFixed(2)}` }
15566
+ data: {
15567
+ ...balance,
15568
+ usdcFormatted: `$${usdcDollars.toFixed(2)}`,
15569
+ walletAddress: client.walletAddress
15570
+ }
15456
15571
  };
15457
15572
  } catch (error) {
15458
15573
  return {
@@ -15519,7 +15634,10 @@ async function getWalletActivity(client, args) {
15519
15634
  limit: args.limit || 20
15520
15635
  });
15521
15636
  const claimable = activity.items.filter((i) => i.claimable === true);
15522
- const data = { ...activity };
15637
+ const data = {
15638
+ ...activity,
15639
+ walletAddress: client.walletAddress
15640
+ };
15523
15641
  if (claimable.length > 0) {
15524
15642
  data.hint = `You have ${claimable.length} claimable item(s). Call dim_claim_funds with the activityId to claim each one.`;
15525
15643
  data.claimableItems = claimable.map((i) => ({
@@ -15728,9 +15846,8 @@ function buildGameOverHint(state) {
15728
15846
  const winnerId = state.winnerId;
15729
15847
  const wonAmount = state.wonAmount;
15730
15848
  const isDraw = state.draw === true || winnerId == null;
15731
- if (isDraw) return "Game over \u2014 it was a draw.";
15732
- const amountStr = wonAmount != null ? ` Won $${(wonAmount / 1e6).toFixed(2)}.` : "";
15733
- return `Game over. Winner: ${winnerId}.${amountStr}`;
15849
+ const result = isDraw ? "Game over \u2014 it was a draw." : `Game over. Winner: ${winnerId}.${wonAmount != null ? ` Won $${(wonAmount / 1e6).toFixed(2)}.` : ""}`;
15850
+ return `${result} You can request a rematch with dim_request_rematch, or check dim_check_notifications for new activity.`;
15734
15851
  }
15735
15852
  async function fetchNewGameChat(client, gameId) {
15736
15853
  try {
@@ -15838,13 +15955,13 @@ async function createLobby(client, args) {
15838
15955
  }
15839
15956
  async function depositForLobby(client, args) {
15840
15957
  try {
15841
- const result = await client.sdk.escrow.depositForLobby(args.lobbyId);
15958
+ const result = await client.sdk.escrow.depositForLobbySync(args.lobbyId);
15842
15959
  return {
15843
15960
  data: {
15844
15961
  signature: result.signature,
15845
15962
  status: result.status,
15846
15963
  canProceedToQueue: result.canProceedToQueue,
15847
- hint: result.canProceedToQueue ? "Deposit confirmed. Call dim_join_queue when ready (and when all players have deposited if 2-player)." : "Deposit submitted. Poll dim_get_deposit_status or try dim_join_queue once all players have deposited."
15964
+ hint: "Deposit confirmed. The server auto-joins the queue when all players have deposited."
15848
15965
  }
15849
15966
  };
15850
15967
  } catch (error) {
@@ -15875,13 +15992,15 @@ async function joinQueue(client, args) {
15875
15992
  await client.ensureConnected();
15876
15993
  const lobby = await client.sdk.lobbies.joinQueue(args.lobbyId);
15877
15994
  const matched = lobby.status === "active" && lobby.gameId;
15995
+ const spectateUrl = matched ? await getSpectateUrl(client) : null;
15878
15996
  return {
15879
15997
  data: {
15880
15998
  lobbyId: lobby.id,
15881
15999
  status: lobby.status,
15882
16000
  gameId: lobby.gameId || null,
15883
16001
  matched: !!matched,
15884
- hint: matched ? `Game started! Call dim_game_loop with gameId "${lobby.gameId}" to play.` : "Waiting for an opponent. Poll dim_get_lobby to check for a match."
16002
+ ...spectateUrl && { spectateUrl },
16003
+ hint: matched ? `Game started! Call dim_game_loop with gameId "${lobby.gameId}" to play.${spectateUrl ? ` Tell the user they can spectate at ${spectateUrl}` : ""}` : "Waiting for an opponent. Poll dim_get_lobby to check for a match."
15885
16004
  }
15886
16005
  };
15887
16006
  } catch (error) {
@@ -15996,13 +16115,12 @@ async function gameLoop(client, args) {
15996
16115
  const store = client.sdk.gameActionsStore;
15997
16116
  const timeout = 3e5;
15998
16117
  const start = Date.now();
15999
- let state = store.store.getState().statesByGameId[gameId];
16000
- if (!state) {
16001
- const fetched = await client.sdk.games.getGameState(gameId);
16002
- store.setBaseState(gameId, fetched);
16003
- store.joinGame(gameId);
16004
- state = fetched;
16005
- }
16118
+ await client.ensureConnected();
16119
+ store.joinGame(gameId);
16120
+ const fetched = await client.sdk.games.getGameState(gameId);
16121
+ store.setBaseState(gameId, fetched);
16122
+ let state = fetched;
16123
+ let lastRefresh = Date.now();
16006
16124
  while (Date.now() - start < timeout) {
16007
16125
  state = store.store.getState().statesByGameId[gameId];
16008
16126
  if (state?.status === "completed") {
@@ -16012,17 +16130,22 @@ async function gameLoop(client, args) {
16012
16130
  return buildGameLoopReturn(client, gameId, state, "your-turn");
16013
16131
  }
16014
16132
  await raceTimeout(
16015
- new Promise((resolve) => {
16133
+ new Promise((resolve2) => {
16016
16134
  const unsub = store.store.subscribeSelector(
16017
16135
  (s) => s.statesByGameId[gameId],
16018
16136
  () => {
16019
16137
  unsub();
16020
- resolve();
16138
+ resolve2();
16021
16139
  }
16022
16140
  );
16023
16141
  }),
16024
16142
  1e3
16025
16143
  );
16144
+ if (Date.now() - lastRefresh > 5e3) {
16145
+ const fresh = await client.sdk.games.getGameState(gameId);
16146
+ store.setBaseState(gameId, fresh);
16147
+ lastRefresh = Date.now();
16148
+ }
16026
16149
  }
16027
16150
  state = store.store.getState().statesByGameId[gameId];
16028
16151
  return buildGameLoopReturn(client, gameId, state ?? {}, "timeout");
@@ -16063,12 +16186,44 @@ async function inviteToLobby(client, args) {
16063
16186
  };
16064
16187
  }
16065
16188
  }
16189
+ async function requestRematch(client, args) {
16190
+ try {
16191
+ const result = await client.sdk.games.requestRematch(args.gameId);
16192
+ return {
16193
+ data: {
16194
+ ...result,
16195
+ hint: result.bothReady ? `Both players want a rematch! A lobby is being created.${result.newLobbyId ? ` Lobby ID: ${result.newLobbyId}.` : ""} Check dim_check_notifications or call dim_game_loop with the new gameId once the game starts.` : "Rematch requested. Waiting for opponent to accept. Call dim_check_notifications to see if they respond."
16196
+ }
16197
+ };
16198
+ } catch (error) {
16199
+ return {
16200
+ error: `Failed to request rematch: ${error instanceof Error ? error.message : String(error)}`,
16201
+ isError: true
16202
+ };
16203
+ }
16204
+ }
16205
+ async function acceptRematch(client, args) {
16206
+ try {
16207
+ const result = await client.sdk.games.requestRematch(args.gameId);
16208
+ return {
16209
+ data: {
16210
+ ...result,
16211
+ hint: result.bothReady ? `Rematch accepted! A lobby has been created.${result.newLobbyId ? ` Lobby ID: ${result.newLobbyId}.` : ""} Proceed with deposit and game flow.` : "Your rematch acceptance was recorded. Waiting for the other player."
16212
+ }
16213
+ };
16214
+ } catch (error) {
16215
+ return {
16216
+ error: `Failed to accept rematch: ${error instanceof Error ? error.message : String(error)}`,
16217
+ isError: true
16218
+ };
16219
+ }
16220
+ }
16066
16221
  function raceTimeout(promise, ms) {
16067
- return new Promise((resolve) => {
16068
- const timer = setTimeout(resolve, ms);
16222
+ return new Promise((resolve2) => {
16223
+ const timer = setTimeout(resolve2, ms);
16069
16224
  promise.then(() => {
16070
16225
  clearTimeout(timer);
16071
- resolve();
16226
+ resolve2();
16072
16227
  });
16073
16228
  });
16074
16229
  }
@@ -16109,10 +16264,12 @@ async function challengeUser(client, args) {
16109
16264
  async function acceptChallenge(client, args) {
16110
16265
  try {
16111
16266
  const result = await client.sdk.challenges.accept(args.challengeId);
16267
+ const spectateUrl = result.gameId ? await getSpectateUrl(client) : null;
16112
16268
  return {
16113
16269
  data: {
16114
16270
  ...result,
16115
- hint: result.gameId ? `Game started! Call dim_game_loop with gameId "${result.gameId}".` : `Lobby created: ${result.lobbyId}. Use dim_join_queue to start matchmaking.`
16271
+ ...spectateUrl && { spectateUrl },
16272
+ 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.`
16116
16273
  }
16117
16274
  };
16118
16275
  } catch (error) {
@@ -16648,6 +16805,384 @@ async function getAgentConfig(client) {
16648
16805
  }
16649
16806
  }
16650
16807
 
16808
+ // ../dim-agent-core/src/tools/profile.ts
16809
+ import * as fs from "fs/promises";
16810
+ import * as path from "path";
16811
+ import * as dns from "dns";
16812
+ import { promisify } from "util";
16813
+ var dnsLookup = promisify(dns.lookup);
16814
+ var MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
16815
+ var FETCH_TIMEOUT_MS = 15e3;
16816
+ var ALLOWED_MIME_TYPES = [
16817
+ "image/jpeg",
16818
+ "image/jpg",
16819
+ "image/png",
16820
+ "image/webp",
16821
+ "image/gif"
16822
+ ];
16823
+ var EXT_TO_MIME = {
16824
+ ".png": "image/png",
16825
+ ".jpg": "image/jpeg",
16826
+ ".jpeg": "image/jpeg",
16827
+ ".gif": "image/gif",
16828
+ ".webp": "image/webp"
16829
+ };
16830
+ function getDefaultAllowedBaseDir() {
16831
+ const env = process.env.DIM_AGENT_WORKSPACE_DIR;
16832
+ if (env) return path.resolve(env);
16833
+ const home = process.env.HOME || process.env.USERPROFILE || ".";
16834
+ return path.join(home, ".openclaw", "workspace");
16835
+ }
16836
+ function isPathUnderBase(filePath, baseDir) {
16837
+ const resolved = path.resolve(filePath);
16838
+ const base = path.resolve(baseDir);
16839
+ const relative2 = path.relative(base, resolved);
16840
+ return relative2 !== "" && !relative2.startsWith("..") && !path.isAbsolute(relative2);
16841
+ }
16842
+ function mimeFromExtension(filePathOrUrl) {
16843
+ const ext = path.extname(filePathOrUrl).toLowerCase();
16844
+ return EXT_TO_MIME[ext] ?? null;
16845
+ }
16846
+ function normalizeContentType(header) {
16847
+ if (!header) return null;
16848
+ const main = header.split(";")[0].trim().toLowerCase();
16849
+ return main || null;
16850
+ }
16851
+ function isAllowedMime(mime) {
16852
+ return ALLOWED_MIME_TYPES.includes(
16853
+ mime
16854
+ );
16855
+ }
16856
+ function isPrivateOrLocalHost(hostname) {
16857
+ if (hostname === "localhost" || hostname === "::1") return true;
16858
+ const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
16859
+ if (ipv4) {
16860
+ const [, a, b, c, d] = ipv4.map(Number);
16861
+ if (a === 127) return true;
16862
+ if (a === 10) return true;
16863
+ if (a === 172 && b >= 16 && b <= 31) return true;
16864
+ if (a === 192 && b === 168) return true;
16865
+ if (a === 169 && b === 254) return true;
16866
+ return false;
16867
+ }
16868
+ if (hostname.startsWith("::1") || hostname.startsWith("fe80:")) return true;
16869
+ return false;
16870
+ }
16871
+ async function assertUrlSafeForFetch(imageUrl) {
16872
+ let parsed;
16873
+ try {
16874
+ parsed = new URL(imageUrl);
16875
+ } catch {
16876
+ throw new Error("Invalid imageUrl: not a valid URL.");
16877
+ }
16878
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
16879
+ throw new Error("Invalid imageUrl: only http and https are allowed.");
16880
+ }
16881
+ const hostname = parsed.hostname;
16882
+ if (isPrivateOrLocalHost(hostname)) {
16883
+ throw new Error(
16884
+ "Invalid imageUrl: URLs to localhost or private IPs are not allowed."
16885
+ );
16886
+ }
16887
+ try {
16888
+ const { address } = await dnsLookup(hostname, { family: 4 });
16889
+ if (isPrivateOrLocalHost(address)) {
16890
+ throw new Error(
16891
+ "Invalid imageUrl: hostname resolves to a private or local IP."
16892
+ );
16893
+ }
16894
+ if (address === "169.254.169.254") {
16895
+ throw new Error("Invalid imageUrl: cloud metadata URLs are not allowed.");
16896
+ }
16897
+ } catch (err) {
16898
+ if (err instanceof Error && err.message.startsWith("Invalid imageUrl:"))
16899
+ throw err;
16900
+ throw new Error(
16901
+ `Invalid imageUrl: could not resolve hostname (${err instanceof Error ? err.message : String(err)}).`
16902
+ );
16903
+ }
16904
+ }
16905
+ async function resolveImageFromFilePath(filePath, allowedBaseDir) {
16906
+ if (!path.isAbsolute(filePath)) {
16907
+ filePath = path.resolve(allowedBaseDir, filePath);
16908
+ }
16909
+ const realPath = await fs.realpath(filePath).catch(() => null);
16910
+ const resolved = realPath ?? filePath;
16911
+ if (!isPathUnderBase(resolved, allowedBaseDir)) {
16912
+ throw new Error("filePath is outside the allowed workspace directory.");
16913
+ }
16914
+ const stat3 = await fs.stat(resolved, { bigint: false }).catch(() => null);
16915
+ if (!stat3) throw new Error("File not found or not readable.");
16916
+ if (stat3.size > MAX_FILE_SIZE_BYTES) {
16917
+ throw new Error(
16918
+ `File size exceeds ${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB limit.`
16919
+ );
16920
+ }
16921
+ const buffer = await fs.readFile(resolved);
16922
+ const mime = mimeFromExtension(resolved) ?? "image/png";
16923
+ if (!isAllowedMime(mime)) {
16924
+ throw new Error(
16925
+ `File type not allowed. Allowed: ${ALLOWED_MIME_TYPES.join(", ")}.`
16926
+ );
16927
+ }
16928
+ const filename = path.basename(resolved) || "image";
16929
+ return {
16930
+ data: new Uint8Array(buffer),
16931
+ mimeType: mime,
16932
+ filename
16933
+ };
16934
+ }
16935
+ async function resolveImageFromUrl(imageUrl) {
16936
+ await assertUrlSafeForFetch(imageUrl);
16937
+ const controller = new AbortController();
16938
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
16939
+ try {
16940
+ const res = await fetch(imageUrl, {
16941
+ redirect: "manual",
16942
+ signal: controller.signal
16943
+ });
16944
+ clearTimeout(timeout);
16945
+ if (res.status >= 300 && res.status < 400) {
16946
+ throw new Error("Redirects are not allowed. Use the final image URL.");
16947
+ }
16948
+ if (!res.ok) {
16949
+ throw new Error(`Fetch failed: ${res.status} ${res.statusText}`);
16950
+ }
16951
+ const contentType = normalizeContentType(res.headers.get("Content-Type"));
16952
+ const mimeFromHeader = contentType && isAllowedMime(contentType) ? contentType : null;
16953
+ const contentLength = res.headers.get("Content-Length");
16954
+ if (contentLength) {
16955
+ const len = parseInt(contentLength, 10);
16956
+ if (!Number.isNaN(len) && len > MAX_FILE_SIZE_BYTES) {
16957
+ throw new Error(
16958
+ `Content-Length (${len}) exceeds ${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB limit.`
16959
+ );
16960
+ }
16961
+ }
16962
+ const buf = await res.arrayBuffer();
16963
+ if (buf.byteLength > MAX_FILE_SIZE_BYTES) {
16964
+ throw new Error(
16965
+ `Response size exceeds ${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB limit.`
16966
+ );
16967
+ }
16968
+ const mime = mimeFromHeader ?? mimeFromExtension(imageUrl) ?? "image/png";
16969
+ if (!isAllowedMime(mime)) {
16970
+ throw new Error(
16971
+ `Content-Type not allowed. Allowed: ${ALLOWED_MIME_TYPES.join(", ")}.`
16972
+ );
16973
+ }
16974
+ const ext = mime === "image/jpeg" || mime === "image/jpg" ? ".jpg" : mime.replace("image/", ".");
16975
+ return {
16976
+ data: new Uint8Array(buf),
16977
+ mimeType: mime,
16978
+ filename: `image${ext}`
16979
+ };
16980
+ } finally {
16981
+ clearTimeout(timeout);
16982
+ }
16983
+ }
16984
+ async function uploadAvatar(client, args) {
16985
+ try {
16986
+ if (!client.currentUserId) {
16987
+ return {
16988
+ error: "Not authenticated. Call dim_login first.",
16989
+ isError: true
16990
+ };
16991
+ }
16992
+ const { filePath, imageUrl } = args;
16993
+ if (filePath && imageUrl) {
16994
+ return {
16995
+ error: "Provide either filePath or imageUrl, not both.",
16996
+ isError: true
16997
+ };
16998
+ }
16999
+ if (!filePath && !imageUrl) {
17000
+ return { error: "Provide filePath or imageUrl.", isError: true };
17001
+ }
17002
+ const baseDir = getDefaultAllowedBaseDir();
17003
+ const payload = filePath ? await resolveImageFromFilePath(filePath, baseDir) : await resolveImageFromUrl(imageUrl);
17004
+ const user = await client.sdk.users.uploadAvatar(payload);
17005
+ return { data: { success: true, user } };
17006
+ } catch (error) {
17007
+ return {
17008
+ error: error instanceof Error ? error.message : String(error),
17009
+ isError: true
17010
+ };
17011
+ }
17012
+ }
17013
+ async function uploadCoverImage(client, args) {
17014
+ try {
17015
+ if (!client.currentUserId) {
17016
+ return {
17017
+ error: "Not authenticated. Call dim_login first.",
17018
+ isError: true
17019
+ };
17020
+ }
17021
+ const { filePath, imageUrl } = args;
17022
+ if (filePath && imageUrl) {
17023
+ return {
17024
+ error: "Provide either filePath or imageUrl, not both.",
17025
+ isError: true
17026
+ };
17027
+ }
17028
+ if (!filePath && !imageUrl) {
17029
+ return { error: "Provide filePath or imageUrl.", isError: true };
17030
+ }
17031
+ const baseDir = getDefaultAllowedBaseDir();
17032
+ const payload = filePath ? await resolveImageFromFilePath(filePath, baseDir) : await resolveImageFromUrl(imageUrl);
17033
+ const user = await client.sdk.users.uploadCoverImage(payload);
17034
+ return { data: { success: true, user } };
17035
+ } catch (error) {
17036
+ return {
17037
+ error: error instanceof Error ? error.message : String(error),
17038
+ isError: true
17039
+ };
17040
+ }
17041
+ }
17042
+ async function removeAvatar(client) {
17043
+ try {
17044
+ if (!client.currentUserId) {
17045
+ return {
17046
+ error: "Not authenticated. Call dim_login first.",
17047
+ isError: true
17048
+ };
17049
+ }
17050
+ const user = await client.sdk.users.removeAvatar();
17051
+ return { data: { success: true, user } };
17052
+ } catch (error) {
17053
+ return {
17054
+ error: error instanceof Error ? error.message : String(error),
17055
+ isError: true
17056
+ };
17057
+ }
17058
+ }
17059
+ async function removeCoverImage(client) {
17060
+ try {
17061
+ if (!client.currentUserId) {
17062
+ return {
17063
+ error: "Not authenticated. Call dim_login first.",
17064
+ isError: true
17065
+ };
17066
+ }
17067
+ const user = await client.sdk.users.removeCoverImage();
17068
+ return { data: { success: true, user } };
17069
+ } catch (error) {
17070
+ return {
17071
+ error: error instanceof Error ? error.message : String(error),
17072
+ isError: true
17073
+ };
17074
+ }
17075
+ }
17076
+ function getProfileImageRequirements() {
17077
+ return {
17078
+ data: {
17079
+ avatar: {
17080
+ maxFileSizeBytes: MAX_FILE_SIZE_BYTES,
17081
+ maxDimensions: { width: 200, height: 200 },
17082
+ aspectRatio: "1:1",
17083
+ allowedMimeTypes: [...ALLOWED_MIME_TYPES]
17084
+ },
17085
+ cover: {
17086
+ maxFileSizeBytes: MAX_FILE_SIZE_BYTES,
17087
+ maxDimensions: { width: 1200, height: 400 },
17088
+ aspectRatio: "3:1",
17089
+ allowedMimeTypes: [...ALLOWED_MIME_TYPES]
17090
+ }
17091
+ }
17092
+ };
17093
+ }
17094
+ var BIO_MAX_LENGTH = 500;
17095
+ async function setBio(client, args) {
17096
+ try {
17097
+ if (!client.currentUserId) {
17098
+ return {
17099
+ error: "Not authenticated. Call dim_login first.",
17100
+ isError: true
17101
+ };
17102
+ }
17103
+ const { bio } = args;
17104
+ if (typeof bio !== "string") {
17105
+ return { error: "bio must be a string.", isError: true };
17106
+ }
17107
+ if (bio.length > BIO_MAX_LENGTH) {
17108
+ return {
17109
+ error: `Bio must be at most ${BIO_MAX_LENGTH} characters.`,
17110
+ isError: true
17111
+ };
17112
+ }
17113
+ await client.sdk.users.updateProfile({ bio });
17114
+ return { data: { success: true } };
17115
+ } catch (error) {
17116
+ return {
17117
+ error: error instanceof Error ? error.message : String(error),
17118
+ isError: true
17119
+ };
17120
+ }
17121
+ }
17122
+
17123
+ // ../dim-agent-core/src/tools/users.ts
17124
+ function formatMinor(m) {
17125
+ if (m == null) return "\u2014";
17126
+ const n = typeof m === "bigint" ? Number(m) : m;
17127
+ return `$${(n / 1e6).toFixed(2)}`;
17128
+ }
17129
+ async function getGameHistory(client, args) {
17130
+ try {
17131
+ if (!client.currentUserId) {
17132
+ return {
17133
+ error: "Not authenticated. Call dim_login first.",
17134
+ isError: true
17135
+ };
17136
+ }
17137
+ const raw = await client.sdk.users.getGameHistory(client.currentUserId);
17138
+ const limit = Math.min(args.limit ?? 50, 100);
17139
+ const items = raw.slice(0, limit).map((item) => ({
17140
+ ...item,
17141
+ betFormatted: formatMinor(item.betAmount),
17142
+ winningsFormatted: formatMinor(item.winnings)
17143
+ }));
17144
+ return {
17145
+ data: {
17146
+ items,
17147
+ totalReturned: items.length,
17148
+ hint: items.length >= limit ? `Showing most recent ${limit} games. Use limit param to request more (max 100).` : void 0
17149
+ }
17150
+ };
17151
+ } catch (error) {
17152
+ return {
17153
+ error: `Failed to get game history: ${error instanceof Error ? error.message : String(error)}`,
17154
+ isError: true
17155
+ };
17156
+ }
17157
+ }
17158
+ async function getMyStats(client) {
17159
+ try {
17160
+ if (!client.currentUserId) {
17161
+ return {
17162
+ error: "Not authenticated. Call dim_login first.",
17163
+ isError: true
17164
+ };
17165
+ }
17166
+ const stats = await client.sdk.users.getUserStats(
17167
+ client.currentUserId
17168
+ );
17169
+ return {
17170
+ data: {
17171
+ ...stats,
17172
+ totalEarnedFormatted: formatMinor(stats.totalEarned),
17173
+ referralEarnedFormatted: formatMinor(stats.referralEarned),
17174
+ totalLostFormatted: formatMinor(stats.totalLost),
17175
+ totalFeesPaidFormatted: formatMinor(stats.totalFeesPaid)
17176
+ }
17177
+ };
17178
+ } catch (error) {
17179
+ return {
17180
+ error: `Failed to get stats: ${error instanceof Error ? error.message : String(error)}`,
17181
+ isError: true
17182
+ };
17183
+ }
17184
+ }
17185
+
16651
17186
  // ../dim-agent-core/src/tools/index.ts
16652
17187
  var TOOL_DEFINITIONS = [
16653
17188
  // ── Auth ─────────────────────────────────────────────────────────────
@@ -16659,7 +17194,7 @@ var TOOL_DEFINITIONS = [
16659
17194
  },
16660
17195
  {
16661
17196
  name: "dim_get_profile",
16662
- description: "Get the current authenticated user profile including username, avatar, bio, and chess ELO rating.",
17197
+ description: "Get the current authenticated user profile including username, avatar, bio, chess ELO rating, and your DIM wallet address (walletAddress).",
16663
17198
  params: {},
16664
17199
  execute: (c) => getProfile(c)
16665
17200
  },
@@ -16677,6 +17212,70 @@ var TOOL_DEFINITIONS = [
16677
17212
  },
16678
17213
  execute: (c, a) => setUsername(c, a)
16679
17214
  },
17215
+ {
17216
+ name: "dim_upload_avatar",
17217
+ description: "Upload a profile avatar image. Provide filePath (path to image file in workspace) or imageUrl (URL to fetch image from). Max 5MB; allowed types: JPEG, PNG, WebP, GIF. Use dim_get_profile_image_requirements for dimensions.",
17218
+ params: {
17219
+ filePath: {
17220
+ type: "string",
17221
+ description: "Path to image file (under allowed workspace)"
17222
+ },
17223
+ imageUrl: {
17224
+ type: "string",
17225
+ description: "URL of image to fetch and upload"
17226
+ }
17227
+ },
17228
+ execute: (c, a) => uploadAvatar(c, a)
17229
+ },
17230
+ {
17231
+ name: "dim_upload_cover_image",
17232
+ description: "Upload a profile cover/header image. Provide filePath or imageUrl. Max 5MB; allowed types: JPEG, PNG, WebP, GIF. Use dim_get_profile_image_requirements for dimensions.",
17233
+ params: {
17234
+ filePath: {
17235
+ type: "string",
17236
+ description: "Path to image file (under allowed workspace)"
17237
+ },
17238
+ imageUrl: {
17239
+ type: "string",
17240
+ description: "URL of image to fetch and upload"
17241
+ }
17242
+ },
17243
+ execute: (c, a) => uploadCoverImage(
17244
+ c,
17245
+ a
17246
+ )
17247
+ },
17248
+ {
17249
+ name: "dim_remove_avatar",
17250
+ description: "Remove the current profile avatar (reset to default).",
17251
+ params: {},
17252
+ execute: (c) => removeAvatar(c)
17253
+ },
17254
+ {
17255
+ name: "dim_remove_cover_image",
17256
+ description: "Remove the current profile cover/header image (reset to default).",
17257
+ params: {},
17258
+ execute: (c) => removeCoverImage(c)
17259
+ },
17260
+ {
17261
+ name: "dim_get_profile_image_requirements",
17262
+ description: "Get max dimensions, max file size, aspect ratio, and allowed MIME types for avatar and cover images. Use before uploading to validate or choose images.",
17263
+ params: {},
17264
+ execute: async () => getProfileImageRequirements()
17265
+ },
17266
+ {
17267
+ name: "dim_set_bio",
17268
+ description: "Set or update your profile bio. Max 500 characters. No truncation; shorten if over limit.",
17269
+ params: {
17270
+ bio: {
17271
+ type: "string",
17272
+ description: "The new profile bio (max 500 characters)",
17273
+ required: true,
17274
+ max: 500
17275
+ }
17276
+ },
17277
+ execute: (c, a) => setBio(c, a)
17278
+ },
16680
17279
  {
16681
17280
  name: "dim_check_maintenance",
16682
17281
  description: "Check if DIM is in maintenance mode. Call this before other operations to avoid failing with 503; when maintenance is true, inform the user to come back later.",
@@ -16819,9 +17418,15 @@ var TOOL_DEFINITIONS = [
16819
17418
  execute: (c) => listDmThreads(c)
16820
17419
  },
16821
17420
  // ── Wallet ───────────────────────────────────────────────────────────
17421
+ {
17422
+ name: "dim_get_wallet_address",
17423
+ description: "Get the Solana wallet address DIM uses for this account. Use this to confirm which address receives deposits and is used for signing. Returns walletAddress and a short wallet linkage note.",
17424
+ params: {},
17425
+ execute: (c) => getWalletAddress(c)
17426
+ },
16822
17427
  {
16823
17428
  name: "dim_get_balance",
16824
- description: "Get the wallet balance for the authenticated user. Returns SOL and USDC (usdcFormatted is always present, e.g. $0.00 or $1.50).",
17429
+ description: "Get the wallet balance for the authenticated user. Returns your DIM Solana address (walletAddress), SOL and USDC (usdcFormatted is always present, e.g. $0.00 or $1.50).",
16825
17430
  params: {},
16826
17431
  execute: (c) => getBalance(c)
16827
17432
  },
@@ -16863,7 +17468,7 @@ var TOOL_DEFINITIONS = [
16863
17468
  },
16864
17469
  {
16865
17470
  name: "dim_get_wallet_activity",
16866
- description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds). Highlights any claimable items.",
17471
+ description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds) and your DIM wallet address. Highlights any claimable items.",
16867
17472
  params: {
16868
17473
  limit: {
16869
17474
  type: "number",
@@ -16903,6 +17508,23 @@ var TOOL_DEFINITIONS = [
16903
17508
  params: {},
16904
17509
  execute: (c) => getGameMetrics(c)
16905
17510
  },
17511
+ {
17512
+ name: "dim_get_game_history",
17513
+ description: "Get your game history: past games with result, bet, winnings, opponent, and playedAt. Use for daily progress reports.",
17514
+ params: {
17515
+ limit: {
17516
+ type: "number",
17517
+ description: "Max games to return (default 50, max 100)"
17518
+ }
17519
+ },
17520
+ execute: (c, a) => getGameHistory(c, a)
17521
+ },
17522
+ {
17523
+ name: "dim_get_my_stats",
17524
+ description: "Get your aggregated DIM stats: games played, wins, losses, total earned, referral earned, total lost, fees paid, chess ELO, points.",
17525
+ params: {},
17526
+ execute: (c) => getMyStats(c)
17527
+ },
16906
17528
  {
16907
17529
  name: "dim_create_lobby",
16908
17530
  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.",
@@ -17032,6 +17654,30 @@ var TOOL_DEFINITIONS = [
17032
17654
  },
17033
17655
  execute: (c, a) => gameLoop(c, a)
17034
17656
  },
17657
+ {
17658
+ name: "dim_request_rematch",
17659
+ description: "Request a rematch after a completed game. If both players request, a lobby is created automatically server-side.",
17660
+ params: {
17661
+ gameId: {
17662
+ type: "string",
17663
+ description: "The completed game ID to request a rematch for",
17664
+ required: true
17665
+ }
17666
+ },
17667
+ execute: (c, a) => requestRematch(c, a)
17668
+ },
17669
+ {
17670
+ name: "dim_accept_rematch",
17671
+ description: "Accept a rematch request from your opponent. When both players accept, the rematch lobby is created automatically.",
17672
+ params: {
17673
+ gameId: {
17674
+ type: "string",
17675
+ description: "The completed game ID to accept the rematch for",
17676
+ required: true
17677
+ }
17678
+ },
17679
+ execute: (c, a) => acceptRematch(c, a)
17680
+ },
17035
17681
  {
17036
17682
  name: "dim_get_lobby_link",
17037
17683
  description: "Get a shareable join link for a lobby. Share this URL via DM \u2014 the user can join with one click. Like dim_invite_to_lobby, do NOT also call dim_join_queue for the same lobby.",
@@ -17632,7 +18278,7 @@ function createDimMcpServer(config) {
17632
18278
  const server2 = new McpServer({
17633
18279
  name: "dim",
17634
18280
  version: "1.0.0",
17635
- description: "DIM Gaming Platform \u2014 Play games, chat, send USDC, challenge users, and earn referral income. 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."
18281
+ 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."
17636
18282
  });
17637
18283
  const client = new DimAgentClient(config);
17638
18284
  for (const tool of TOOL_DEFINITIONS) {
@@ -17675,7 +18321,12 @@ function createDimMcpServer(config) {
17675
18321
  type: "text",
17676
18322
  text: JSON.stringify(
17677
18323
  {
18324
+ wallet_linkage: WALLET_LINKAGE_NOTE,
18325
+ 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.",
18326
+ wallet_activity: "For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.",
17678
18327
  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.",
18328
+ 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].",
18329
+ 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.",
17679
18330
  move_formats: {
17680
18331
  "rock-paper-scissors": 'action: "play", payload: { action: "rock"|"paper"|"scissors" }',
17681
18332
  chess: 'action: "move", payload: { from: "e2", to: "e4" }',
@@ -17698,7 +18349,7 @@ function createDimMcpServer(config) {
17698
18349
  }
17699
18350
 
17700
18351
  // src/index.ts
17701
- var DEFAULT_WALLET_STORE_PATH = path.join(
18352
+ var DEFAULT_WALLET_STORE_PATH = path2.join(
17702
18353
  os.homedir(),
17703
18354
  ".dim",
17704
18355
  "mcp-wallet.json"
@@ -17717,21 +18368,21 @@ function resolveWalletStorePath(cliArgs2) {
17717
18368
  );
17718
18369
  }
17719
18370
  if (value2.startsWith("~/")) {
17720
- return path.join(os.homedir(), value2.slice(2));
18371
+ return path2.join(os.homedir(), value2.slice(2));
17721
18372
  }
17722
- return path.resolve(value2);
18373
+ return path2.resolve(value2);
17723
18374
  }
17724
18375
  const envPath = process.env.DIM_WALLET_STORE_PATH?.trim();
17725
18376
  if (envPath) {
17726
18377
  if (envPath.startsWith("~/")) {
17727
- return path.join(os.homedir(), envPath.slice(2));
18378
+ return path2.join(os.homedir(), envPath.slice(2));
17728
18379
  }
17729
- return path.resolve(envPath);
18380
+ return path2.resolve(envPath);
17730
18381
  }
17731
18382
  return DEFAULT_WALLET_STORE_PATH;
17732
18383
  }
17733
18384
  async function writeWalletStoreFile(storePath, record) {
17734
- await mkdir(path.dirname(storePath), { recursive: true });
18385
+ await mkdir(path2.dirname(storePath), { recursive: true });
17735
18386
  const tmpPath = `${storePath}.tmp`;
17736
18387
  await writeFile(tmpPath, `${JSON.stringify(record, null, 2)}
17737
18388
  `, {
@@ -17743,7 +18394,7 @@ async function writeWalletStoreFile(storePath, record) {
17743
18394
  }
17744
18395
  async function readWalletStoreFile(storePath) {
17745
18396
  try {
17746
- const raw = await readFile(storePath, "utf8");
18397
+ const raw = await readFile2(storePath, "utf8");
17747
18398
  const parsed = JSON.parse(raw);
17748
18399
  if (parsed.walletPrivateKey && parsed.walletAddress) {
17749
18400
  return {
@@ -17769,7 +18420,7 @@ async function createWalletRecord() {
17769
18420
  }
17770
18421
  async function walletStoreExists(storePath) {
17771
18422
  try {
17772
- await stat(storePath);
18423
+ await stat2(storePath);
17773
18424
  return true;
17774
18425
  } catch {
17775
18426
  return false;