@dimcool/dimclaw 0.1.14 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6513,14 +6513,14 @@ var require_url_state_machine = __commonJS({
6513
6513
  return url3.replace(/\u0009|\u000A|\u000D/g, "");
6514
6514
  }
6515
6515
  function shortenPath(url3) {
6516
- const path2 = url3.path;
6517
- if (path2.length === 0) {
6516
+ const path3 = url3.path;
6517
+ if (path3.length === 0) {
6518
6518
  return;
6519
6519
  }
6520
- if (url3.scheme === "file" && path2.length === 1 && isNormalizedWindowsDriveLetter(path2[0])) {
6520
+ if (url3.scheme === "file" && path3.length === 1 && isNormalizedWindowsDriveLetter(path3[0])) {
6521
6521
  return;
6522
6522
  }
6523
- path2.pop();
6523
+ path3.pop();
6524
6524
  }
6525
6525
  function includesCredentials(url3) {
6526
6526
  return url3.username !== "" || url3.password !== "";
@@ -7577,7 +7577,7 @@ var require_node_gyp_build = __commonJS({
7577
7577
  "../../node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
7578
7578
  "use strict";
7579
7579
  var fs = __require("fs");
7580
- var path2 = __require("path");
7580
+ var path3 = __require("path");
7581
7581
  var os = __require("os");
7582
7582
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
7583
7583
  var vars = process.config && process.config.variables || {};
@@ -7594,21 +7594,21 @@ var require_node_gyp_build = __commonJS({
7594
7594
  return runtimeRequire(load.resolve(dir));
7595
7595
  }
7596
7596
  load.resolve = load.path = function(dir) {
7597
- dir = path2.resolve(dir || ".");
7597
+ dir = path3.resolve(dir || ".");
7598
7598
  try {
7599
- var name = runtimeRequire(path2.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
7599
+ var name = runtimeRequire(path3.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
7600
7600
  if (process.env[name + "_PREBUILD"]) dir = process.env[name + "_PREBUILD"];
7601
7601
  } catch (err) {
7602
7602
  }
7603
7603
  if (!prebuildsOnly) {
7604
- var release = getFirst(path2.join(dir, "build/Release"), matchBuild);
7604
+ var release = getFirst(path3.join(dir, "build/Release"), matchBuild);
7605
7605
  if (release) return release;
7606
- var debug12 = getFirst(path2.join(dir, "build/Debug"), matchBuild);
7606
+ var debug12 = getFirst(path3.join(dir, "build/Debug"), matchBuild);
7607
7607
  if (debug12) return debug12;
7608
7608
  }
7609
7609
  var prebuild = resolve(dir);
7610
7610
  if (prebuild) return prebuild;
7611
- var nearby = resolve(path2.dirname(process.execPath));
7611
+ var nearby = resolve(path3.dirname(process.execPath));
7612
7612
  if (nearby) return nearby;
7613
7613
  var target = [
7614
7614
  "platform=" + platform,
@@ -7625,14 +7625,14 @@ var require_node_gyp_build = __commonJS({
7625
7625
  ].filter(Boolean).join(" ");
7626
7626
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
7627
7627
  function resolve(dir2) {
7628
- var tuples = readdirSync(path2.join(dir2, "prebuilds")).map(parseTuple);
7628
+ var tuples = readdirSync(path3.join(dir2, "prebuilds")).map(parseTuple);
7629
7629
  var tuple3 = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
7630
7630
  if (!tuple3) return;
7631
- var prebuilds = path2.join(dir2, "prebuilds", tuple3.name);
7631
+ var prebuilds = path3.join(dir2, "prebuilds", tuple3.name);
7632
7632
  var parsed = readdirSync(prebuilds).map(parseTags);
7633
7633
  var candidates = parsed.filter(matchTags(runtime, abi));
7634
7634
  var winner = candidates.sort(compareTags(runtime))[0];
7635
- if (winner) return path2.join(prebuilds, winner.file);
7635
+ if (winner) return path3.join(prebuilds, winner.file);
7636
7636
  }
7637
7637
  };
7638
7638
  function readdirSync(dir) {
@@ -7644,7 +7644,7 @@ var require_node_gyp_build = __commonJS({
7644
7644
  }
7645
7645
  function getFirst(dir, filter) {
7646
7646
  var files = readdirSync(dir).filter(filter);
7647
- return files[0] && path2.join(dir, files[0]);
7647
+ return files[0] && path3.join(dir, files[0]);
7648
7648
  }
7649
7649
  function matchBuild(name) {
7650
7650
  return /\.node$/.test(name);
@@ -18589,8 +18589,8 @@ var require_nacl_fast = __commonJS({
18589
18589
  });
18590
18590
 
18591
18591
  // index.ts
18592
- import { mkdir, readFile, rename, writeFile } from "fs/promises";
18593
- import path from "path";
18592
+ import { mkdir as mkdir2, readFile, rename as rename2, writeFile as writeFile2 } from "fs/promises";
18593
+ import path2 from "path";
18594
18594
 
18595
18595
  // ../../node_modules/@solana/web3.js/lib/index.esm.js
18596
18596
  import { Buffer as Buffer2 } from "buffer";
@@ -21589,8 +21589,8 @@ var StructError = class extends TypeError {
21589
21589
  constructor(failure, failures) {
21590
21590
  let cached;
21591
21591
  const { message, explanation, ...rest } = failure;
21592
- const { path: path2 } = failure;
21593
- const msg = path2.length === 0 ? message : `At path: ${path2.join(".")} -- ${message}`;
21592
+ const { path: path3 } = failure;
21593
+ const msg = path3.length === 0 ? message : `At path: ${path3.join(".")} -- ${message}`;
21594
21594
  super(explanation ?? msg);
21595
21595
  if (explanation != null)
21596
21596
  this.cause = msg;
@@ -21628,15 +21628,15 @@ function toFailure(result, context, struct2, value2) {
21628
21628
  } else if (typeof result === "string") {
21629
21629
  result = { message: result };
21630
21630
  }
21631
- const { path: path2, branch } = context;
21631
+ const { path: path3, branch } = context;
21632
21632
  const { type: type3 } = struct2;
21633
21633
  const { refinement, message = `Expected a value of type \`${type3}\`${refinement ? ` with refinement \`${refinement}\`` : ""}, but received: \`${print(value2)}\`` } = result;
21634
21634
  return {
21635
21635
  value: value2,
21636
21636
  type: type3,
21637
21637
  refinement,
21638
- key: path2[path2.length - 1],
21639
- path: path2,
21638
+ key: path3[path3.length - 1],
21639
+ path: path3,
21640
21640
  branch,
21641
21641
  ...result,
21642
21642
  message
@@ -21654,8 +21654,8 @@ function* toFailures(result, context, struct2, value2) {
21654
21654
  }
21655
21655
  }
21656
21656
  function* run(value2, struct2, options = {}) {
21657
- const { path: path2 = [], branch = [value2], coerce: coerce3 = false, mask: mask3 = false } = options;
21658
- const ctx = { path: path2, branch, mask: mask3 };
21657
+ const { path: path3 = [], branch = [value2], coerce: coerce3 = false, mask: mask3 = false } = options;
21658
+ const ctx = { path: path3, branch, mask: mask3 };
21659
21659
  if (coerce3) {
21660
21660
  value2 = struct2.coercer(value2, ctx);
21661
21661
  }
@@ -21667,7 +21667,7 @@ function* run(value2, struct2, options = {}) {
21667
21667
  }
21668
21668
  for (let [k, v, s] of struct2.entries(value2, ctx)) {
21669
21669
  const ts = run(v, s, {
21670
- path: k === void 0 ? path2 : [...path2, k],
21670
+ path: k === void 0 ? path3 : [...path3, k],
21671
21671
  branch: k === void 0 ? branch : [...branch, v],
21672
21672
  coerce: coerce3,
21673
21673
  mask: mask3,
@@ -28583,6 +28583,10 @@ var esm_default = base;
28583
28583
  var ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
28584
28584
  var esm_default2 = esm_default(ALPHABET);
28585
28585
 
28586
+ // dim-client.ts
28587
+ import { mkdir, writeFile, rename } from "fs/promises";
28588
+ import path from "path";
28589
+
28586
28590
  // ../sdk/dist/index.js
28587
28591
  import crypto22 from "crypto";
28588
28592
  import crypto32 from "crypto";
@@ -29836,12 +29840,12 @@ function parse3(str) {
29836
29840
  uri.queryKey = queryKey(uri, uri["query"]);
29837
29841
  return uri;
29838
29842
  }
29839
- function pathNames(obj, path2) {
29840
- const regx = /\/{2,9}/g, names = path2.replace(regx, "/").split("/");
29841
- if (path2.slice(0, 1) == "/" || path2.length === 0) {
29843
+ function pathNames(obj, path3) {
29844
+ const regx = /\/{2,9}/g, names = path3.replace(regx, "/").split("/");
29845
+ if (path3.slice(0, 1) == "/" || path3.length === 0) {
29842
29846
  names.splice(0, 1);
29843
29847
  }
29844
- if (path2.slice(-1) == "/") {
29848
+ if (path3.slice(-1) == "/") {
29845
29849
  names.splice(names.length - 1, 1);
29846
29850
  }
29847
29851
  return names;
@@ -30458,7 +30462,7 @@ var protocol2 = Socket.protocol;
30458
30462
  // ../../node_modules/socket.io-client/build/esm-debug/url.js
30459
30463
  var import_debug7 = __toESM(require_src2(), 1);
30460
30464
  var debug7 = (0, import_debug7.default)("socket.io-client:url");
30461
- function url2(uri, path2 = "", loc) {
30465
+ function url2(uri, path3 = "", loc) {
30462
30466
  let obj = uri;
30463
30467
  loc = loc || typeof location !== "undefined" && location;
30464
30468
  if (null == uri)
@@ -30492,7 +30496,7 @@ function url2(uri, path2 = "", loc) {
30492
30496
  obj.path = obj.path || "/";
30493
30497
  const ipv6 = obj.host.indexOf(":") !== -1;
30494
30498
  const host = ipv6 ? "[" + obj.host + "]" : obj.host;
30495
- obj.id = obj.protocol + "://" + host + ":" + obj.port + path2;
30499
+ obj.id = obj.protocol + "://" + host + ":" + obj.port + path3;
30496
30500
  obj.href = obj.protocol + "://" + host + (loc && loc.port === obj.port ? "" : ":" + obj.port);
30497
30501
  return obj;
30498
30502
  }
@@ -32116,8 +32120,8 @@ function lookup(uri, opts) {
32116
32120
  const parsed = url2(uri, opts.path || "/socket.io");
32117
32121
  const source = parsed.source;
32118
32122
  const id = parsed.id;
32119
- const path2 = parsed.path;
32120
- const sameNamespace = cache[id] && path2 in cache[id]["nsps"];
32123
+ const path3 = parsed.path;
32124
+ const sameNamespace = cache[id] && path3 in cache[id]["nsps"];
32121
32125
  const newConnection = opts.forceNew || opts["force new connection"] || false === opts.multiplex || sameNamespace;
32122
32126
  let io;
32123
32127
  if (newConnection) {
@@ -38664,14 +38668,14 @@ var require_url_state_machine2 = __commonJS2({
38664
38668
  return url22.replace(/\u0009|\u000A|\u000D/g, "");
38665
38669
  }
38666
38670
  function shortenPath(url22) {
38667
- const path2 = url22.path;
38668
- if (path2.length === 0) {
38671
+ const path3 = url22.path;
38672
+ if (path3.length === 0) {
38669
38673
  return;
38670
38674
  }
38671
- if (url22.scheme === "file" && path2.length === 1 && isNormalizedWindowsDriveLetter(path2[0])) {
38675
+ if (url22.scheme === "file" && path3.length === 1 && isNormalizedWindowsDriveLetter(path3[0])) {
38672
38676
  return;
38673
38677
  }
38674
- path2.pop();
38678
+ path3.pop();
38675
38679
  }
38676
38680
  function includesCredentials(url22) {
38677
38681
  return url22.username !== "" || url22.password !== "";
@@ -39718,7 +39722,7 @@ var require_node_gyp_build3 = __commonJS2({
39718
39722
  "../../node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
39719
39723
  "use strict";
39720
39724
  var fs = __require2("fs");
39721
- var path2 = __require2("path");
39725
+ var path3 = __require2("path");
39722
39726
  var os = __require2("os");
39723
39727
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require2;
39724
39728
  var vars = process.config && process.config.variables || {};
@@ -39735,21 +39739,21 @@ var require_node_gyp_build3 = __commonJS2({
39735
39739
  return runtimeRequire(load.resolve(dir));
39736
39740
  }
39737
39741
  load.resolve = load.path = function(dir) {
39738
- dir = path2.resolve(dir || ".");
39742
+ dir = path3.resolve(dir || ".");
39739
39743
  try {
39740
- var name = runtimeRequire(path2.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
39744
+ var name = runtimeRequire(path3.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
39741
39745
  if (process.env[name + "_PREBUILD"]) dir = process.env[name + "_PREBUILD"];
39742
39746
  } catch (err) {
39743
39747
  }
39744
39748
  if (!prebuildsOnly) {
39745
- var release = getFirst(path2.join(dir, "build/Release"), matchBuild);
39749
+ var release = getFirst(path3.join(dir, "build/Release"), matchBuild);
39746
39750
  if (release) return release;
39747
- var debug12 = getFirst(path2.join(dir, "build/Debug"), matchBuild);
39751
+ var debug12 = getFirst(path3.join(dir, "build/Debug"), matchBuild);
39748
39752
  if (debug12) return debug12;
39749
39753
  }
39750
39754
  var prebuild = resolve(dir);
39751
39755
  if (prebuild) return prebuild;
39752
- var nearby = resolve(path2.dirname(process.execPath));
39756
+ var nearby = resolve(path3.dirname(process.execPath));
39753
39757
  if (nearby) return nearby;
39754
39758
  var target = [
39755
39759
  "platform=" + platform,
@@ -39766,14 +39770,14 @@ var require_node_gyp_build3 = __commonJS2({
39766
39770
  ].filter(Boolean).join(" ");
39767
39771
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
39768
39772
  function resolve(dir2) {
39769
- var tuples = readdirSync(path2.join(dir2, "prebuilds")).map(parseTuple);
39773
+ var tuples = readdirSync(path3.join(dir2, "prebuilds")).map(parseTuple);
39770
39774
  var tuple22 = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
39771
39775
  if (!tuple22) return;
39772
- var prebuilds = path2.join(dir2, "prebuilds", tuple22.name);
39776
+ var prebuilds = path3.join(dir2, "prebuilds", tuple22.name);
39773
39777
  var parsed = readdirSync(prebuilds).map(parseTags);
39774
39778
  var candidates = parsed.filter(matchTags(runtime, abi));
39775
39779
  var winner = candidates.sort(compareTags(runtime))[0];
39776
- if (winner) return path2.join(prebuilds, winner.file);
39780
+ if (winner) return path3.join(prebuilds, winner.file);
39777
39781
  }
39778
39782
  };
39779
39783
  function readdirSync(dir) {
@@ -39785,7 +39789,7 @@ var require_node_gyp_build3 = __commonJS2({
39785
39789
  }
39786
39790
  function getFirst(dir, filter) {
39787
39791
  var files = readdirSync(dir).filter(filter);
39788
- return files[0] && path2.join(dir, files[0]);
39792
+ return files[0] && path3.join(dir, files[0]);
39789
39793
  }
39790
39794
  function matchBuild(name) {
39791
39795
  return /\.node$/.test(name);
@@ -46870,8 +46874,8 @@ var StructError2 = class extends TypeError {
46870
46874
  constructor(failure, failures) {
46871
46875
  let cached;
46872
46876
  const { message, explanation, ...rest } = failure;
46873
- const { path: path2 } = failure;
46874
- const msg = path2.length === 0 ? message : `At path: ${path2.join(".")} -- ${message}`;
46877
+ const { path: path3 } = failure;
46878
+ const msg = path3.length === 0 ? message : `At path: ${path3.join(".")} -- ${message}`;
46875
46879
  super(explanation ?? msg);
46876
46880
  if (explanation != null)
46877
46881
  this.cause = msg;
@@ -46909,15 +46913,15 @@ function toFailure2(result, context, struct2, value2) {
46909
46913
  } else if (typeof result === "string") {
46910
46914
  result = { message: result };
46911
46915
  }
46912
- const { path: path2, branch } = context;
46916
+ const { path: path3, branch } = context;
46913
46917
  const { type: type22 } = struct2;
46914
46918
  const { refinement, message = `Expected a value of type \`${type22}\`${refinement ? ` with refinement \`${refinement}\`` : ""}, but received: \`${print2(value2)}\`` } = result;
46915
46919
  return {
46916
46920
  value: value2,
46917
46921
  type: type22,
46918
46922
  refinement,
46919
- key: path2[path2.length - 1],
46920
- path: path2,
46923
+ key: path3[path3.length - 1],
46924
+ path: path3,
46921
46925
  branch,
46922
46926
  ...result,
46923
46927
  message
@@ -46935,8 +46939,8 @@ function* toFailures2(result, context, struct2, value2) {
46935
46939
  }
46936
46940
  }
46937
46941
  function* run2(value2, struct2, options = {}) {
46938
- const { path: path2 = [], branch = [value2], coerce: coerce22 = false, mask: mask22 = false } = options;
46939
- const ctx = { path: path2, branch, mask: mask22 };
46942
+ const { path: path3 = [], branch = [value2], coerce: coerce22 = false, mask: mask22 = false } = options;
46943
+ const ctx = { path: path3, branch, mask: mask22 };
46940
46944
  if (coerce22) {
46941
46945
  value2 = struct2.coercer(value2, ctx);
46942
46946
  }
@@ -46948,7 +46952,7 @@ function* run2(value2, struct2, options = {}) {
46948
46952
  }
46949
46953
  for (let [k, v, s] of struct2.entries(value2, ctx)) {
46950
46954
  const ts = run2(v, s, {
46951
- path: k === void 0 ? path2 : [...path2, k],
46955
+ path: k === void 0 ? path3 : [...path3, k],
46952
46956
  branch: k === void 0 ? branch : [...branch, v],
46953
46957
  coerce: coerce22,
46954
46958
  mask: mask22,
@@ -57305,6 +57309,8 @@ var DimClient = class {
57305
57309
  config;
57306
57310
  authenticated = false;
57307
57311
  userId = null;
57312
+ eventQueue = [];
57313
+ unsubscribers = [];
57308
57314
  constructor(config) {
57309
57315
  this.config = config;
57310
57316
  const secretKeyBytes = esm_default2.decode(config.walletPrivateKey);
@@ -57360,6 +57366,97 @@ var DimClient = class {
57360
57366
  async ensureConnected(timeoutMs = 1e4) {
57361
57367
  await this.sdk.ensureWebSocketConnected(timeoutMs);
57362
57368
  }
57369
+ /**
57370
+ * Subscribe to key WS events and buffer them for agent consumption.
57371
+ * Call after authenticate() when the WS transport is connected.
57372
+ */
57373
+ startEventListeners() {
57374
+ const events = [
57375
+ "chat:message",
57376
+ "notification",
57377
+ "lobby:matched",
57378
+ "lobby:invitation",
57379
+ "game:turn",
57380
+ "game:completed"
57381
+ ];
57382
+ for (const event of events) {
57383
+ this.unsubscribers.push(
57384
+ this.sdk.events.subscribe(event, (payload) => {
57385
+ this.eventQueue.push({
57386
+ event,
57387
+ payload,
57388
+ at: (/* @__PURE__ */ new Date()).toISOString()
57389
+ });
57390
+ this.writeHeartbeat().catch(() => {
57391
+ });
57392
+ })
57393
+ );
57394
+ }
57395
+ }
57396
+ /** Write HEARTBEAT.md with pending event summary for OpenClaw's heartbeat cycle. */
57397
+ async writeHeartbeat() {
57398
+ if (!this.config.heartbeatPath) return;
57399
+ const filePath = this.resolveHeartbeatPath();
57400
+ const count = this.eventQueue.length;
57401
+ if (count === 0) return;
57402
+ const lines = ["# DIM Heartbeat", ""];
57403
+ const eventTypes = new Set(this.eventQueue.map((e) => e.event));
57404
+ if (eventTypes.has("chat:message")) {
57405
+ lines.push("- You have new DMs \u2014 call dim_check_notifications");
57406
+ }
57407
+ if (eventTypes.has("notification")) {
57408
+ lines.push(
57409
+ "- New notifications (challenges, friend requests, game results) \u2014 call dim_check_notifications"
57410
+ );
57411
+ }
57412
+ if (eventTypes.has("lobby:matched") || eventTypes.has("lobby:invitation")) {
57413
+ lines.push("- A game match is ready \u2014 call dim_get_pending_events");
57414
+ }
57415
+ if (eventTypes.has("game:turn")) {
57416
+ lines.push("- It's your turn in a game \u2014 call dim_get_pending_events");
57417
+ }
57418
+ if (eventTypes.has("game:completed")) {
57419
+ lines.push("- A game has completed \u2014 call dim_get_pending_events");
57420
+ }
57421
+ lines.push("");
57422
+ await mkdir(path.dirname(filePath), { recursive: true });
57423
+ const tmp = `${filePath}.tmp`;
57424
+ await writeFile(tmp, lines.join("\n"), "utf8");
57425
+ await rename(tmp, filePath);
57426
+ }
57427
+ resolveHeartbeatPath() {
57428
+ const p = this.config.heartbeatPath;
57429
+ if (p.startsWith("~/") && process.env.HOME) {
57430
+ return path.join(process.env.HOME, p.slice(2));
57431
+ }
57432
+ return path.resolve(p);
57433
+ }
57434
+ /** Drain all buffered events since last call. */
57435
+ drainEvents() {
57436
+ return this.eventQueue.splice(0);
57437
+ }
57438
+ /** Peek at buffered event count without draining. */
57439
+ get pendingEventCount() {
57440
+ return this.eventQueue.length;
57441
+ }
57442
+ // ── Daily spend tracking ──────────────────────────────────────────────
57443
+ dailySpendMinor = 0;
57444
+ spendResetDate = "";
57445
+ resetDailySpendIfNeeded() {
57446
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
57447
+ if (this.spendResetDate !== today) {
57448
+ this.dailySpendMinor = 0;
57449
+ this.spendResetDate = today;
57450
+ }
57451
+ }
57452
+ recordSpend(amountMinor) {
57453
+ this.resetDailySpendIfNeeded();
57454
+ this.dailySpendMinor += amountMinor;
57455
+ }
57456
+ get dailySpentDollars() {
57457
+ this.resetDailySpendIfNeeded();
57458
+ return this.dailySpendMinor / 1e6;
57459
+ }
57363
57460
  getKeypair() {
57364
57461
  return this.keypair;
57365
57462
  }
@@ -57373,8 +57470,8 @@ function getPluginConfig(api) {
57373
57470
  return dimclawEntry?.config ?? null;
57374
57471
  }
57375
57472
  function resolveStorePath(storePath) {
57376
- const expanded = storePath.startsWith("~/") && process.env.HOME ? path.join(process.env.HOME, storePath.slice(2)) : storePath;
57377
- return path.resolve(expanded);
57473
+ const expanded = storePath.startsWith("~/") && process.env.HOME ? path2.join(process.env.HOME, storePath.slice(2)) : storePath;
57474
+ return path2.resolve(expanded);
57378
57475
  }
57379
57476
  async function readWalletFile(storePath) {
57380
57477
  try {
@@ -57394,14 +57491,14 @@ async function readWalletFile(storePath) {
57394
57491
  }
57395
57492
  }
57396
57493
  async function writeWalletFile(storePath, record3) {
57397
- await mkdir(path.dirname(storePath), { recursive: true });
57494
+ await mkdir2(path2.dirname(storePath), { recursive: true });
57398
57495
  const tmp = `${storePath}.tmp`;
57399
- await writeFile(tmp, `${JSON.stringify(record3, null, 2)}
57496
+ await writeFile2(tmp, `${JSON.stringify(record3, null, 2)}
57400
57497
  `, {
57401
57498
  encoding: "utf8",
57402
57499
  mode: 384
57403
57500
  });
57404
- await rename(tmp, storePath);
57501
+ await rename2(tmp, storePath);
57405
57502
  }
57406
57503
  function createWalletRecord() {
57407
57504
  const keypair = Keypair.generate();
@@ -57422,6 +57519,194 @@ async function resolveWalletKey(config) {
57422
57519
  await writeWalletFile(storePath, record3);
57423
57520
  return record3.walletPrivateKey;
57424
57521
  }
57522
+ function generateOnboardingScript(platform, referrer) {
57523
+ const templates = {
57524
+ openclaw: `# Connect to DIM \u2014 OpenClaw Setup
57525
+
57526
+ ## Step 1: Install the DIM plugin
57527
+ \`\`\`bash
57528
+ openclaw plugins install @dimcool/dimclaw
57529
+ \`\`\`
57530
+
57531
+ ## Step 2: Configure
57532
+ Add to your OpenClaw config under \`plugins.entries.dimclaw.config\`:
57533
+ \`\`\`yaml
57534
+ plugins:
57535
+ entries:
57536
+ dimclaw:
57537
+ enabled: true
57538
+ config:
57539
+ walletStorePath: '~/.dim/openclaw-wallet.json'
57540
+ \`\`\`
57541
+ A wallet will be created automatically on first run.
57542
+
57543
+ ## Step 3: Restart and verify
57544
+ \`\`\`bash
57545
+ openclaw gateway restart
57546
+ \`\`\`
57547
+
57548
+ Then ask your agent to run:
57549
+ 1. \`dim_login\`
57550
+ 2. \`dim_apply_referral_code\` with referralCode: \`${referrer}\`
57551
+ 3. \`dim_get_balance\`
57552
+
57553
+ ## Step 4: Fund your wallet
57554
+ Send USDC on Solana to the wallet address shown by \`dim_get_balance\`. Even $5 is enough to start playing.
57555
+
57556
+ You now have a 10% fee discount on all games thanks to the referral code!`,
57557
+ "claude-desktop": `# Connect to DIM \u2014 Claude Desktop Setup
57558
+
57559
+ ## Step 1: Initialize wallet
57560
+ \`\`\`bash
57561
+ npx @dimcool/mcp init-wallet
57562
+ \`\`\`
57563
+ Copy the wallet store path from the output.
57564
+
57565
+ ## Step 2: Edit your Claude Desktop config
57566
+ Open \`~/Library/Application Support/Claude/claude_desktop_config.json\` (macOS) or \`%APPDATA%\\Claude\\claude_desktop_config.json\` (Windows).
57567
+
57568
+ Add this to the \`mcpServers\` section:
57569
+ \`\`\`json
57570
+ {
57571
+ "mcpServers": {
57572
+ "dim": {
57573
+ "command": "npx",
57574
+ "args": ["@dimcool/mcp"],
57575
+ "env": {
57576
+ "DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
57577
+ "DIM_API_URL": "https://api.dim.cool",
57578
+ "DIM_REFERRAL_CODE": "${referrer}"
57579
+ }
57580
+ }
57581
+ }
57582
+ }
57583
+ \`\`\`
57584
+ Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
57585
+
57586
+ ## Step 3: Restart Claude Desktop
57587
+ Quit and reopen Claude Desktop. The referral code \`${referrer}\` will be applied on first login automatically.
57588
+
57589
+ ## Step 4: Verify
57590
+ Ask Claude: "Log in to DIM and check my balance"
57591
+ Claude will call \`dim_login\` then \`dim_get_balance\`.
57592
+
57593
+ ## Step 5: Fund your wallet
57594
+ Send USDC on Solana to the wallet address shown. Even $5 is enough to start.
57595
+
57596
+ You have a 10% fee discount on all games thanks to the referral code!`,
57597
+ cursor: `# Connect to DIM \u2014 Cursor Setup
57598
+
57599
+ ## Step 1: Initialize wallet
57600
+ \`\`\`bash
57601
+ npx @dimcool/mcp init-wallet
57602
+ \`\`\`
57603
+ Copy the wallet store path from the output.
57604
+
57605
+ ## Step 2: Edit your Cursor MCP config
57606
+ Create or edit \`.cursor/mcp.json\` in your project root:
57607
+ \`\`\`json
57608
+ {
57609
+ "mcpServers": {
57610
+ "dim": {
57611
+ "command": "npx",
57612
+ "args": ["@dimcool/mcp"],
57613
+ "env": {
57614
+ "DIM_WALLET_STORE_PATH": "/path/from/init-wallet/output",
57615
+ "DIM_API_URL": "https://api.dim.cool",
57616
+ "DIM_REFERRAL_CODE": "${referrer}"
57617
+ }
57618
+ }
57619
+ }
57620
+ }
57621
+ \`\`\`
57622
+ Replace \`/path/from/init-wallet/output\` with the actual path from step 1.
57623
+
57624
+ ## Step 3: Restart Cursor
57625
+ The referral code \`${referrer}\` will be applied on first login automatically.
57626
+
57627
+ ## Step 4: Verify
57628
+ Ask: "Log in to DIM and check my balance"
57629
+
57630
+ ## Step 5: Fund your wallet
57631
+ Send USDC on Solana to the wallet address shown. Even $5 is enough to start.`,
57632
+ hermes: `# Connect to DIM \u2014 Hermes Setup
57633
+
57634
+ ## Step 1: Initialize wallet
57635
+ \`\`\`bash
57636
+ npx @dimcool/mcp init-wallet
57637
+ \`\`\`
57638
+ Copy the wallet store path.
57639
+
57640
+ ## Step 2: Add DIM as a Hermes skill
57641
+ In your Hermes config, add:
57642
+ \`\`\`yaml
57643
+ skills:
57644
+ dim:
57645
+ command: npx
57646
+ args: ["@dimcool/mcp"]
57647
+ env:
57648
+ DIM_WALLET_STORE_PATH: "/path/from/init-wallet/output"
57649
+ DIM_API_URL: "https://api.dim.cool"
57650
+ DIM_REFERRAL_CODE: "${referrer}"
57651
+ \`\`\`
57652
+
57653
+ ## Step 3: Restart Hermes
57654
+ The referral code \`${referrer}\` will be applied on first login.
57655
+
57656
+ ## Step 4: Verify
57657
+ Call \`dim_login\` then \`dim_get_balance\`.
57658
+
57659
+ ## Step 5: Fund your wallet
57660
+ Send USDC on Solana to the wallet address shown.`,
57661
+ "node-sdk": `# Connect to DIM \u2014 Node.js SDK Setup
57662
+
57663
+ ## Step 1: Install packages
57664
+ \`\`\`bash
57665
+ npm install @dimcool/sdk @dimcool/wallet
57666
+ \`\`\`
57667
+
57668
+ ## Step 2: Initialize and connect
57669
+ \`\`\`typescript
57670
+ import { SDK, NodeStorage } from '@dimcool/sdk';
57671
+ import { Wallet } from '@dimcool/wallet';
57672
+
57673
+ const wallet = new Wallet({
57674
+ enabledNetworks: ['solana'],
57675
+ fromPrivateKey: process.env.DIM_WALLET_PRIVATE_KEY!,
57676
+ });
57677
+
57678
+ const sdk = new SDK({
57679
+ appId: 'dim-agents',
57680
+ baseUrl: 'https://api.dim.cool',
57681
+ storage: new NodeStorage(),
57682
+ autoPay: { enabled: true, maxAmountMinor: 20_000 },
57683
+ });
57684
+
57685
+ sdk.wallet.setSigner(wallet.getSigner());
57686
+
57687
+ const { access_token, user } = await sdk.auth.loginWithWallet({
57688
+ referralCode: '${referrer}',
57689
+ });
57690
+
57691
+ sdk.wsTransport.setAccessToken(access_token);
57692
+ await sdk.ensureWebSocketConnected(10000);
57693
+
57694
+ console.log('Connected as', user.username, '\u2014 wallet:', wallet.getAddresses().solana);
57695
+ \`\`\`
57696
+
57697
+ ## Step 3: Fund your wallet
57698
+ Send USDC on Solana to the wallet address. Even $5 is enough to start.
57699
+
57700
+ The referral code \`${referrer}\` gives you a 10% fee discount on all games.
57701
+
57702
+ Full docs: https://docs.dim.cool`
57703
+ };
57704
+ const script = templates[platform];
57705
+ if (!script) {
57706
+ return `Unknown platform "${platform}". Supported: openclaw, claude-desktop, cursor, hermes, node-sdk`;
57707
+ }
57708
+ return script;
57709
+ }
57425
57710
  function textResult(text, isError = false) {
57426
57711
  return {
57427
57712
  content: [{ type: "text", text }],
@@ -57430,15 +57715,32 @@ function textResult(text, isError = false) {
57430
57715
  }
57431
57716
  function register(api) {
57432
57717
  let client = null;
57718
+ let pluginConfig = null;
57719
+ function checkSpendLimit(c, amountDollars, isGameBet = false) {
57720
+ if (!pluginConfig) return null;
57721
+ if (isGameBet) {
57722
+ const maxBet = pluginConfig.maxBetPerGame ?? 1;
57723
+ if (amountDollars > maxBet) {
57724
+ return `Bet $${amountDollars.toFixed(2)} exceeds maxBetPerGame limit of $${maxBet.toFixed(2)}. Ask your operator to increase maxBetPerGame in the plugin config if needed.`;
57725
+ }
57726
+ }
57727
+ const limit = pluginConfig.dailySpendLimit ?? 20;
57728
+ const projected = c.dailySpentDollars + amountDollars;
57729
+ if (projected > limit) {
57730
+ return `Daily spend limit reached ($${c.dailySpentDollars.toFixed(2)} spent of $${limit.toFixed(2)} limit). This action would cost $${amountDollars.toFixed(2)}. Ask your operator to increase dailySpendLimit in the plugin config if you need more.`;
57731
+ }
57732
+ return null;
57733
+ }
57433
57734
  async function getClient() {
57434
57735
  if (client) return client;
57435
- const config = getPluginConfig(api);
57436
- if (!config) return null;
57437
- const walletPrivateKey = await resolveWalletKey(config);
57736
+ pluginConfig = getPluginConfig(api);
57737
+ if (!pluginConfig) return null;
57738
+ const walletPrivateKey = await resolveWalletKey(pluginConfig);
57438
57739
  if (!walletPrivateKey) return null;
57439
57740
  client = new DimClient({
57440
57741
  walletPrivateKey,
57441
- apiUrl: config.apiUrl
57742
+ apiUrl: pluginConfig.apiUrl,
57743
+ heartbeatPath: pluginConfig.heartbeatPath
57442
57744
  });
57443
57745
  return client;
57444
57746
  }
@@ -57461,6 +57763,7 @@ function register(api) {
57461
57763
  if ("error" in c) return c.error;
57462
57764
  try {
57463
57765
  const result = await c.authenticate();
57766
+ c.startEventListeners();
57464
57767
  const nextSteps = [];
57465
57768
  if (result.username == null || result.username === "") {
57466
57769
  nextSteps.push(
@@ -57488,6 +57791,15 @@ function register(api) {
57488
57791
  userId: result.userId,
57489
57792
  username: result.username ?? null,
57490
57793
  walletAddress: c.walletAddress,
57794
+ agentConfig: {
57795
+ autoAcceptFriendRequests: pluginConfig?.autoAcceptFriendRequests ?? false,
57796
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
57797
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
57798
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1,
57799
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20,
57800
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
57801
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false
57802
+ },
57491
57803
  nextSteps
57492
57804
  };
57493
57805
  return textResult(JSON.stringify(response, null, 2));
@@ -57692,7 +58004,23 @@ function register(api) {
57692
58004
  name: "dim_redeem_shares",
57693
58005
  description: "Redeem shares after market resolution."
57694
58006
  },
57695
- { name: "dim_get_market_analytics", description: "Get market analytics." }
58007
+ { name: "dim_get_market_analytics", description: "Get market analytics." },
58008
+ {
58009
+ name: "dim_get_pending_events",
58010
+ description: "Drain buffered real-time events (DMs, challenges, game turns). Call regularly."
58011
+ },
58012
+ {
58013
+ name: "dim_check_notifications",
58014
+ description: "Check unread notifications, DMs, and friend requests in one call."
58015
+ },
58016
+ {
58017
+ name: "dim_get_agent_config",
58018
+ description: "Get autonomy scopes, spending limits, and current daily spend."
58019
+ },
58020
+ {
58021
+ name: "dim_get_referral_onboarding",
58022
+ description: "Get platform-specific setup instructions to share with another agent, with your referral code embedded."
58023
+ }
57696
58024
  ];
57697
58025
  api.registerTool({
57698
58026
  name: "dim_list_instructions",
@@ -57757,9 +58085,12 @@ function register(api) {
57757
58085
  if ("error" in c) return c.error;
57758
58086
  const recipient = String(params.recipient ?? "");
57759
58087
  const amount = Number(params.amount ?? 0);
58088
+ const limitErr = checkSpendLimit(c, amount);
58089
+ if (limitErr) return textResult(limitErr, true);
57760
58090
  try {
57761
58091
  const amountMinor = Math.round(amount * 1e6);
57762
58092
  const result = await c.sdk.wallet.send(recipient, amountMinor);
58093
+ c.recordSpend(amountMinor);
57763
58094
  return textResult(
57764
58095
  JSON.stringify(
57765
58096
  {
@@ -57806,9 +58137,12 @@ function register(api) {
57806
58137
  if ("error" in c) return c.error;
57807
58138
  const recipientUsername = String(params.recipientUsername ?? "");
57808
58139
  const amount = Number(params.amount ?? 0);
58140
+ const limitErr = checkSpendLimit(c, amount);
58141
+ if (limitErr) return textResult(limitErr, true);
57809
58142
  try {
57810
58143
  const amountMinor = Math.round(amount * 1e6);
57811
58144
  const result = await c.sdk.tips.send(recipientUsername, amountMinor);
58145
+ c.recordSpend(amountMinor);
57812
58146
  return textResult(
57813
58147
  JSON.stringify(
57814
58148
  {
@@ -58197,9 +58531,15 @@ function register(api) {
58197
58531
  const c = await requireClient();
58198
58532
  if ("error" in c) return c.error;
58199
58533
  const gameType = String(params.gameType ?? "");
58200
- const betAmount = typeof params.betAmount === "number" ? Math.round(params.betAmount * 1e6) : void 0;
58534
+ const betDollars = typeof params.betAmount === "number" ? params.betAmount : 0;
58535
+ const betAmount = betDollars > 0 ? Math.round(betDollars * 1e6) : void 0;
58536
+ if (betDollars > 0) {
58537
+ const limitErr = checkSpendLimit(c, betDollars, true);
58538
+ if (limitErr) return textResult(limitErr, true);
58539
+ }
58201
58540
  try {
58202
58541
  const lobby = await c.sdk.lobbies.createLobby(gameType, betAmount);
58542
+ if (betAmount) c.recordSpend(betAmount);
58203
58543
  return textResult(
58204
58544
  JSON.stringify(
58205
58545
  {
@@ -58397,12 +58737,15 @@ function register(api) {
58397
58737
  const c = await requireClient();
58398
58738
  if ("error" in c) return c.error;
58399
58739
  const amount = Number(params.amount ?? 0);
58740
+ const limitErr = checkSpendLimit(c, amount);
58741
+ if (limitErr) return textResult(limitErr, true);
58400
58742
  try {
58401
58743
  const amountMinor = Math.round(amount * 1e6);
58402
58744
  const result = await c.sdk.games.sendDonation(
58403
58745
  String(params.gameId ?? ""),
58404
58746
  amountMinor
58405
58747
  );
58748
+ c.recordSpend(amountMinor);
58406
58749
  return textResult(
58407
58750
  JSON.stringify(
58408
58751
  {
@@ -58983,6 +59326,8 @@ function register(api) {
58983
59326
  const amount = Number(params.amount ?? 0);
58984
59327
  const gameId = String(params.gameId ?? "");
58985
59328
  const outcomeId = String(params.outcomeId ?? "");
59329
+ const limitErr = checkSpendLimit(c, amount);
59330
+ if (limitErr) return textResult(limitErr, true);
58986
59331
  try {
58987
59332
  const amountMinor = Math.round(amount * 1e6);
58988
59333
  const { transaction: txBase64 } = await c.sdk.markets.prepareBuyOrder(
@@ -58999,6 +59344,7 @@ function register(api) {
58999
59344
  outcomeId,
59000
59345
  amountMinor
59001
59346
  );
59347
+ c.recordSpend(amountMinor);
59002
59348
  return textResult(
59003
59349
  JSON.stringify(
59004
59350
  {
@@ -59186,6 +59532,137 @@ function register(api) {
59186
59532
  }
59187
59533
  }
59188
59534
  });
59535
+ api.registerTool({
59536
+ name: "dim_get_agent_config",
59537
+ description: "Get the agent autonomy configuration: what actions are allowed (friends, DMs, games, chat, referral promotion), spending limits, and current daily spend.",
59538
+ parameters: { type: "object", properties: {}, additionalProperties: false },
59539
+ async execute() {
59540
+ const c = await requireClient();
59541
+ if ("error" in c) return c.error;
59542
+ return textResult(
59543
+ JSON.stringify(
59544
+ {
59545
+ autoAcceptFriendRequests: pluginConfig?.autoAcceptFriendRequests ?? false,
59546
+ autoReplyDms: pluginConfig?.autoReplyDms ?? false,
59547
+ autoPlayGames: pluginConfig?.autoPlayGames ?? false,
59548
+ maxBetPerGame: pluginConfig?.maxBetPerGame ?? 1,
59549
+ dailySpendLimit: pluginConfig?.dailySpendLimit ?? 20,
59550
+ dailySpentSoFar: `$${c.dailySpentDollars.toFixed(2)}`,
59551
+ dailyRemaining: `$${Math.max(0, (pluginConfig?.dailySpendLimit ?? 20) - c.dailySpentDollars).toFixed(2)}`,
59552
+ autoJoinGlobalChat: pluginConfig?.autoJoinGlobalChat ?? false,
59553
+ autoPromoteReferrals: pluginConfig?.autoPromoteReferrals ?? false,
59554
+ hint: "These scopes are set by your operator in the plugin config. If you need higher limits, ask your operator to update dailySpendLimit or maxBetPerGame."
59555
+ },
59556
+ null,
59557
+ 2
59558
+ )
59559
+ );
59560
+ }
59561
+ });
59562
+ api.registerTool({
59563
+ name: "dim_get_referral_onboarding",
59564
+ description: "Get platform-specific setup instructions to share with another agent or user to onboard them to DIM with your referral code. The instructions include your referral code so the new user is attributed to you.",
59565
+ parameters: {
59566
+ type: "object",
59567
+ properties: {
59568
+ platform: {
59569
+ type: "string",
59570
+ enum: ["openclaw", "claude-desktop", "cursor", "hermes", "node-sdk"],
59571
+ description: "Target platform: openclaw, claude-desktop, cursor, hermes, or node-sdk"
59572
+ }
59573
+ },
59574
+ required: ["platform"],
59575
+ additionalProperties: false
59576
+ },
59577
+ async execute(_, params) {
59578
+ const c = await requireClient();
59579
+ if ("error" in c) return c.error;
59580
+ try {
59581
+ let username = "YOUR_USERNAME";
59582
+ try {
59583
+ if (c.currentUserId) {
59584
+ const profile = await c.sdk.users.getUserById(c.currentUserId);
59585
+ if (profile.username) username = profile.username;
59586
+ }
59587
+ } catch {
59588
+ }
59589
+ const platform = String(params.platform ?? "openclaw");
59590
+ const script = generateOnboardingScript(platform, username);
59591
+ return textResult(script);
59592
+ } catch (err) {
59593
+ return textResult(
59594
+ `Failed to generate onboarding script: ${err instanceof Error ? err.message : String(err)}`,
59595
+ true
59596
+ );
59597
+ }
59598
+ }
59599
+ });
59600
+ api.registerTool({
59601
+ name: "dim_get_pending_events",
59602
+ description: "Drain buffered real-time events (DMs, challenges, game turns, match notifications). Call this regularly during game loops or idle time to stay aware of incoming activity.",
59603
+ parameters: { type: "object", properties: {}, additionalProperties: false },
59604
+ async execute() {
59605
+ const c = await requireClient();
59606
+ if ("error" in c) return c.error;
59607
+ try {
59608
+ const events = c.drainEvents();
59609
+ return textResult(
59610
+ JSON.stringify(
59611
+ {
59612
+ count: events.length,
59613
+ events,
59614
+ hint: events.length === 0 ? "No new events since last check." : "Process these events and take action as needed."
59615
+ },
59616
+ null,
59617
+ 2
59618
+ )
59619
+ );
59620
+ } catch (err) {
59621
+ return textResult(
59622
+ `Failed to get pending events: ${err instanceof Error ? err.message : String(err)}`,
59623
+ true
59624
+ );
59625
+ }
59626
+ }
59627
+ });
59628
+ api.registerTool({
59629
+ name: "dim_check_notifications",
59630
+ description: "Check all pending items in one call: unread notifications (challenges, game results), unread DM threads, and incoming friend requests. Use this to catch up after being idle.",
59631
+ parameters: { type: "object", properties: {}, additionalProperties: false },
59632
+ async execute() {
59633
+ const c = await requireClient();
59634
+ if ("error" in c) return c.error;
59635
+ try {
59636
+ const [notifications, dmThreads, friendRequests] = await Promise.all([
59637
+ c.sdk.notifications.list({ page: 1, limit: 20 }),
59638
+ c.sdk.chat.listDmThreads(),
59639
+ c.sdk.users.getIncomingFriendRequests()
59640
+ ]);
59641
+ const unreadDms = dmThreads.filter(
59642
+ (t) => (t.unreadCount ?? 0) > 0
59643
+ );
59644
+ return textResult(
59645
+ JSON.stringify(
59646
+ {
59647
+ unreadNotificationCount: notifications.unreadCount,
59648
+ notifications: notifications.notifications.filter((n) => !n.read),
59649
+ unreadDmThreads: unreadDms,
59650
+ incomingFriendRequests: friendRequests,
59651
+ pendingWsEvents: c.pendingEventCount,
59652
+ hint: "Use dim_get_pending_events to drain buffered real-time events."
59653
+ },
59654
+ null,
59655
+ 2
59656
+ )
59657
+ );
59658
+ } catch (err) {
59659
+ return textResult(
59660
+ `Failed to check notifications: ${err instanceof Error ? err.message : String(err)}`,
59661
+ true
59662
+ );
59663
+ }
59664
+ }
59665
+ });
59189
59666
  }
59190
59667
  export {
59191
59668
  register as default