@dimcool/mcp 0.1.27 → 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 +485 -68
  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() {
@@ -12178,7 +12196,7 @@ async function withRetry(fn, options = {}) {
12178
12196
  throw lastError;
12179
12197
  }
12180
12198
  function sleep(ms) {
12181
- return new Promise((resolve) => setTimeout(resolve, ms));
12199
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
12182
12200
  }
12183
12201
  var DEFAULT_RETRY_OPTIONS = {
12184
12202
  maxAttempts: 3,
@@ -13446,12 +13464,12 @@ var BaseWsTransport = class {
13446
13464
  if (this.connectionState.connected) {
13447
13465
  return Promise.resolve();
13448
13466
  }
13449
- return new Promise((resolve, reject) => {
13467
+ return new Promise((resolve2, reject) => {
13450
13468
  const timeoutId = setTimeout(() => {
13451
13469
  this.waiters.delete(waiter);
13452
13470
  reject(new Error("WebSocket connection timed out"));
13453
13471
  }, timeoutMs);
13454
- const waiter = { resolve, reject, timeoutId };
13472
+ const waiter = { resolve: resolve2, reject, timeoutId };
13455
13473
  this.waiters.add(waiter);
13456
13474
  });
13457
13475
  }
@@ -14225,6 +14243,22 @@ function createGameActionsStore(transport2) {
14225
14243
  updateState(gameId, updated);
14226
14244
  break;
14227
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
+ }
14228
14262
  case "game:state": {
14229
14263
  const gameId = event.payload.gameId ?? event.payload.state.gameId;
14230
14264
  if (!gameId) return;
@@ -16081,13 +16115,12 @@ async function gameLoop(client, args) {
16081
16115
  const store = client.sdk.gameActionsStore;
16082
16116
  const timeout = 3e5;
16083
16117
  const start = Date.now();
16084
- let state = store.store.getState().statesByGameId[gameId];
16085
- if (!state) {
16086
- const fetched = await client.sdk.games.getGameState(gameId);
16087
- store.setBaseState(gameId, fetched);
16088
- store.joinGame(gameId);
16089
- state = fetched;
16090
- }
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();
16091
16124
  while (Date.now() - start < timeout) {
16092
16125
  state = store.store.getState().statesByGameId[gameId];
16093
16126
  if (state?.status === "completed") {
@@ -16097,17 +16130,22 @@ async function gameLoop(client, args) {
16097
16130
  return buildGameLoopReturn(client, gameId, state, "your-turn");
16098
16131
  }
16099
16132
  await raceTimeout(
16100
- new Promise((resolve) => {
16133
+ new Promise((resolve2) => {
16101
16134
  const unsub = store.store.subscribeSelector(
16102
16135
  (s) => s.statesByGameId[gameId],
16103
16136
  () => {
16104
16137
  unsub();
16105
- resolve();
16138
+ resolve2();
16106
16139
  }
16107
16140
  );
16108
16141
  }),
16109
16142
  1e3
16110
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
+ }
16111
16149
  }
16112
16150
  state = store.store.getState().statesByGameId[gameId];
16113
16151
  return buildGameLoopReturn(client, gameId, state ?? {}, "timeout");
@@ -16181,11 +16219,11 @@ async function acceptRematch(client, args) {
16181
16219
  }
16182
16220
  }
16183
16221
  function raceTimeout(promise, ms) {
16184
- return new Promise((resolve) => {
16185
- const timer = setTimeout(resolve, ms);
16222
+ return new Promise((resolve2) => {
16223
+ const timer = setTimeout(resolve2, ms);
16186
16224
  promise.then(() => {
16187
16225
  clearTimeout(timer);
16188
- resolve();
16226
+ resolve2();
16189
16227
  });
16190
16228
  });
16191
16229
  }
@@ -16767,6 +16805,321 @@ async function getAgentConfig(client) {
16767
16805
  }
16768
16806
  }
16769
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
+
16770
17123
  // ../dim-agent-core/src/tools/users.ts
16771
17124
  function formatMinor(m) {
16772
17125
  if (m == null) return "\u2014";
@@ -16859,6 +17212,70 @@ var TOOL_DEFINITIONS = [
16859
17212
  },
16860
17213
  execute: (c, a) => setUsername(c, a)
16861
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
+ },
16862
17279
  {
16863
17280
  name: "dim_check_maintenance",
16864
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.",
@@ -17932,7 +18349,7 @@ function createDimMcpServer(config) {
17932
18349
  }
17933
18350
 
17934
18351
  // src/index.ts
17935
- var DEFAULT_WALLET_STORE_PATH = path.join(
18352
+ var DEFAULT_WALLET_STORE_PATH = path2.join(
17936
18353
  os.homedir(),
17937
18354
  ".dim",
17938
18355
  "mcp-wallet.json"
@@ -17951,21 +18368,21 @@ function resolveWalletStorePath(cliArgs2) {
17951
18368
  );
17952
18369
  }
17953
18370
  if (value2.startsWith("~/")) {
17954
- return path.join(os.homedir(), value2.slice(2));
18371
+ return path2.join(os.homedir(), value2.slice(2));
17955
18372
  }
17956
- return path.resolve(value2);
18373
+ return path2.resolve(value2);
17957
18374
  }
17958
18375
  const envPath = process.env.DIM_WALLET_STORE_PATH?.trim();
17959
18376
  if (envPath) {
17960
18377
  if (envPath.startsWith("~/")) {
17961
- return path.join(os.homedir(), envPath.slice(2));
18378
+ return path2.join(os.homedir(), envPath.slice(2));
17962
18379
  }
17963
- return path.resolve(envPath);
18380
+ return path2.resolve(envPath);
17964
18381
  }
17965
18382
  return DEFAULT_WALLET_STORE_PATH;
17966
18383
  }
17967
18384
  async function writeWalletStoreFile(storePath, record) {
17968
- await mkdir(path.dirname(storePath), { recursive: true });
18385
+ await mkdir(path2.dirname(storePath), { recursive: true });
17969
18386
  const tmpPath = `${storePath}.tmp`;
17970
18387
  await writeFile(tmpPath, `${JSON.stringify(record, null, 2)}
17971
18388
  `, {
@@ -17977,7 +18394,7 @@ async function writeWalletStoreFile(storePath, record) {
17977
18394
  }
17978
18395
  async function readWalletStoreFile(storePath) {
17979
18396
  try {
17980
- const raw = await readFile(storePath, "utf8");
18397
+ const raw = await readFile2(storePath, "utf8");
17981
18398
  const parsed = JSON.parse(raw);
17982
18399
  if (parsed.walletPrivateKey && parsed.walletAddress) {
17983
18400
  return {
@@ -18003,7 +18420,7 @@ async function createWalletRecord() {
18003
18420
  }
18004
18421
  async function walletStoreExists(storePath) {
18005
18422
  try {
18006
- await stat(storePath);
18423
+ await stat2(storePath);
18007
18424
  return true;
18008
18425
  } catch {
18009
18426
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dimcool/mcp",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "MCP server for DIM — lets AI agents play games, chat, send USDC, and earn referral income on the DIM platform",
5
5
  "type": "module",
6
6
  "bin": {