@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.
- package/dist/index.js +772 -121
- 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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
344
|
+
while (fs2.existsSync(syncFile)) {
|
|
345
345
|
}
|
|
346
|
-
self2.responseText =
|
|
346
|
+
self2.responseText = fs2.readFileSync(contentFile, "utf8");
|
|
347
347
|
syncProc.stdin.end();
|
|
348
|
-
|
|
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
|
|
1239
|
-
var
|
|
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 =
|
|
1256
|
+
dir = path3.resolve(dir || ".");
|
|
1257
1257
|
try {
|
|
1258
|
-
var name = runtimeRequire(
|
|
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(
|
|
1263
|
+
var release = getFirst(path3.join(dir, "build/Release"), matchBuild);
|
|
1264
1264
|
if (release) return release;
|
|
1265
|
-
var debug12 = getFirst(
|
|
1265
|
+
var debug12 = getFirst(path3.join(dir, "build/Debug"), matchBuild);
|
|
1266
1266
|
if (debug12) return debug12;
|
|
1267
1267
|
}
|
|
1268
|
-
var prebuild =
|
|
1268
|
+
var prebuild = resolve2(dir);
|
|
1269
1269
|
if (prebuild) return prebuild;
|
|
1270
|
-
var nearby =
|
|
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
|
|
1287
|
-
var tuples = readdirSync(
|
|
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 =
|
|
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
|
|
1294
|
+
if (winner) return path3.join(prebuilds, winner.file);
|
|
1295
1295
|
}
|
|
1296
1296
|
};
|
|
1297
1297
|
function readdirSync(dir) {
|
|
1298
1298
|
try {
|
|
1299
|
-
return
|
|
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] &&
|
|
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" &&
|
|
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
|
|
4169
|
+
if (address instanceof URL2) {
|
|
4170
4170
|
parsedUrl = address;
|
|
4171
4171
|
} else {
|
|
4172
4172
|
try {
|
|
4173
|
-
parsedUrl = new
|
|
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
|
|
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
|
|
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,
|
|
8576
|
-
const regx = /\/{2,9}/g, names =
|
|
8577
|
-
if (
|
|
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 (
|
|
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,
|
|
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 +
|
|
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((
|
|
9881
|
+
return new Promise((resolve2, reject) => {
|
|
9882
9882
|
const fn = (arg1, arg2) => {
|
|
9883
|
-
return arg1 ? reject(arg1) :
|
|
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
|
|
10856
|
-
const sameNamespace = cache[id] &&
|
|
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(
|
|
11841
|
-
const
|
|
11842
|
-
|
|
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(
|
|
11846
|
-
const
|
|
11847
|
-
|
|
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
|
|
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.
|
|
11996
|
-
const prepareResponse = await escrow.prepareDepositTransaction(lobby.id);
|
|
12013
|
+
const { transaction } = await escrow.prepareAndStartDeposit(lobby.id);
|
|
11997
12014
|
return {
|
|
11998
12015
|
lobby,
|
|
11999
|
-
unsignedTransaction:
|
|
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((
|
|
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
|
|
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.
|
|
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: "
|
|
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((
|
|
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
|
-
|
|
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: {
|
|
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 = {
|
|
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
|
-
|
|
15732
|
-
|
|
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.
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
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((
|
|
16133
|
+
new Promise((resolve2) => {
|
|
16016
16134
|
const unsub = store.store.subscribeSelector(
|
|
16017
16135
|
(s) => s.statesByGameId[gameId],
|
|
16018
16136
|
() => {
|
|
16019
16137
|
unsub();
|
|
16020
|
-
|
|
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((
|
|
16068
|
-
const timer = setTimeout(
|
|
16222
|
+
return new Promise((resolve2) => {
|
|
16223
|
+
const timer = setTimeout(resolve2, ms);
|
|
16069
16224
|
promise.then(() => {
|
|
16070
16225
|
clearTimeout(timer);
|
|
16071
|
-
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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
|
|
18371
|
+
return path2.join(os.homedir(), value2.slice(2));
|
|
17721
18372
|
}
|
|
17722
|
-
return
|
|
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
|
|
18378
|
+
return path2.join(os.homedir(), envPath.slice(2));
|
|
17728
18379
|
}
|
|
17729
|
-
return
|
|
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(
|
|
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
|
|
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
|
|
18423
|
+
await stat2(storePath);
|
|
17773
18424
|
return true;
|
|
17774
18425
|
} catch {
|
|
17775
18426
|
return false;
|