@dimcool/dimclaw 0.1.13 → 0.1.17
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/dim-client.ts +89 -0
- package/dist/index.js +252 -90
- package/index.ts +88 -1
- package/openclaw.plugin.json +7 -0
- package/package.json +1 -1
package/dim-client.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Handles wallet-based authentication using a Solana keypair.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { mkdir, writeFile, rename } from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
6
8
|
import { SDK, NodeStorage } from '@dimcool/sdk';
|
|
7
9
|
import { Keypair, Transaction } from '@solana/web3.js';
|
|
8
10
|
import bs58 from 'bs58';
|
|
@@ -15,6 +17,14 @@ export interface DimClientConfig {
|
|
|
15
17
|
apiUrl?: string;
|
|
16
18
|
/** Referral code to use on first signup */
|
|
17
19
|
referralCode?: string;
|
|
20
|
+
/** Path to HEARTBEAT.md for OpenClaw's heartbeat cycle */
|
|
21
|
+
heartbeatPath?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface BufferedEvent {
|
|
25
|
+
event: string;
|
|
26
|
+
payload: unknown;
|
|
27
|
+
at: string;
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
export class DimClient {
|
|
@@ -23,6 +33,8 @@ export class DimClient {
|
|
|
23
33
|
private config: DimClientConfig;
|
|
24
34
|
private authenticated = false;
|
|
25
35
|
private userId: string | null = null;
|
|
36
|
+
private eventQueue: BufferedEvent[] = [];
|
|
37
|
+
private unsubscribers: Array<() => void> = [];
|
|
26
38
|
|
|
27
39
|
constructor(config: DimClientConfig) {
|
|
28
40
|
this.config = config;
|
|
@@ -96,6 +108,83 @@ export class DimClient {
|
|
|
96
108
|
await this.sdk.ensureWebSocketConnected(timeoutMs);
|
|
97
109
|
}
|
|
98
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Subscribe to key WS events and buffer them for agent consumption.
|
|
113
|
+
* Call after authenticate() when the WS transport is connected.
|
|
114
|
+
*/
|
|
115
|
+
startEventListeners(): void {
|
|
116
|
+
const events = [
|
|
117
|
+
'chat:message',
|
|
118
|
+
'notification',
|
|
119
|
+
'lobby:matched',
|
|
120
|
+
'lobby:invitation',
|
|
121
|
+
'game:turn',
|
|
122
|
+
'game:completed',
|
|
123
|
+
];
|
|
124
|
+
for (const event of events) {
|
|
125
|
+
this.unsubscribers.push(
|
|
126
|
+
this.sdk.events.subscribe(event, (payload: unknown) => {
|
|
127
|
+
this.eventQueue.push({
|
|
128
|
+
event,
|
|
129
|
+
payload,
|
|
130
|
+
at: new Date().toISOString(),
|
|
131
|
+
});
|
|
132
|
+
this.writeHeartbeat().catch(() => {});
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Write HEARTBEAT.md with pending event summary for OpenClaw's heartbeat cycle. */
|
|
139
|
+
private async writeHeartbeat(): Promise<void> {
|
|
140
|
+
if (!this.config.heartbeatPath) return;
|
|
141
|
+
const filePath = this.resolveHeartbeatPath();
|
|
142
|
+
const count = this.eventQueue.length;
|
|
143
|
+
if (count === 0) return;
|
|
144
|
+
const lines = ['# DIM Heartbeat', ''];
|
|
145
|
+
const eventTypes = new Set(this.eventQueue.map((e) => e.event));
|
|
146
|
+
if (eventTypes.has('chat:message')) {
|
|
147
|
+
lines.push('- You have new DMs — call dim_check_notifications');
|
|
148
|
+
}
|
|
149
|
+
if (eventTypes.has('notification')) {
|
|
150
|
+
lines.push(
|
|
151
|
+
'- New notifications (challenges, friend requests, game results) — call dim_check_notifications',
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (eventTypes.has('lobby:matched') || eventTypes.has('lobby:invitation')) {
|
|
155
|
+
lines.push('- A game match is ready — call dim_get_pending_events');
|
|
156
|
+
}
|
|
157
|
+
if (eventTypes.has('game:turn')) {
|
|
158
|
+
lines.push("- It's your turn in a game — call dim_get_pending_events");
|
|
159
|
+
}
|
|
160
|
+
if (eventTypes.has('game:completed')) {
|
|
161
|
+
lines.push('- A game has completed — call dim_get_pending_events');
|
|
162
|
+
}
|
|
163
|
+
lines.push('');
|
|
164
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
165
|
+
const tmp = `${filePath}.tmp`;
|
|
166
|
+
await writeFile(tmp, lines.join('\n'), 'utf8');
|
|
167
|
+
await rename(tmp, filePath);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private resolveHeartbeatPath(): string {
|
|
171
|
+
const p = this.config.heartbeatPath!;
|
|
172
|
+
if (p.startsWith('~/') && process.env.HOME) {
|
|
173
|
+
return path.join(process.env.HOME, p.slice(2));
|
|
174
|
+
}
|
|
175
|
+
return path.resolve(p);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Drain all buffered events since last call. */
|
|
179
|
+
drainEvents(): BufferedEvent[] {
|
|
180
|
+
return this.eventQueue.splice(0);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Peek at buffered event count without draining. */
|
|
184
|
+
get pendingEventCount(): number {
|
|
185
|
+
return this.eventQueue.length;
|
|
186
|
+
}
|
|
187
|
+
|
|
99
188
|
getKeypair(): Keypair {
|
|
100
189
|
return this.keypair;
|
|
101
190
|
}
|
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
|
|
6517
|
-
if (
|
|
6516
|
+
const path3 = url3.path;
|
|
6517
|
+
if (path3.length === 0) {
|
|
6518
6518
|
return;
|
|
6519
6519
|
}
|
|
6520
|
-
if (url3.scheme === "file" &&
|
|
6520
|
+
if (url3.scheme === "file" && path3.length === 1 && isNormalizedWindowsDriveLetter(path3[0])) {
|
|
6521
6521
|
return;
|
|
6522
6522
|
}
|
|
6523
|
-
|
|
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
|
|
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 =
|
|
7597
|
+
dir = path3.resolve(dir || ".");
|
|
7598
7598
|
try {
|
|
7599
|
-
var name = runtimeRequire(
|
|
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(
|
|
7604
|
+
var release = getFirst(path3.join(dir, "build/Release"), matchBuild);
|
|
7605
7605
|
if (release) return release;
|
|
7606
|
-
var debug12 = getFirst(
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
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] &&
|
|
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
|
|
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:
|
|
21593
|
-
const msg =
|
|
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:
|
|
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:
|
|
21639
|
-
path:
|
|
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:
|
|
21658
|
-
const ctx = { path:
|
|
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 ?
|
|
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,
|
|
29840
|
-
const regx = /\/{2,9}/g, names =
|
|
29841
|
-
if (
|
|
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 (
|
|
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,
|
|
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 +
|
|
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
|
|
32120
|
-
const sameNamespace = cache[id] &&
|
|
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
|
|
38668
|
-
if (
|
|
38671
|
+
const path3 = url22.path;
|
|
38672
|
+
if (path3.length === 0) {
|
|
38669
38673
|
return;
|
|
38670
38674
|
}
|
|
38671
|
-
if (url22.scheme === "file" &&
|
|
38675
|
+
if (url22.scheme === "file" && path3.length === 1 && isNormalizedWindowsDriveLetter(path3[0])) {
|
|
38672
38676
|
return;
|
|
38673
38677
|
}
|
|
38674
|
-
|
|
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
|
|
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 =
|
|
39742
|
+
dir = path3.resolve(dir || ".");
|
|
39739
39743
|
try {
|
|
39740
|
-
var name = runtimeRequire(
|
|
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(
|
|
39749
|
+
var release = getFirst(path3.join(dir, "build/Release"), matchBuild);
|
|
39746
39750
|
if (release) return release;
|
|
39747
|
-
var debug12 = getFirst(
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
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] &&
|
|
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:
|
|
46874
|
-
const msg =
|
|
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:
|
|
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:
|
|
46920
|
-
path:
|
|
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:
|
|
46939
|
-
const ctx = { path:
|
|
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 ?
|
|
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,
|
|
@@ -53700,6 +53704,27 @@ var VoteAccountLayout2 = BufferLayout2.struct([
|
|
|
53700
53704
|
BufferLayout2.seq(BufferLayout2.struct([BufferLayout2.nu64("epoch"), BufferLayout2.nu64("credits"), BufferLayout2.nu64("prevCredits")]), BufferLayout2.offset(BufferLayout2.u32(), -8), "epochCredits"),
|
|
53701
53705
|
BufferLayout2.struct([BufferLayout2.nu64("slot"), BufferLayout2.nu64("timestamp")], "lastTimestamp")
|
|
53702
53706
|
]);
|
|
53707
|
+
function base64ToBytes(base64) {
|
|
53708
|
+
if (typeof Buffer !== "undefined") {
|
|
53709
|
+
return Buffer.from(base64, "base64");
|
|
53710
|
+
}
|
|
53711
|
+
const binary = atob(base64);
|
|
53712
|
+
const bytes = new Uint8Array(binary.length);
|
|
53713
|
+
for (let i = 0; i < binary.length; i++) {
|
|
53714
|
+
bytes[i] = binary.charCodeAt(i);
|
|
53715
|
+
}
|
|
53716
|
+
return bytes;
|
|
53717
|
+
}
|
|
53718
|
+
function bytesToBase64(bytes) {
|
|
53719
|
+
if (typeof Buffer !== "undefined") {
|
|
53720
|
+
return Buffer.from(bytes).toString("base64");
|
|
53721
|
+
}
|
|
53722
|
+
let binary = "";
|
|
53723
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
53724
|
+
binary += String.fromCharCode(bytes[i]);
|
|
53725
|
+
}
|
|
53726
|
+
return btoa(binary);
|
|
53727
|
+
}
|
|
53703
53728
|
var HttpClient = class {
|
|
53704
53729
|
constructor(baseUrl, storage, logger2, appId, autoPayConfig) {
|
|
53705
53730
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
@@ -53971,11 +53996,11 @@ var HttpClient = class {
|
|
|
53971
53996
|
}
|
|
53972
53997
|
}
|
|
53973
53998
|
);
|
|
53974
|
-
const unsignedTx = Transaction2.from(
|
|
53975
|
-
Buffer.from(prepare.transaction, "base64")
|
|
53976
|
-
);
|
|
53999
|
+
const unsignedTx = Transaction2.from(base64ToBytes(prepare.transaction));
|
|
53977
54000
|
const signedTx = await this.signTransactionHandler(unsignedTx);
|
|
53978
|
-
const signedTransaction =
|
|
54001
|
+
const signedTransaction = bytesToBase64(
|
|
54002
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
54003
|
+
);
|
|
53979
54004
|
const submitted = await this.post(
|
|
53980
54005
|
"/payments/submit",
|
|
53981
54006
|
{
|
|
@@ -53989,19 +54014,6 @@ var HttpClient = class {
|
|
|
53989
54014
|
return submitted.paymentProof;
|
|
53990
54015
|
}
|
|
53991
54016
|
};
|
|
53992
|
-
function toBase64(bytes) {
|
|
53993
|
-
if (typeof Buffer !== "undefined") {
|
|
53994
|
-
return Buffer.from(bytes).toString("base64");
|
|
53995
|
-
}
|
|
53996
|
-
let binary = "";
|
|
53997
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
53998
|
-
binary += String.fromCharCode(bytes[i]);
|
|
53999
|
-
}
|
|
54000
|
-
if (typeof btoa === "undefined") {
|
|
54001
|
-
throw new Error("Base64 encoding is not available in this environment");
|
|
54002
|
-
}
|
|
54003
|
-
return btoa(binary);
|
|
54004
|
-
}
|
|
54005
54017
|
var Auth = class {
|
|
54006
54018
|
constructor(http22, storage, wallet, logger2) {
|
|
54007
54019
|
this.http = http22;
|
|
@@ -54066,7 +54078,7 @@ var Auth = class {
|
|
|
54066
54078
|
}
|
|
54067
54079
|
const { message } = await this.generateHandshake(address);
|
|
54068
54080
|
const signatureResult = await this.wallet.signMessage(message);
|
|
54069
|
-
const signedMessage = typeof signatureResult === "string" ? signatureResult :
|
|
54081
|
+
const signedMessage = typeof signatureResult === "string" ? signatureResult : bytesToBase64(signatureResult);
|
|
54070
54082
|
this.logger.debug("Auth.loginWithWallet called", {
|
|
54071
54083
|
address,
|
|
54072
54084
|
walletMeta: options?.walletMeta
|
|
@@ -54540,11 +54552,11 @@ var Games = class {
|
|
|
54540
54552
|
);
|
|
54541
54553
|
}
|
|
54542
54554
|
const prepared = await this.prepareGameDonation(gameId, amountMinor);
|
|
54543
|
-
const unsignedTx = Transaction2.from(
|
|
54544
|
-
Buffer.from(prepared.transaction, "base64")
|
|
54545
|
-
);
|
|
54555
|
+
const unsignedTx = Transaction2.from(base64ToBytes(prepared.transaction));
|
|
54546
54556
|
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
54547
|
-
const signedTransaction =
|
|
54557
|
+
const signedTransaction = bytesToBase64(
|
|
54558
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
54559
|
+
);
|
|
54548
54560
|
const result = await this.donateToGame(
|
|
54549
54561
|
gameId,
|
|
54550
54562
|
amountMinor,
|
|
@@ -54812,11 +54824,11 @@ var Tips = class {
|
|
|
54812
54824
|
}
|
|
54813
54825
|
const { publicKey: senderAddress } = await this.wallet.loadWallet();
|
|
54814
54826
|
const prepared = await this.prepare({ recipientUsername, amount });
|
|
54815
|
-
const unsignedTx = Transaction2.from(
|
|
54816
|
-
Buffer.from(prepared.transaction, "base64")
|
|
54817
|
-
);
|
|
54827
|
+
const unsignedTx = Transaction2.from(base64ToBytes(prepared.transaction));
|
|
54818
54828
|
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
54819
|
-
const signedTxBase64 =
|
|
54829
|
+
const signedTxBase64 = bytesToBase64(
|
|
54830
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
54831
|
+
);
|
|
54820
54832
|
const transfer = await this.wallet.submitTransfer(
|
|
54821
54833
|
signedTxBase64,
|
|
54822
54834
|
senderAddress,
|
|
@@ -55181,11 +55193,11 @@ var Wallet = class {
|
|
|
55181
55193
|
amount,
|
|
55182
55194
|
token
|
|
55183
55195
|
);
|
|
55184
|
-
const unsignedTx = Transaction2.from(
|
|
55185
|
-
Buffer.from(prepared.transaction, "base64")
|
|
55186
|
-
);
|
|
55196
|
+
const unsignedTx = Transaction2.from(base64ToBytes(prepared.transaction));
|
|
55187
55197
|
const signedTx = await this.signTransaction(unsignedTx);
|
|
55188
|
-
const signedTxBase64 =
|
|
55198
|
+
const signedTxBase64 = bytesToBase64(
|
|
55199
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
55200
|
+
);
|
|
55189
55201
|
const submitted = await this.submitTransfer(
|
|
55190
55202
|
signedTxBase64,
|
|
55191
55203
|
senderAddress,
|
|
@@ -57297,6 +57309,8 @@ var DimClient = class {
|
|
|
57297
57309
|
config;
|
|
57298
57310
|
authenticated = false;
|
|
57299
57311
|
userId = null;
|
|
57312
|
+
eventQueue = [];
|
|
57313
|
+
unsubscribers = [];
|
|
57300
57314
|
constructor(config) {
|
|
57301
57315
|
this.config = config;
|
|
57302
57316
|
const secretKeyBytes = esm_default2.decode(config.walletPrivateKey);
|
|
@@ -57352,6 +57366,79 @@ var DimClient = class {
|
|
|
57352
57366
|
async ensureConnected(timeoutMs = 1e4) {
|
|
57353
57367
|
await this.sdk.ensureWebSocketConnected(timeoutMs);
|
|
57354
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
|
+
}
|
|
57355
57442
|
getKeypair() {
|
|
57356
57443
|
return this.keypair;
|
|
57357
57444
|
}
|
|
@@ -57365,8 +57452,8 @@ function getPluginConfig(api) {
|
|
|
57365
57452
|
return dimclawEntry?.config ?? null;
|
|
57366
57453
|
}
|
|
57367
57454
|
function resolveStorePath(storePath) {
|
|
57368
|
-
const expanded = storePath.startsWith("~/") && process.env.HOME ?
|
|
57369
|
-
return
|
|
57455
|
+
const expanded = storePath.startsWith("~/") && process.env.HOME ? path2.join(process.env.HOME, storePath.slice(2)) : storePath;
|
|
57456
|
+
return path2.resolve(expanded);
|
|
57370
57457
|
}
|
|
57371
57458
|
async function readWalletFile(storePath) {
|
|
57372
57459
|
try {
|
|
@@ -57386,14 +57473,14 @@ async function readWalletFile(storePath) {
|
|
|
57386
57473
|
}
|
|
57387
57474
|
}
|
|
57388
57475
|
async function writeWalletFile(storePath, record3) {
|
|
57389
|
-
await
|
|
57476
|
+
await mkdir2(path2.dirname(storePath), { recursive: true });
|
|
57390
57477
|
const tmp = `${storePath}.tmp`;
|
|
57391
|
-
await
|
|
57478
|
+
await writeFile2(tmp, `${JSON.stringify(record3, null, 2)}
|
|
57392
57479
|
`, {
|
|
57393
57480
|
encoding: "utf8",
|
|
57394
57481
|
mode: 384
|
|
57395
57482
|
});
|
|
57396
|
-
await
|
|
57483
|
+
await rename2(tmp, storePath);
|
|
57397
57484
|
}
|
|
57398
57485
|
function createWalletRecord() {
|
|
57399
57486
|
const keypair = Keypair.generate();
|
|
@@ -57453,6 +57540,7 @@ function register(api) {
|
|
|
57453
57540
|
if ("error" in c) return c.error;
|
|
57454
57541
|
try {
|
|
57455
57542
|
const result = await c.authenticate();
|
|
57543
|
+
c.startEventListeners();
|
|
57456
57544
|
const nextSteps = [];
|
|
57457
57545
|
if (result.username == null || result.username === "") {
|
|
57458
57546
|
nextSteps.push(
|
|
@@ -57684,7 +57772,15 @@ function register(api) {
|
|
|
57684
57772
|
name: "dim_redeem_shares",
|
|
57685
57773
|
description: "Redeem shares after market resolution."
|
|
57686
57774
|
},
|
|
57687
|
-
{ name: "dim_get_market_analytics", description: "Get market analytics." }
|
|
57775
|
+
{ name: "dim_get_market_analytics", description: "Get market analytics." },
|
|
57776
|
+
{
|
|
57777
|
+
name: "dim_get_pending_events",
|
|
57778
|
+
description: "Drain buffered real-time events (DMs, challenges, game turns). Call regularly."
|
|
57779
|
+
},
|
|
57780
|
+
{
|
|
57781
|
+
name: "dim_check_notifications",
|
|
57782
|
+
description: "Check unread notifications, DMs, and friend requests in one call."
|
|
57783
|
+
}
|
|
57688
57784
|
];
|
|
57689
57785
|
api.registerTool({
|
|
57690
57786
|
name: "dim_list_instructions",
|
|
@@ -59178,6 +59274,72 @@ function register(api) {
|
|
|
59178
59274
|
}
|
|
59179
59275
|
}
|
|
59180
59276
|
});
|
|
59277
|
+
api.registerTool({
|
|
59278
|
+
name: "dim_get_pending_events",
|
|
59279
|
+
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.",
|
|
59280
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
59281
|
+
async execute() {
|
|
59282
|
+
const c = await requireClient();
|
|
59283
|
+
if ("error" in c) return c.error;
|
|
59284
|
+
try {
|
|
59285
|
+
const events = c.drainEvents();
|
|
59286
|
+
return textResult(
|
|
59287
|
+
JSON.stringify(
|
|
59288
|
+
{
|
|
59289
|
+
count: events.length,
|
|
59290
|
+
events,
|
|
59291
|
+
hint: events.length === 0 ? "No new events since last check." : "Process these events and take action as needed."
|
|
59292
|
+
},
|
|
59293
|
+
null,
|
|
59294
|
+
2
|
|
59295
|
+
)
|
|
59296
|
+
);
|
|
59297
|
+
} catch (err) {
|
|
59298
|
+
return textResult(
|
|
59299
|
+
`Failed to get pending events: ${err instanceof Error ? err.message : String(err)}`,
|
|
59300
|
+
true
|
|
59301
|
+
);
|
|
59302
|
+
}
|
|
59303
|
+
}
|
|
59304
|
+
});
|
|
59305
|
+
api.registerTool({
|
|
59306
|
+
name: "dim_check_notifications",
|
|
59307
|
+
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.",
|
|
59308
|
+
parameters: { type: "object", properties: {}, additionalProperties: false },
|
|
59309
|
+
async execute() {
|
|
59310
|
+
const c = await requireClient();
|
|
59311
|
+
if ("error" in c) return c.error;
|
|
59312
|
+
try {
|
|
59313
|
+
const [notifications, dmThreads, friendRequests] = await Promise.all([
|
|
59314
|
+
c.sdk.notifications.list({ page: 1, limit: 20 }),
|
|
59315
|
+
c.sdk.chat.listDmThreads(),
|
|
59316
|
+
c.sdk.users.getIncomingFriendRequests()
|
|
59317
|
+
]);
|
|
59318
|
+
const unreadDms = dmThreads.filter(
|
|
59319
|
+
(t) => (t.unreadCount ?? 0) > 0
|
|
59320
|
+
);
|
|
59321
|
+
return textResult(
|
|
59322
|
+
JSON.stringify(
|
|
59323
|
+
{
|
|
59324
|
+
unreadNotificationCount: notifications.unreadCount,
|
|
59325
|
+
notifications: notifications.notifications.filter((n) => !n.read),
|
|
59326
|
+
unreadDmThreads: unreadDms,
|
|
59327
|
+
incomingFriendRequests: friendRequests,
|
|
59328
|
+
pendingWsEvents: c.pendingEventCount,
|
|
59329
|
+
hint: "Use dim_get_pending_events to drain buffered real-time events."
|
|
59330
|
+
},
|
|
59331
|
+
null,
|
|
59332
|
+
2
|
|
59333
|
+
)
|
|
59334
|
+
);
|
|
59335
|
+
} catch (err) {
|
|
59336
|
+
return textResult(
|
|
59337
|
+
`Failed to check notifications: ${err instanceof Error ? err.message : String(err)}`,
|
|
59338
|
+
true
|
|
59339
|
+
);
|
|
59340
|
+
}
|
|
59341
|
+
}
|
|
59342
|
+
});
|
|
59181
59343
|
}
|
|
59182
59344
|
export {
|
|
59183
59345
|
register as default
|
package/index.ts
CHANGED
|
@@ -157,6 +157,7 @@ export default function register(api: {
|
|
|
157
157
|
if ('error' in c) return c.error;
|
|
158
158
|
try {
|
|
159
159
|
const result = await c.authenticate();
|
|
160
|
+
c.startEventListeners();
|
|
160
161
|
const nextSteps: string[] = [];
|
|
161
162
|
if (result.username == null || result.username === '') {
|
|
162
163
|
nextSteps.push(
|
|
@@ -345,7 +346,8 @@ export default function register(api: {
|
|
|
345
346
|
},
|
|
346
347
|
{
|
|
347
348
|
name: 'dim_join_queue',
|
|
348
|
-
description:
|
|
349
|
+
description:
|
|
350
|
+
'Join matchmaking queue. If not matched immediately, poll dim_get_lobby until gameId appears.',
|
|
349
351
|
},
|
|
350
352
|
{ name: 'dim_get_lobby', description: 'Get lobby details by ID.' },
|
|
351
353
|
{
|
|
@@ -403,6 +405,16 @@ export default function register(api: {
|
|
|
403
405
|
description: 'Redeem shares after market resolution.',
|
|
404
406
|
},
|
|
405
407
|
{ name: 'dim_get_market_analytics', description: 'Get market analytics.' },
|
|
408
|
+
{
|
|
409
|
+
name: 'dim_get_pending_events',
|
|
410
|
+
description:
|
|
411
|
+
'Drain buffered real-time events (DMs, challenges, game turns). Call regularly.',
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: 'dim_check_notifications',
|
|
415
|
+
description:
|
|
416
|
+
'Check unread notifications, DMs, and friend requests in one call.',
|
|
417
|
+
},
|
|
406
418
|
];
|
|
407
419
|
api.registerTool({
|
|
408
420
|
name: 'dim_list_instructions',
|
|
@@ -2026,4 +2038,79 @@ export default function register(api: {
|
|
|
2026
2038
|
}
|
|
2027
2039
|
},
|
|
2028
2040
|
});
|
|
2041
|
+
|
|
2042
|
+
// ── Real-time event awareness ─────────────────────────────────────────
|
|
2043
|
+
|
|
2044
|
+
api.registerTool({
|
|
2045
|
+
name: 'dim_get_pending_events',
|
|
2046
|
+
description:
|
|
2047
|
+
'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.',
|
|
2048
|
+
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
2049
|
+
async execute() {
|
|
2050
|
+
const c = await requireClient();
|
|
2051
|
+
if ('error' in c) return c.error;
|
|
2052
|
+
try {
|
|
2053
|
+
const events = c.drainEvents();
|
|
2054
|
+
return textResult(
|
|
2055
|
+
JSON.stringify(
|
|
2056
|
+
{
|
|
2057
|
+
count: events.length,
|
|
2058
|
+
events,
|
|
2059
|
+
hint:
|
|
2060
|
+
events.length === 0
|
|
2061
|
+
? 'No new events since last check.'
|
|
2062
|
+
: 'Process these events and take action as needed.',
|
|
2063
|
+
},
|
|
2064
|
+
null,
|
|
2065
|
+
2,
|
|
2066
|
+
),
|
|
2067
|
+
);
|
|
2068
|
+
} catch (err) {
|
|
2069
|
+
return textResult(
|
|
2070
|
+
`Failed to get pending events: ${err instanceof Error ? err.message : String(err)}`,
|
|
2071
|
+
true,
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
},
|
|
2075
|
+
});
|
|
2076
|
+
|
|
2077
|
+
api.registerTool({
|
|
2078
|
+
name: 'dim_check_notifications',
|
|
2079
|
+
description:
|
|
2080
|
+
'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.',
|
|
2081
|
+
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
2082
|
+
async execute() {
|
|
2083
|
+
const c = await requireClient();
|
|
2084
|
+
if ('error' in c) return c.error;
|
|
2085
|
+
try {
|
|
2086
|
+
const [notifications, dmThreads, friendRequests] = await Promise.all([
|
|
2087
|
+
c.sdk.notifications.list({ page: 1, limit: 20 }),
|
|
2088
|
+
c.sdk.chat.listDmThreads(),
|
|
2089
|
+
c.sdk.users.getIncomingFriendRequests(),
|
|
2090
|
+
]);
|
|
2091
|
+
const unreadDms = (dmThreads as Array<{ unreadCount?: number }>).filter(
|
|
2092
|
+
(t) => (t.unreadCount ?? 0) > 0,
|
|
2093
|
+
);
|
|
2094
|
+
return textResult(
|
|
2095
|
+
JSON.stringify(
|
|
2096
|
+
{
|
|
2097
|
+
unreadNotificationCount: notifications.unreadCount,
|
|
2098
|
+
notifications: notifications.notifications.filter((n) => !n.read),
|
|
2099
|
+
unreadDmThreads: unreadDms,
|
|
2100
|
+
incomingFriendRequests: friendRequests,
|
|
2101
|
+
pendingWsEvents: c.pendingEventCount,
|
|
2102
|
+
hint: 'Use dim_get_pending_events to drain buffered real-time events.',
|
|
2103
|
+
},
|
|
2104
|
+
null,
|
|
2105
|
+
2,
|
|
2106
|
+
),
|
|
2107
|
+
);
|
|
2108
|
+
} catch (err) {
|
|
2109
|
+
return textResult(
|
|
2110
|
+
`Failed to check notifications: ${err instanceof Error ? err.message : String(err)}`,
|
|
2111
|
+
true,
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
},
|
|
2115
|
+
});
|
|
2029
2116
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
},
|
|
14
14
|
"apiUrl": {
|
|
15
15
|
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
"heartbeatPath": {
|
|
18
|
+
"type": "string"
|
|
16
19
|
}
|
|
17
20
|
},
|
|
18
21
|
"required": []
|
|
@@ -29,6 +32,10 @@
|
|
|
29
32
|
"apiUrl": {
|
|
30
33
|
"label": "API URL",
|
|
31
34
|
"placeholder": "https://api.dim.cool"
|
|
35
|
+
},
|
|
36
|
+
"heartbeatPath": {
|
|
37
|
+
"label": "Heartbeat file path",
|
|
38
|
+
"placeholder": "~/.openclaw/workspace/HEARTBEAT.md"
|
|
32
39
|
}
|
|
33
40
|
},
|
|
34
41
|
"setupHint": "After install, add your Solana wallet private key to plugins.entries.dimclaw.config.walletPrivateKey, then call dim_login to get started.",
|
package/package.json
CHANGED