@dimcool/dimclaw 0.1.26 → 0.1.27
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 +292 -53
- package/index.ts +16 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33236,19 +33236,18 @@ var Lobbies = class {
|
|
|
33236
33236
|
return this.http.delete(`/lobbies/admin/${lobbyId}`);
|
|
33237
33237
|
}
|
|
33238
33238
|
/**
|
|
33239
|
-
* Play again: Create a new lobby
|
|
33240
|
-
* Returns the lobby and unsigned transaction that needs to be signed
|
|
33239
|
+
* Play again: Create a new lobby and prepare deposit in one flow.
|
|
33240
|
+
* Returns the lobby and unsigned transaction that needs to be signed.
|
|
33241
33241
|
* @param gameType - The game type to play again
|
|
33242
33242
|
* @param betAmount - The bet amount (same as previous game)
|
|
33243
33243
|
* @param escrow - The escrow service instance (from sdk.escrow)
|
|
33244
33244
|
*/
|
|
33245
33245
|
async playAgain(gameType, betAmount, escrow) {
|
|
33246
33246
|
const lobby = await this.createLobby(gameType, betAmount);
|
|
33247
|
-
await escrow.
|
|
33248
|
-
const prepareResponse = await escrow.prepareDepositTransaction(lobby.id);
|
|
33247
|
+
const { transaction } = await escrow.prepareAndStartDeposit(lobby.id);
|
|
33249
33248
|
return {
|
|
33250
33249
|
lobby,
|
|
33251
|
-
unsignedTransaction:
|
|
33250
|
+
unsignedTransaction: transaction
|
|
33252
33251
|
};
|
|
33253
33252
|
}
|
|
33254
33253
|
};
|
|
@@ -34112,6 +34111,17 @@ var Escrow = class {
|
|
|
34112
34111
|
`/escrow/lobby/${lobbyId}/deposit/prepare`
|
|
34113
34112
|
);
|
|
34114
34113
|
}
|
|
34114
|
+
/**
|
|
34115
|
+
* Combined prepare-and-start: validates lobby, transitions waiting→preparing,
|
|
34116
|
+
* initializes depositStatus, and prepares the unsigned transaction in one call.
|
|
34117
|
+
* Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
|
|
34118
|
+
*/
|
|
34119
|
+
async prepareAndStartDeposit(lobbyId) {
|
|
34120
|
+
return this.http.post(
|
|
34121
|
+
`/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
|
|
34122
|
+
{}
|
|
34123
|
+
);
|
|
34124
|
+
}
|
|
34115
34125
|
/**
|
|
34116
34126
|
* Submit a signed deposit transaction
|
|
34117
34127
|
* The transaction will be submitted to the Solana network and confirmed
|
|
@@ -34142,7 +34152,7 @@ var Escrow = class {
|
|
|
34142
34152
|
);
|
|
34143
34153
|
}
|
|
34144
34154
|
/**
|
|
34145
|
-
* One-call lobby deposit: start
|
|
34155
|
+
* One-call lobby deposit: prepare-and-start, sign, submit, and poll until
|
|
34146
34156
|
* the current user's deposit is confirmed. Requires sdk.wallet.setSigner() to be set.
|
|
34147
34157
|
* Returns when all required deposits are confirmed and the lobby can proceed to queue.
|
|
34148
34158
|
*
|
|
@@ -34154,8 +34164,7 @@ var Escrow = class {
|
|
|
34154
34164
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
34155
34165
|
);
|
|
34156
34166
|
}
|
|
34157
|
-
await this.
|
|
34158
|
-
const { transaction } = await this.prepareDepositTransaction(lobbyId);
|
|
34167
|
+
const { transaction } = await this.prepareAndStartDeposit(lobbyId);
|
|
34159
34168
|
const unsignedTx = Transaction.from(base64ToBytes(transaction));
|
|
34160
34169
|
let signature;
|
|
34161
34170
|
if (this.wallet.isSignAndSendMode()) {
|
|
@@ -34177,7 +34186,7 @@ var Escrow = class {
|
|
|
34177
34186
|
if (status.allConfirmed && status.canProceedToQueue) {
|
|
34178
34187
|
return {
|
|
34179
34188
|
signature,
|
|
34180
|
-
status: "
|
|
34189
|
+
status: "confirmed",
|
|
34181
34190
|
canProceedToQueue: true
|
|
34182
34191
|
};
|
|
34183
34192
|
}
|
|
@@ -34189,6 +34198,39 @@ var Escrow = class {
|
|
|
34189
34198
|
canProceedToQueue: false
|
|
34190
34199
|
};
|
|
34191
34200
|
}
|
|
34201
|
+
/**
|
|
34202
|
+
* Zero-polling deposit variant using server-side awaitConfirmation.
|
|
34203
|
+
* 2 HTTP calls total, 0 client-side polling. Ideal for agents.
|
|
34204
|
+
*
|
|
34205
|
+
* Automatically uses signAndSendTransaction or signTransaction based on signer capability.
|
|
34206
|
+
*/
|
|
34207
|
+
async depositForLobbySync(lobbyId) {
|
|
34208
|
+
if (!this.wallet.hasSigner()) {
|
|
34209
|
+
throw new Error(
|
|
34210
|
+
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
34211
|
+
);
|
|
34212
|
+
}
|
|
34213
|
+
const { transaction } = await this.prepareAndStartDeposit(lobbyId);
|
|
34214
|
+
const unsignedTx = Transaction.from(base64ToBytes(transaction));
|
|
34215
|
+
let signature;
|
|
34216
|
+
if (this.wallet.isSignAndSendMode()) {
|
|
34217
|
+
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
34218
|
+
await this.http.post(
|
|
34219
|
+
`/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
|
|
34220
|
+
{ signature }
|
|
34221
|
+
);
|
|
34222
|
+
} else {
|
|
34223
|
+
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
34224
|
+
const signedBase64 = bytesToBase64(
|
|
34225
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
34226
|
+
);
|
|
34227
|
+
const result = await this.http.post(`/escrow/lobby/${lobbyId}/deposit/submit?awaitConfirmation=true`, {
|
|
34228
|
+
signedTransaction: signedBase64
|
|
34229
|
+
});
|
|
34230
|
+
signature = result.signature;
|
|
34231
|
+
}
|
|
34232
|
+
return { signature, status: "confirmed", canProceedToQueue: true };
|
|
34233
|
+
}
|
|
34192
34234
|
async claimLobbyDepositRefund(lobbyId, depositSignature) {
|
|
34193
34235
|
return this.http.post(
|
|
34194
34236
|
`/escrow/lobby/${lobbyId}/deposit/${depositSignature}/claim-refund`,
|
|
@@ -34201,33 +34243,6 @@ var Escrow = class {
|
|
|
34201
34243
|
{}
|
|
34202
34244
|
);
|
|
34203
34245
|
}
|
|
34204
|
-
/**
|
|
34205
|
-
* Submit a signed deposit transaction and wait until the lobby is queued/active.
|
|
34206
|
-
*
|
|
34207
|
-
* Note: the backend may auto-transition the lobby to the queue once deposits are confirmed.
|
|
34208
|
-
* In that case, calling /join-queue from the client would be redundant and can fail with
|
|
34209
|
-
* "Current status: queued".
|
|
34210
|
-
* @param lobbyId - The lobby ID
|
|
34211
|
-
* @param signedTransaction - The signed transaction (base64 encoded)
|
|
34212
|
-
* @param lobbies - The lobbies service instance (from sdk.lobbies) to read lobby status
|
|
34213
|
-
*/
|
|
34214
|
-
async submitDepositAndJoinQueue(lobbyId, signedTransaction, lobbies) {
|
|
34215
|
-
await this.submitDeposit(lobbyId, signedTransaction);
|
|
34216
|
-
const MAX_WAIT_TIME = 3e4;
|
|
34217
|
-
const POLL_INTERVAL = 1e3;
|
|
34218
|
-
const startTime = Date.now();
|
|
34219
|
-
while (Date.now() - startTime < MAX_WAIT_TIME) {
|
|
34220
|
-
const status = await this.getDepositStatus(lobbyId);
|
|
34221
|
-
if (status.allConfirmed && status.canProceedToQueue) {
|
|
34222
|
-
const lobby = await lobbies.getLobby(lobbyId);
|
|
34223
|
-
if (lobby.status === "queued" || lobby.status === "active") {
|
|
34224
|
-
return lobby;
|
|
34225
|
-
}
|
|
34226
|
-
}
|
|
34227
|
-
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
|
|
34228
|
-
}
|
|
34229
|
-
throw new Error("Deposit confirmation timeout");
|
|
34230
|
-
}
|
|
34231
34246
|
};
|
|
34232
34247
|
var Daily = class {
|
|
34233
34248
|
constructor(http2, logger2 = logger) {
|
|
@@ -34854,8 +34869,14 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
34854
34869
|
connecting: false,
|
|
34855
34870
|
error: null
|
|
34856
34871
|
});
|
|
34872
|
+
const wasReconnect = this.reconnectAttempts > 0;
|
|
34857
34873
|
this.reconnectAttempts = 0;
|
|
34858
34874
|
this.onReconnect();
|
|
34875
|
+
if (wasReconnect) {
|
|
34876
|
+
this.dispatchEvent("connection:reconnected", {
|
|
34877
|
+
timestamp: Date.now()
|
|
34878
|
+
});
|
|
34879
|
+
}
|
|
34859
34880
|
});
|
|
34860
34881
|
socket.on("disconnect", (reason) => {
|
|
34861
34882
|
this.updateConnectionState({ connected: false, connecting: false });
|
|
@@ -34997,7 +35018,8 @@ function createLobbyStore(transport) {
|
|
|
34997
35018
|
const store = createSdkStore({
|
|
34998
35019
|
lobbiesById: {},
|
|
34999
35020
|
matchedEvent: null,
|
|
35000
|
-
typingByLobbyId: {}
|
|
35021
|
+
typingByLobbyId: {},
|
|
35022
|
+
depositStatusByLobbyId: {}
|
|
35001
35023
|
});
|
|
35002
35024
|
const setBaseState = (lobbies) => {
|
|
35003
35025
|
const lobbiesById = {};
|
|
@@ -35077,6 +35099,15 @@ function createLobbyStore(transport) {
|
|
|
35077
35099
|
};
|
|
35078
35100
|
});
|
|
35079
35101
|
break;
|
|
35102
|
+
case "lobby:deposit:updated":
|
|
35103
|
+
store.updateState((state) => ({
|
|
35104
|
+
...state,
|
|
35105
|
+
depositStatusByLobbyId: {
|
|
35106
|
+
...state.depositStatusByLobbyId,
|
|
35107
|
+
[event.payload.lobbyId]: event.payload
|
|
35108
|
+
}
|
|
35109
|
+
}));
|
|
35110
|
+
break;
|
|
35080
35111
|
case "lobby:typing:start":
|
|
35081
35112
|
case "lobby:typing:stop":
|
|
35082
35113
|
store.updateState((state) => {
|
|
@@ -35105,12 +35136,22 @@ function createLobbyStore(transport) {
|
|
|
35105
35136
|
}
|
|
35106
35137
|
}
|
|
35107
35138
|
);
|
|
35139
|
+
const subscribeDepositUpdate = (lobbyId, callback) => store.subscribeSelector(
|
|
35140
|
+
(state) => state.depositStatusByLobbyId[lobbyId],
|
|
35141
|
+
() => {
|
|
35142
|
+
const data = store.getState().depositStatusByLobbyId[lobbyId];
|
|
35143
|
+
if (data) {
|
|
35144
|
+
callback(data);
|
|
35145
|
+
}
|
|
35146
|
+
}
|
|
35147
|
+
);
|
|
35108
35148
|
return {
|
|
35109
35149
|
store,
|
|
35110
35150
|
setBaseState,
|
|
35111
35151
|
applyWsEvent,
|
|
35112
35152
|
joinLobby,
|
|
35113
|
-
subscribeMatched
|
|
35153
|
+
subscribeMatched,
|
|
35154
|
+
subscribeDepositUpdate
|
|
35114
35155
|
};
|
|
35115
35156
|
}
|
|
35116
35157
|
function createGameStore(transport) {
|
|
@@ -35392,7 +35433,6 @@ function createGameActionsStore(transport) {
|
|
|
35392
35433
|
...current,
|
|
35393
35434
|
roundState: {
|
|
35394
35435
|
...current.roundState,
|
|
35395
|
-
phase: "completed",
|
|
35396
35436
|
timeRemaining: 0,
|
|
35397
35437
|
actions: timedOutUser ? {
|
|
35398
35438
|
...current.roundState.actions,
|
|
@@ -35904,6 +35944,7 @@ var WS_EVENT_NAMES = [
|
|
|
35904
35944
|
"lobby:queue:joined",
|
|
35905
35945
|
"lobby:queue:cancelled",
|
|
35906
35946
|
"lobby:matched",
|
|
35947
|
+
"lobby:deposit:updated",
|
|
35907
35948
|
"lobby:typing:start",
|
|
35908
35949
|
"lobby:typing:stop",
|
|
35909
35950
|
"game:updated",
|
|
@@ -35980,6 +36021,11 @@ function decodeWsEvent(eventName, payload) {
|
|
|
35980
36021
|
event: "lobby:matched",
|
|
35981
36022
|
payload
|
|
35982
36023
|
};
|
|
36024
|
+
case "lobby:deposit:updated":
|
|
36025
|
+
return {
|
|
36026
|
+
event: "lobby:deposit:updated",
|
|
36027
|
+
payload
|
|
36028
|
+
};
|
|
35983
36029
|
case "lobby:typing:start":
|
|
35984
36030
|
case "lobby:typing:stop":
|
|
35985
36031
|
return {
|
|
@@ -36569,6 +36615,17 @@ var SAFETY_RULES = [
|
|
|
36569
36615
|
];
|
|
36570
36616
|
|
|
36571
36617
|
// ../dim-agent-core/src/tools/auth.ts
|
|
36618
|
+
var WALLET_LINKAGE_NOTE = "DIM is noncustodial. Your DIM wallet is the Solana address shown here; it was configured with a base58 private key at setup. Signing is done locally where this instance runs, using that private key and DIM's wallet package.";
|
|
36619
|
+
async function getSpectateUrl(client) {
|
|
36620
|
+
if (!client.currentUserId) return null;
|
|
36621
|
+
try {
|
|
36622
|
+
const user = await client.sdk.users.getUserById(client.currentUserId);
|
|
36623
|
+
const u = user;
|
|
36624
|
+
return u?.username ? `https://dim.cool/spectate/${u.username}` : null;
|
|
36625
|
+
} catch {
|
|
36626
|
+
return null;
|
|
36627
|
+
}
|
|
36628
|
+
}
|
|
36572
36629
|
async function login(client) {
|
|
36573
36630
|
try {
|
|
36574
36631
|
const result = await client.authenticate();
|
|
@@ -36598,6 +36655,7 @@ async function login(client) {
|
|
|
36598
36655
|
userId: result.userId,
|
|
36599
36656
|
username: result.username ?? null,
|
|
36600
36657
|
walletAddress: client.walletAddress,
|
|
36658
|
+
walletNote: WALLET_LINKAGE_NOTE,
|
|
36601
36659
|
safetyRules: SAFETY_RULES,
|
|
36602
36660
|
nextSteps
|
|
36603
36661
|
};
|
|
@@ -36631,7 +36689,11 @@ async function getProfile(client) {
|
|
|
36631
36689
|
};
|
|
36632
36690
|
}
|
|
36633
36691
|
const user = await client.sdk.users.getUserById(client.currentUserId);
|
|
36634
|
-
|
|
36692
|
+
const profile = {
|
|
36693
|
+
...typeof user === "object" && user !== null ? user : {},
|
|
36694
|
+
walletAddress: client.walletAddress
|
|
36695
|
+
};
|
|
36696
|
+
return { data: profile };
|
|
36635
36697
|
} catch (error) {
|
|
36636
36698
|
return {
|
|
36637
36699
|
error: `Failed to get profile: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -36832,12 +36894,31 @@ async function listDmThreads(client) {
|
|
|
36832
36894
|
}
|
|
36833
36895
|
|
|
36834
36896
|
// ../dim-agent-core/src/tools/wallet.ts
|
|
36897
|
+
async function getWalletAddress(client) {
|
|
36898
|
+
try {
|
|
36899
|
+
return {
|
|
36900
|
+
data: {
|
|
36901
|
+
walletAddress: client.walletAddress,
|
|
36902
|
+
walletNote: WALLET_LINKAGE_NOTE
|
|
36903
|
+
}
|
|
36904
|
+
};
|
|
36905
|
+
} catch (error) {
|
|
36906
|
+
return {
|
|
36907
|
+
error: `Failed to get wallet address: ${error instanceof Error ? error.message : String(error)}`,
|
|
36908
|
+
isError: true
|
|
36909
|
+
};
|
|
36910
|
+
}
|
|
36911
|
+
}
|
|
36835
36912
|
async function getBalance(client) {
|
|
36836
36913
|
try {
|
|
36837
36914
|
const balance = await client.sdk.wallet.getBalances();
|
|
36838
36915
|
const usdcDollars = balance.usdc / 1e6;
|
|
36839
36916
|
return {
|
|
36840
|
-
data: {
|
|
36917
|
+
data: {
|
|
36918
|
+
...balance,
|
|
36919
|
+
usdcFormatted: `$${usdcDollars.toFixed(2)}`,
|
|
36920
|
+
walletAddress: client.walletAddress
|
|
36921
|
+
}
|
|
36841
36922
|
};
|
|
36842
36923
|
} catch (error) {
|
|
36843
36924
|
return {
|
|
@@ -36904,7 +36985,10 @@ async function getWalletActivity(client, args) {
|
|
|
36904
36985
|
limit: args.limit || 20
|
|
36905
36986
|
});
|
|
36906
36987
|
const claimable = activity.items.filter((i) => i.claimable === true);
|
|
36907
|
-
const data = {
|
|
36988
|
+
const data = {
|
|
36989
|
+
...activity,
|
|
36990
|
+
walletAddress: client.walletAddress
|
|
36991
|
+
};
|
|
36908
36992
|
if (claimable.length > 0) {
|
|
36909
36993
|
data.hint = `You have ${claimable.length} claimable item(s). Call dim_claim_funds with the activityId to claim each one.`;
|
|
36910
36994
|
data.claimableItems = claimable.map((i) => ({
|
|
@@ -37113,9 +37197,8 @@ function buildGameOverHint(state) {
|
|
|
37113
37197
|
const winnerId = state.winnerId;
|
|
37114
37198
|
const wonAmount = state.wonAmount;
|
|
37115
37199
|
const isDraw = state.draw === true || winnerId == null;
|
|
37116
|
-
|
|
37117
|
-
|
|
37118
|
-
return `Game over. Winner: ${winnerId}.${amountStr}`;
|
|
37200
|
+
const result = isDraw ? "Game over \u2014 it was a draw." : `Game over. Winner: ${winnerId}.${wonAmount != null ? ` Won $${(wonAmount / 1e6).toFixed(2)}.` : ""}`;
|
|
37201
|
+
return `${result} You can request a rematch with dim_request_rematch, or check dim_check_notifications for new activity.`;
|
|
37119
37202
|
}
|
|
37120
37203
|
async function fetchNewGameChat(client, gameId) {
|
|
37121
37204
|
try {
|
|
@@ -37223,13 +37306,13 @@ async function createLobby(client, args) {
|
|
|
37223
37306
|
}
|
|
37224
37307
|
async function depositForLobby(client, args) {
|
|
37225
37308
|
try {
|
|
37226
|
-
const result = await client.sdk.escrow.
|
|
37309
|
+
const result = await client.sdk.escrow.depositForLobbySync(args.lobbyId);
|
|
37227
37310
|
return {
|
|
37228
37311
|
data: {
|
|
37229
37312
|
signature: result.signature,
|
|
37230
37313
|
status: result.status,
|
|
37231
37314
|
canProceedToQueue: result.canProceedToQueue,
|
|
37232
|
-
hint:
|
|
37315
|
+
hint: "Deposit confirmed. The server auto-joins the queue when all players have deposited."
|
|
37233
37316
|
}
|
|
37234
37317
|
};
|
|
37235
37318
|
} catch (error) {
|
|
@@ -37260,13 +37343,15 @@ async function joinQueue(client, args) {
|
|
|
37260
37343
|
await client.ensureConnected();
|
|
37261
37344
|
const lobby = await client.sdk.lobbies.joinQueue(args.lobbyId);
|
|
37262
37345
|
const matched = lobby.status === "active" && lobby.gameId;
|
|
37346
|
+
const spectateUrl = matched ? await getSpectateUrl(client) : null;
|
|
37263
37347
|
return {
|
|
37264
37348
|
data: {
|
|
37265
37349
|
lobbyId: lobby.id,
|
|
37266
37350
|
status: lobby.status,
|
|
37267
37351
|
gameId: lobby.gameId || null,
|
|
37268
37352
|
matched: !!matched,
|
|
37269
|
-
|
|
37353
|
+
...spectateUrl && { spectateUrl },
|
|
37354
|
+
hint: matched ? `Game started! Call dim_game_loop with gameId "${lobby.gameId}" to play.${spectateUrl ? ` Tell the user they can spectate at ${spectateUrl}` : ""}` : "Waiting for an opponent. Poll dim_get_lobby to check for a match."
|
|
37270
37355
|
}
|
|
37271
37356
|
};
|
|
37272
37357
|
} catch (error) {
|
|
@@ -37448,6 +37533,38 @@ async function inviteToLobby(client, args) {
|
|
|
37448
37533
|
};
|
|
37449
37534
|
}
|
|
37450
37535
|
}
|
|
37536
|
+
async function requestRematch(client, args) {
|
|
37537
|
+
try {
|
|
37538
|
+
const result = await client.sdk.games.requestRematch(args.gameId);
|
|
37539
|
+
return {
|
|
37540
|
+
data: {
|
|
37541
|
+
...result,
|
|
37542
|
+
hint: result.bothReady ? `Both players want a rematch! A lobby is being created.${result.newLobbyId ? ` Lobby ID: ${result.newLobbyId}.` : ""} Check dim_check_notifications or call dim_game_loop with the new gameId once the game starts.` : "Rematch requested. Waiting for opponent to accept. Call dim_check_notifications to see if they respond."
|
|
37543
|
+
}
|
|
37544
|
+
};
|
|
37545
|
+
} catch (error) {
|
|
37546
|
+
return {
|
|
37547
|
+
error: `Failed to request rematch: ${error instanceof Error ? error.message : String(error)}`,
|
|
37548
|
+
isError: true
|
|
37549
|
+
};
|
|
37550
|
+
}
|
|
37551
|
+
}
|
|
37552
|
+
async function acceptRematch(client, args) {
|
|
37553
|
+
try {
|
|
37554
|
+
const result = await client.sdk.games.requestRematch(args.gameId);
|
|
37555
|
+
return {
|
|
37556
|
+
data: {
|
|
37557
|
+
...result,
|
|
37558
|
+
hint: result.bothReady ? `Rematch accepted! A lobby has been created.${result.newLobbyId ? ` Lobby ID: ${result.newLobbyId}.` : ""} Proceed with deposit and game flow.` : "Your rematch acceptance was recorded. Waiting for the other player."
|
|
37559
|
+
}
|
|
37560
|
+
};
|
|
37561
|
+
} catch (error) {
|
|
37562
|
+
return {
|
|
37563
|
+
error: `Failed to accept rematch: ${error instanceof Error ? error.message : String(error)}`,
|
|
37564
|
+
isError: true
|
|
37565
|
+
};
|
|
37566
|
+
}
|
|
37567
|
+
}
|
|
37451
37568
|
function raceTimeout(promise, ms) {
|
|
37452
37569
|
return new Promise((resolve) => {
|
|
37453
37570
|
const timer = setTimeout(resolve, ms);
|
|
@@ -37494,10 +37611,12 @@ async function challengeUser(client, args) {
|
|
|
37494
37611
|
async function acceptChallenge(client, args) {
|
|
37495
37612
|
try {
|
|
37496
37613
|
const result = await client.sdk.challenges.accept(args.challengeId);
|
|
37614
|
+
const spectateUrl = result.gameId ? await getSpectateUrl(client) : null;
|
|
37497
37615
|
return {
|
|
37498
37616
|
data: {
|
|
37499
37617
|
...result,
|
|
37500
|
-
|
|
37618
|
+
...spectateUrl && { spectateUrl },
|
|
37619
|
+
hint: result.gameId ? `Game started! Call dim_game_loop with gameId "${result.gameId}".${spectateUrl ? ` Tell the user they can spectate at ${spectateUrl}` : ""}` : `Lobby created: ${result.lobbyId}. Use dim_join_queue to start matchmaking.`
|
|
37501
37620
|
}
|
|
37502
37621
|
};
|
|
37503
37622
|
} catch (error) {
|
|
@@ -38033,6 +38152,69 @@ async function getAgentConfig(client) {
|
|
|
38033
38152
|
}
|
|
38034
38153
|
}
|
|
38035
38154
|
|
|
38155
|
+
// ../dim-agent-core/src/tools/users.ts
|
|
38156
|
+
function formatMinor(m) {
|
|
38157
|
+
if (m == null) return "\u2014";
|
|
38158
|
+
const n = typeof m === "bigint" ? Number(m) : m;
|
|
38159
|
+
return `$${(n / 1e6).toFixed(2)}`;
|
|
38160
|
+
}
|
|
38161
|
+
async function getGameHistory(client, args) {
|
|
38162
|
+
try {
|
|
38163
|
+
if (!client.currentUserId) {
|
|
38164
|
+
return {
|
|
38165
|
+
error: "Not authenticated. Call dim_login first.",
|
|
38166
|
+
isError: true
|
|
38167
|
+
};
|
|
38168
|
+
}
|
|
38169
|
+
const raw = await client.sdk.users.getGameHistory(client.currentUserId);
|
|
38170
|
+
const limit = Math.min(args.limit ?? 50, 100);
|
|
38171
|
+
const items = raw.slice(0, limit).map((item) => ({
|
|
38172
|
+
...item,
|
|
38173
|
+
betFormatted: formatMinor(item.betAmount),
|
|
38174
|
+
winningsFormatted: formatMinor(item.winnings)
|
|
38175
|
+
}));
|
|
38176
|
+
return {
|
|
38177
|
+
data: {
|
|
38178
|
+
items,
|
|
38179
|
+
totalReturned: items.length,
|
|
38180
|
+
hint: items.length >= limit ? `Showing most recent ${limit} games. Use limit param to request more (max 100).` : void 0
|
|
38181
|
+
}
|
|
38182
|
+
};
|
|
38183
|
+
} catch (error) {
|
|
38184
|
+
return {
|
|
38185
|
+
error: `Failed to get game history: ${error instanceof Error ? error.message : String(error)}`,
|
|
38186
|
+
isError: true
|
|
38187
|
+
};
|
|
38188
|
+
}
|
|
38189
|
+
}
|
|
38190
|
+
async function getMyStats(client) {
|
|
38191
|
+
try {
|
|
38192
|
+
if (!client.currentUserId) {
|
|
38193
|
+
return {
|
|
38194
|
+
error: "Not authenticated. Call dim_login first.",
|
|
38195
|
+
isError: true
|
|
38196
|
+
};
|
|
38197
|
+
}
|
|
38198
|
+
const stats = await client.sdk.users.getUserStats(
|
|
38199
|
+
client.currentUserId
|
|
38200
|
+
);
|
|
38201
|
+
return {
|
|
38202
|
+
data: {
|
|
38203
|
+
...stats,
|
|
38204
|
+
totalEarnedFormatted: formatMinor(stats.totalEarned),
|
|
38205
|
+
referralEarnedFormatted: formatMinor(stats.referralEarned),
|
|
38206
|
+
totalLostFormatted: formatMinor(stats.totalLost),
|
|
38207
|
+
totalFeesPaidFormatted: formatMinor(stats.totalFeesPaid)
|
|
38208
|
+
}
|
|
38209
|
+
};
|
|
38210
|
+
} catch (error) {
|
|
38211
|
+
return {
|
|
38212
|
+
error: `Failed to get stats: ${error instanceof Error ? error.message : String(error)}`,
|
|
38213
|
+
isError: true
|
|
38214
|
+
};
|
|
38215
|
+
}
|
|
38216
|
+
}
|
|
38217
|
+
|
|
38036
38218
|
// ../dim-agent-core/src/tools/index.ts
|
|
38037
38219
|
var TOOL_DEFINITIONS = [
|
|
38038
38220
|
// ── Auth ─────────────────────────────────────────────────────────────
|
|
@@ -38044,7 +38226,7 @@ var TOOL_DEFINITIONS = [
|
|
|
38044
38226
|
},
|
|
38045
38227
|
{
|
|
38046
38228
|
name: "dim_get_profile",
|
|
38047
|
-
description: "Get the current authenticated user profile including username, avatar, bio,
|
|
38229
|
+
description: "Get the current authenticated user profile including username, avatar, bio, chess ELO rating, and your DIM wallet address (walletAddress).",
|
|
38048
38230
|
params: {},
|
|
38049
38231
|
execute: (c) => getProfile(c)
|
|
38050
38232
|
},
|
|
@@ -38204,9 +38386,15 @@ var TOOL_DEFINITIONS = [
|
|
|
38204
38386
|
execute: (c) => listDmThreads(c)
|
|
38205
38387
|
},
|
|
38206
38388
|
// ── Wallet ───────────────────────────────────────────────────────────
|
|
38389
|
+
{
|
|
38390
|
+
name: "dim_get_wallet_address",
|
|
38391
|
+
description: "Get the Solana wallet address DIM uses for this account. Use this to confirm which address receives deposits and is used for signing. Returns walletAddress and a short wallet linkage note.",
|
|
38392
|
+
params: {},
|
|
38393
|
+
execute: (c) => getWalletAddress(c)
|
|
38394
|
+
},
|
|
38207
38395
|
{
|
|
38208
38396
|
name: "dim_get_balance",
|
|
38209
|
-
description: "Get the wallet balance for the authenticated user. Returns SOL and USDC (usdcFormatted is always present, e.g. $0.00 or $1.50).",
|
|
38397
|
+
description: "Get the wallet balance for the authenticated user. Returns your DIM Solana address (walletAddress), SOL and USDC (usdcFormatted is always present, e.g. $0.00 or $1.50).",
|
|
38210
38398
|
params: {},
|
|
38211
38399
|
execute: (c) => getBalance(c)
|
|
38212
38400
|
},
|
|
@@ -38248,7 +38436,7 @@ var TOOL_DEFINITIONS = [
|
|
|
38248
38436
|
},
|
|
38249
38437
|
{
|
|
38250
38438
|
name: "dim_get_wallet_activity",
|
|
38251
|
-
description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds). Highlights any claimable items.",
|
|
38439
|
+
description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds) and your DIM wallet address. Highlights any claimable items.",
|
|
38252
38440
|
params: {
|
|
38253
38441
|
limit: {
|
|
38254
38442
|
type: "number",
|
|
@@ -38288,6 +38476,23 @@ var TOOL_DEFINITIONS = [
|
|
|
38288
38476
|
params: {},
|
|
38289
38477
|
execute: (c) => getGameMetrics(c)
|
|
38290
38478
|
},
|
|
38479
|
+
{
|
|
38480
|
+
name: "dim_get_game_history",
|
|
38481
|
+
description: "Get your game history: past games with result, bet, winnings, opponent, and playedAt. Use for daily progress reports.",
|
|
38482
|
+
params: {
|
|
38483
|
+
limit: {
|
|
38484
|
+
type: "number",
|
|
38485
|
+
description: "Max games to return (default 50, max 100)"
|
|
38486
|
+
}
|
|
38487
|
+
},
|
|
38488
|
+
execute: (c, a) => getGameHistory(c, a)
|
|
38489
|
+
},
|
|
38490
|
+
{
|
|
38491
|
+
name: "dim_get_my_stats",
|
|
38492
|
+
description: "Get your aggregated DIM stats: games played, wins, losses, total earned, referral earned, total lost, fees paid, chess ELO, points.",
|
|
38493
|
+
params: {},
|
|
38494
|
+
execute: (c) => getMyStats(c)
|
|
38495
|
+
},
|
|
38291
38496
|
{
|
|
38292
38497
|
name: "dim_create_lobby",
|
|
38293
38498
|
description: "Create a new game lobby. Bet amount is in USDC dollars (e.g., 1.00 for $1.00). A 1% fee (min 1 cent) is charged per player.",
|
|
@@ -38417,6 +38622,30 @@ var TOOL_DEFINITIONS = [
|
|
|
38417
38622
|
},
|
|
38418
38623
|
execute: (c, a) => gameLoop(c, a)
|
|
38419
38624
|
},
|
|
38625
|
+
{
|
|
38626
|
+
name: "dim_request_rematch",
|
|
38627
|
+
description: "Request a rematch after a completed game. If both players request, a lobby is created automatically server-side.",
|
|
38628
|
+
params: {
|
|
38629
|
+
gameId: {
|
|
38630
|
+
type: "string",
|
|
38631
|
+
description: "The completed game ID to request a rematch for",
|
|
38632
|
+
required: true
|
|
38633
|
+
}
|
|
38634
|
+
},
|
|
38635
|
+
execute: (c, a) => requestRematch(c, a)
|
|
38636
|
+
},
|
|
38637
|
+
{
|
|
38638
|
+
name: "dim_accept_rematch",
|
|
38639
|
+
description: "Accept a rematch request from your opponent. When both players accept, the rematch lobby is created automatically.",
|
|
38640
|
+
params: {
|
|
38641
|
+
gameId: {
|
|
38642
|
+
type: "string",
|
|
38643
|
+
description: "The completed game ID to accept the rematch for",
|
|
38644
|
+
required: true
|
|
38645
|
+
}
|
|
38646
|
+
},
|
|
38647
|
+
execute: (c, a) => acceptRematch(c, a)
|
|
38648
|
+
},
|
|
38420
38649
|
{
|
|
38421
38650
|
name: "dim_get_lobby_link",
|
|
38422
38651
|
description: "Get a shareable join link for a lobby. Share this URL via DM \u2014 the user can join with one click. Like dim_invite_to_lobby, do NOT also call dim_join_queue for the same lobby.",
|
|
@@ -38881,7 +39110,12 @@ function createHeartbeatWriter(heartbeatPath) {
|
|
|
38881
39110
|
const pending = [];
|
|
38882
39111
|
return async (event) => {
|
|
38883
39112
|
pending.push(event);
|
|
38884
|
-
const lines = [
|
|
39113
|
+
const lines = [
|
|
39114
|
+
"# DIM Heartbeat",
|
|
39115
|
+
"",
|
|
39116
|
+
"- At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.",
|
|
39117
|
+
""
|
|
39118
|
+
];
|
|
38885
39119
|
const eventTypes = new Set(pending.map((e) => e.event));
|
|
38886
39120
|
if (eventTypes.has("chat:message")) {
|
|
38887
39121
|
lines.push("- You have new DMs \u2014 call dim_check_notifications");
|
|
@@ -39018,7 +39252,12 @@ function register(api) {
|
|
|
39018
39252
|
return textResult(
|
|
39019
39253
|
JSON.stringify(
|
|
39020
39254
|
{
|
|
39255
|
+
wallet_linkage: WALLET_LINKAGE_NOTE,
|
|
39256
|
+
wallet_address: "Your DIM Solana address is returned by dim_get_profile, dim_get_balance, dim_get_wallet_activity, and dim_get_wallet_address. Use it to confirm which address is tied to DIM.",
|
|
39257
|
+
wallet_activity: "For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.",
|
|
39021
39258
|
hint: "Game flow: For paid lobbies, dim_create_lobby \u2192 dim_deposit_for_lobby \u2192 dim_join_queue. For free lobbies, dim_create_lobby \u2192 dim_join_queue. Then dim_game_loop (blocks until your turn) \u2192 dim_submit_action \u2192 dim_game_loop \u2192 repeat. Use dim_leave_lobby to exit a lobby.",
|
|
39259
|
+
spectate_on_game_start: "When a game starts (dim_join_queue or dim_accept_challenge returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].",
|
|
39260
|
+
daily_summary: "At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.",
|
|
39022
39261
|
move_formats: {
|
|
39023
39262
|
"rock-paper-scissors": 'action: "play", payload: { action: "rock"|"paper"|"scissors" }',
|
|
39024
39263
|
chess: 'action: "move", payload: { from: "e2", to: "e4" }',
|
package/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
DimAgentClient,
|
|
12
12
|
TOOL_DEFINITIONS,
|
|
13
13
|
DIM_INSTRUCTIONS,
|
|
14
|
+
WALLET_LINKAGE_NOTE,
|
|
14
15
|
executeWithAuthRetry,
|
|
15
16
|
} from '@dimcool/dim-agent-core';
|
|
16
17
|
import type { ToolParam, BufferedEvent } from '@dimcool/dim-agent-core';
|
|
@@ -127,7 +128,12 @@ function createHeartbeatWriter(heartbeatPath: string | undefined) {
|
|
|
127
128
|
|
|
128
129
|
return async (event: BufferedEvent) => {
|
|
129
130
|
pending.push(event);
|
|
130
|
-
const lines = [
|
|
131
|
+
const lines = [
|
|
132
|
+
'# DIM Heartbeat',
|
|
133
|
+
'',
|
|
134
|
+
'- At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.',
|
|
135
|
+
'',
|
|
136
|
+
];
|
|
131
137
|
const eventTypes = new Set(pending.map((e) => e.event));
|
|
132
138
|
if (eventTypes.has('chat:message')) {
|
|
133
139
|
lines.push('- You have new DMs — call dim_check_notifications');
|
|
@@ -304,7 +310,16 @@ export default function register(api: {
|
|
|
304
310
|
return textResult(
|
|
305
311
|
JSON.stringify(
|
|
306
312
|
{
|
|
313
|
+
wallet_linkage: WALLET_LINKAGE_NOTE,
|
|
314
|
+
wallet_address:
|
|
315
|
+
'Your DIM Solana address is returned by dim_get_profile, dim_get_balance, dim_get_wallet_activity, and dim_get_wallet_address. Use it to confirm which address is tied to DIM.',
|
|
316
|
+
wallet_activity:
|
|
317
|
+
'For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.',
|
|
307
318
|
hint: 'Game flow: For paid lobbies, dim_create_lobby → dim_deposit_for_lobby → dim_join_queue. For free lobbies, dim_create_lobby → dim_join_queue. Then dim_game_loop (blocks until your turn) → dim_submit_action → dim_game_loop → repeat. Use dim_leave_lobby to exit a lobby.',
|
|
319
|
+
spectate_on_game_start:
|
|
320
|
+
'When a game starts (dim_join_queue or dim_accept_challenge returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].',
|
|
321
|
+
daily_summary:
|
|
322
|
+
'At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.',
|
|
308
323
|
move_formats: {
|
|
309
324
|
'rock-paper-scissors':
|
|
310
325
|
'action: "play", payload: { action: "rock"|"paper"|"scissors" }',
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dimclaw",
|
|
3
3
|
"name": "DIM Gaming Platform",
|
|
4
|
-
"description": "Play games, chat, send USDC and earn on DIM.",
|
|
4
|
+
"description": "Play games, chat, send USDC and earn on DIM. Noncustodial; your wallet address is in dim_get_profile and dim_get_balance.",
|
|
5
5
|
"configSchema": {
|
|
6
6
|
"type": "object",
|
|
7
7
|
"properties": {
|
|
@@ -82,6 +82,6 @@
|
|
|
82
82
|
"label": "Auto-promote referral code"
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
|
-
"setupHint": "After install, add your Solana wallet private key to plugins.entries.dimclaw.config.walletPrivateKey, then call dim_login
|
|
85
|
+
"setupHint": "After install, add your Solana wallet private key (base58) to plugins.entries.dimclaw.config.walletPrivateKey, then call dim_login. Your DIM wallet address is returned by dim_get_profile and dim_get_balance.",
|
|
86
86
|
"skill": "./SKILL.md"
|
|
87
87
|
}
|
package/package.json
CHANGED