chitin-openclaw-plugin 0.2.1 → 0.2.2
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.d.ts +1 -1
- package/dist/index.js +80 -64
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 1. Wallet tools — check balance, claim faucet, deposit to table
|
|
6
6
|
* 2. Game tools — discover, lobby, create table, register, send action
|
|
7
7
|
* 3. WebSocket connection — persistent connection to poker table, your_turn events
|
|
8
|
-
* injected into agent session via
|
|
8
|
+
* injected into agent session via openclaw agent --message
|
|
9
9
|
*/
|
|
10
10
|
declare const plugin: {
|
|
11
11
|
id: string;
|
package/dist/index.js
CHANGED
|
@@ -32,43 +32,42 @@ function connectToTable(gameServerUrl, roomCode, playerId, name, walletAddress,
|
|
|
32
32
|
function connect() {
|
|
33
33
|
if (aborted) return;
|
|
34
34
|
const wsUrl = gameServerUrl.replace(/^http/, "ws") + `/parties/poker/${roomCode}`;
|
|
35
|
-
logger.info(`[chitin]
|
|
35
|
+
logger.info(`[chitin] WS connecting to ${wsUrl}`);
|
|
36
36
|
ws = new WebSocket(wsUrl);
|
|
37
37
|
ws.on("open", () => {
|
|
38
|
-
logger.info(`[chitin]
|
|
38
|
+
logger.info(`[chitin] WS connected to ${roomCode}`);
|
|
39
39
|
wsStore.set(roomCode, ws);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
name,
|
|
44
|
-
walletAddress
|
|
45
|
-
}));
|
|
40
|
+
const enterMsg = { type: "enter", playerId, name, walletAddress };
|
|
41
|
+
logger.info(`[chitin] WS sending enter: ${JSON.stringify(enterMsg)}`);
|
|
42
|
+
ws.send(JSON.stringify(enterMsg));
|
|
46
43
|
});
|
|
47
44
|
ws.on("message", (data) => {
|
|
48
45
|
try {
|
|
49
46
|
const msg = JSON.parse(data.toString());
|
|
50
47
|
handleMessage(msg, roomCode, logger);
|
|
51
48
|
} catch (err) {
|
|
52
|
-
logger.warn(`[chitin]
|
|
49
|
+
logger.warn(`[chitin] WS parse error: ${err.message}`);
|
|
53
50
|
}
|
|
54
51
|
});
|
|
55
|
-
ws.on("close", () => {
|
|
52
|
+
ws.on("close", (code) => {
|
|
56
53
|
if (aborted) return;
|
|
57
|
-
logger.info(`[chitin]
|
|
54
|
+
logger.info(`[chitin] WS closed (code ${code}), reconnecting in 3s...`);
|
|
58
55
|
setTimeout(connect, 3e3);
|
|
59
56
|
});
|
|
60
57
|
ws.on("error", (err) => {
|
|
61
|
-
logger.
|
|
58
|
+
logger.error(`[chitin] WS error: ${err.message}`);
|
|
62
59
|
});
|
|
63
60
|
}
|
|
64
61
|
const latestState = /* @__PURE__ */ new Map();
|
|
65
62
|
function handleMessage(msg, roomCode2, logger2) {
|
|
66
63
|
if (msg.type === "game_state") {
|
|
67
64
|
latestState.set(roomCode2, msg);
|
|
65
|
+
logger2.info(`[chitin] game_state: players=${msg.players?.length || 0} hand=${msg.isHandInProgress ? "yes" : "no"}`);
|
|
68
66
|
} else if (msg.type === "your_turn") {
|
|
69
67
|
const state = latestState.get(roomCode2);
|
|
70
68
|
const legalActions = msg.legalActions?.join(", ") || "";
|
|
71
69
|
const chipRange = msg.chipRange ? `${msg.chipRange.min}-${msg.chipRange.max}` : "";
|
|
70
|
+
logger2.info(`[chitin] YOUR TURN: actions=[${legalActions}] range=${chipRange}`);
|
|
72
71
|
const parts = [`POKER: It's your turn at table ${roomCode2}.`];
|
|
73
72
|
if (state?.holeCards) {
|
|
74
73
|
const cards = state.holeCards.map((c) => `${c.rank}${c.suit[0]}`).join(" ");
|
|
@@ -92,25 +91,24 @@ function connectToTable(gameServerUrl, roomCode, playerId, name, walletAddress,
|
|
|
92
91
|
} else if (msg.type === "hand_result") {
|
|
93
92
|
const winners = msg.result?.winners?.flat() || [];
|
|
94
93
|
const winnerNames = winners.map((w) => `${w.playerId}(${w.amount})`).join(", ");
|
|
94
|
+
logger2.info(`[chitin] HAND RESULT: winners=${winnerNames} rake=${msg.result?.rake || 0}`);
|
|
95
95
|
const text = `POKER HAND RESULT at table ${roomCode2}: Winners: ${winnerNames}. Rake: ${msg.result?.rake || 0}.`;
|
|
96
96
|
promptAgent(text, logger2);
|
|
97
|
+
} else {
|
|
98
|
+
logger2.info(`[chitin] WS msg: type=${msg.type}`);
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
101
|
function promptAgent(text, logger2) {
|
|
102
|
+
logger2.info(`[chitin] Prompting agent: ${text.slice(0, 100)}...`);
|
|
100
103
|
execFile("openclaw", ["agent", "--local", "--message", text], (err, _stdout, stderr) => {
|
|
101
104
|
if (err) {
|
|
102
|
-
logger2.error(`[chitin]
|
|
105
|
+
logger2.error(`[chitin] Agent prompt failed: ${err.message}`);
|
|
103
106
|
return;
|
|
104
107
|
}
|
|
105
|
-
if (stderr) logger2.warn(`[chitin] Agent stderr: ${stderr}`);
|
|
106
|
-
logger2.info(`[chitin] Agent prompted
|
|
108
|
+
if (stderr) logger2.warn(`[chitin] Agent stderr: ${stderr.slice(0, 200)}`);
|
|
109
|
+
logger2.info(`[chitin] Agent prompted ok`);
|
|
107
110
|
});
|
|
108
111
|
}
|
|
109
|
-
const sendAction = (action) => {
|
|
110
|
-
if (ws?.readyState === WebSocket.OPEN) {
|
|
111
|
-
ws.send(JSON.stringify({ type: "action", action }));
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
112
|
connect();
|
|
115
113
|
return () => {
|
|
116
114
|
aborted = true;
|
|
@@ -123,29 +121,22 @@ var plugin = {
|
|
|
123
121
|
description: "Play poker at Chitin Casino \u2014 wallet management and real-time game connection",
|
|
124
122
|
configSchema: {
|
|
125
123
|
type: "object",
|
|
126
|
-
additionalProperties:
|
|
124
|
+
additionalProperties: true,
|
|
127
125
|
properties: {}
|
|
128
126
|
},
|
|
129
127
|
register(api) {
|
|
130
128
|
api.logger.info("[chitin] Chitin Casino plugin loaded");
|
|
131
129
|
const gameServerUrl = process.env.GAME_SERVER_URL || "http://localhost:3665";
|
|
130
|
+
const adminServerUrl = process.env.ADMIN_SERVER_URL || "http://localhost:3667";
|
|
132
131
|
const activeConnections = /* @__PURE__ */ new Map();
|
|
133
132
|
const wsInstances = /* @__PURE__ */ new Map();
|
|
134
|
-
api.registerTool({
|
|
135
|
-
name: "chitin_test",
|
|
136
|
-
label: "Chitin Test",
|
|
137
|
-
description: "A test tool that returns hello world",
|
|
138
|
-
parameters: { type: "object", properties: {} },
|
|
139
|
-
execute: async (_toolCallId) => {
|
|
140
|
-
return { message: "Hello from Chitin Casino plugin!" };
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
133
|
api.registerTool({
|
|
144
134
|
name: "chitin_wallet_address",
|
|
145
135
|
description: "Get your Chitin Casino wallet address",
|
|
146
136
|
parameters: {},
|
|
147
|
-
execute: async (
|
|
137
|
+
execute: async () => {
|
|
148
138
|
const { account } = getWalletClients();
|
|
139
|
+
api.logger.info(`[chitin] wallet_address: ${account.address}`);
|
|
149
140
|
return { address: account.address };
|
|
150
141
|
}
|
|
151
142
|
});
|
|
@@ -160,7 +151,9 @@ var plugin = {
|
|
|
160
151
|
},
|
|
161
152
|
execute: async (_toolCallId, params) => {
|
|
162
153
|
const { account, publicClient } = getWalletClients();
|
|
154
|
+
api.logger.info(`[chitin] wallet_balance: checking ${account.address}`);
|
|
163
155
|
const ethBalance = await publicClient.getBalance({ address: account.address });
|
|
156
|
+
api.logger.info(`[chitin] wallet_balance: ETH=${formatEther(ethBalance)}`);
|
|
164
157
|
let tokenBalance = BigInt(0);
|
|
165
158
|
let tokenAddr = params.tokenAddress;
|
|
166
159
|
if (!tokenAddr) {
|
|
@@ -168,7 +161,9 @@ var plugin = {
|
|
|
168
161
|
const res = await fetch(`${gameServerUrl}/contracts`);
|
|
169
162
|
const data = await res.json();
|
|
170
163
|
tokenAddr = data.token;
|
|
171
|
-
|
|
164
|
+
api.logger.info(`[chitin] wallet_balance: token=${tokenAddr}`);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
api.logger.error(`[chitin] wallet_balance: failed to fetch token address: ${err.message}`);
|
|
172
167
|
}
|
|
173
168
|
}
|
|
174
169
|
if (tokenAddr) {
|
|
@@ -178,6 +173,7 @@ var plugin = {
|
|
|
178
173
|
functionName: "balanceOf",
|
|
179
174
|
args: [account.address]
|
|
180
175
|
});
|
|
176
|
+
api.logger.info(`[chitin] wallet_balance: NUMERO=${formatEther(tokenBalance)}`);
|
|
181
177
|
}
|
|
182
178
|
return {
|
|
183
179
|
address: account.address,
|
|
@@ -188,21 +184,18 @@ var plugin = {
|
|
|
188
184
|
});
|
|
189
185
|
api.registerTool({
|
|
190
186
|
name: "chitin_faucet_claim",
|
|
191
|
-
description: "Claim NUMERO tokens from the faucet.
|
|
187
|
+
description: "Claim NUMERO tokens from the faucet. Free \u2014 no gas needed.",
|
|
192
188
|
parameters: {},
|
|
193
|
-
execute: async (
|
|
189
|
+
execute: async () => {
|
|
194
190
|
const { account } = getWalletClients();
|
|
195
|
-
|
|
196
|
-
const serviceKey = process.env.SERVICE_KEY || "";
|
|
191
|
+
api.logger.info(`[chitin] faucet_claim: wallet=${account.address} server=${adminServerUrl}`);
|
|
197
192
|
const res = await fetch(`${adminServerUrl}/faucet/claim`, {
|
|
198
193
|
method: "POST",
|
|
199
|
-
headers: {
|
|
200
|
-
"Content-Type": "application/json",
|
|
201
|
-
"x-service-key": serviceKey
|
|
202
|
-
},
|
|
194
|
+
headers: { "Content-Type": "application/json" },
|
|
203
195
|
body: JSON.stringify({ walletAddress: account.address })
|
|
204
196
|
});
|
|
205
197
|
const data = await res.json();
|
|
198
|
+
api.logger.info(`[chitin] faucet_claim: status=${res.status} response=${JSON.stringify(data)}`);
|
|
206
199
|
if (!res.ok) throw new Error(data.error || "Faucet claim failed");
|
|
207
200
|
return data;
|
|
208
201
|
}
|
|
@@ -220,21 +213,27 @@ var plugin = {
|
|
|
220
213
|
required: ["amount", "tableId", "gameId"]
|
|
221
214
|
},
|
|
222
215
|
execute: async (_toolCallId, params) => {
|
|
216
|
+
api.logger.info(`[chitin] deposit: amount=${params.amount} tableId=${params.tableId} gameId=${params.gameId}`);
|
|
223
217
|
const { publicClient, walletClient } = getWalletClients();
|
|
218
|
+
api.logger.info(`[chitin] deposit: fetching contracts from ${gameServerUrl}/contracts`);
|
|
224
219
|
const res = await fetch(`${gameServerUrl}/contracts`);
|
|
225
220
|
const contracts = await res.json();
|
|
221
|
+
api.logger.info(`[chitin] deposit: token=${contracts.token} diamond=${contracts.diamond}`);
|
|
226
222
|
const amountWei = parseEther(params.amount);
|
|
227
223
|
const data = encodeAbiParameters(
|
|
228
224
|
[{ type: "bytes32" }, { type: "bytes32" }],
|
|
229
225
|
[params.tableId, params.gameId]
|
|
230
226
|
);
|
|
227
|
+
api.logger.info(`[chitin] deposit: sending transferAndCall amount=${amountWei} to=${contracts.diamond}`);
|
|
231
228
|
const hash = await walletClient.writeContract({
|
|
232
229
|
address: contracts.token,
|
|
233
230
|
abi: ERC20_ABI,
|
|
234
231
|
functionName: "transferAndCall",
|
|
235
232
|
args: [contracts.diamond, amountWei, data]
|
|
236
233
|
});
|
|
234
|
+
api.logger.info(`[chitin] deposit: tx sent hash=${hash}`);
|
|
237
235
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
236
|
+
api.logger.info(`[chitin] deposit: confirmed block=${receipt.blockNumber} status=${receipt.status}`);
|
|
238
237
|
return { txHash: hash, blockNumber: Number(receipt.blockNumber) };
|
|
239
238
|
}
|
|
240
239
|
});
|
|
@@ -242,18 +241,24 @@ var plugin = {
|
|
|
242
241
|
name: "chitin_discover",
|
|
243
242
|
description: "Discover Chitin Casino \u2014 games, contracts, faucet info",
|
|
244
243
|
parameters: {},
|
|
245
|
-
execute: async (
|
|
244
|
+
execute: async () => {
|
|
245
|
+
api.logger.info(`[chitin] discover: ${gameServerUrl}/discover`);
|
|
246
246
|
const res = await fetch(`${gameServerUrl}/discover`);
|
|
247
|
-
|
|
247
|
+
const data = await res.json();
|
|
248
|
+
api.logger.info(`[chitin] discover: status=${res.status}`);
|
|
249
|
+
return data;
|
|
248
250
|
}
|
|
249
251
|
});
|
|
250
252
|
api.registerTool({
|
|
251
253
|
name: "chitin_lobby",
|
|
252
254
|
description: "List poker tables with open seats",
|
|
253
255
|
parameters: {},
|
|
254
|
-
execute: async (
|
|
256
|
+
execute: async () => {
|
|
257
|
+
api.logger.info(`[chitin] lobby: ${gameServerUrl}/parties/pokerlobby/main/tables`);
|
|
255
258
|
const res = await fetch(`${gameServerUrl}/parties/pokerlobby/main/tables`);
|
|
256
|
-
|
|
259
|
+
const data = await res.json();
|
|
260
|
+
api.logger.info(`[chitin] lobby: status=${res.status} tables=${Array.isArray(data) ? data.length : "?"}`);
|
|
261
|
+
return data;
|
|
257
262
|
}
|
|
258
263
|
});
|
|
259
264
|
api.registerTool({
|
|
@@ -269,19 +274,21 @@ var plugin = {
|
|
|
269
274
|
}
|
|
270
275
|
},
|
|
271
276
|
execute: async (_toolCallId, params) => {
|
|
277
|
+
const settings = {
|
|
278
|
+
smallBlind: params.smallBlind || 1,
|
|
279
|
+
bigBlind: params.bigBlind || 2,
|
|
280
|
+
minBuyIn: params.minBuyIn || 50,
|
|
281
|
+
maxBuyIn: params.maxBuyIn || 200
|
|
282
|
+
};
|
|
283
|
+
api.logger.info(`[chitin] create_table: ${JSON.stringify(settings)}`);
|
|
272
284
|
const res = await fetch(`${gameServerUrl}/parties/poker/create`, {
|
|
273
285
|
method: "POST",
|
|
274
286
|
headers: { "Content-Type": "application/json" },
|
|
275
|
-
body: JSON.stringify({
|
|
276
|
-
settings: {
|
|
277
|
-
smallBlind: params.smallBlind || 1,
|
|
278
|
-
bigBlind: params.bigBlind || 2,
|
|
279
|
-
minBuyIn: params.minBuyIn || 50,
|
|
280
|
-
maxBuyIn: params.maxBuyIn || 200
|
|
281
|
-
}
|
|
282
|
-
})
|
|
287
|
+
body: JSON.stringify({ settings })
|
|
283
288
|
});
|
|
284
|
-
|
|
289
|
+
const data = await res.json();
|
|
290
|
+
api.logger.info(`[chitin] create_table: status=${res.status} response=${JSON.stringify(data)}`);
|
|
291
|
+
return data;
|
|
285
292
|
}
|
|
286
293
|
});
|
|
287
294
|
api.registerTool({
|
|
@@ -299,6 +306,7 @@ var plugin = {
|
|
|
299
306
|
execute: async (_toolCallId, params) => {
|
|
300
307
|
const { account } = getWalletClients();
|
|
301
308
|
const botName = params.name || "ClawBot";
|
|
309
|
+
api.logger.info(`[chitin] register: room=${params.roomCode} name=${botName} buyIn=${params.buyIn || 200} wallet=${account.address}`);
|
|
302
310
|
const res = await fetch(`${gameServerUrl}/parties/poker/${params.roomCode}/register`, {
|
|
303
311
|
method: "POST",
|
|
304
312
|
headers: { "Content-Type": "application/json" },
|
|
@@ -309,6 +317,7 @@ var plugin = {
|
|
|
309
317
|
})
|
|
310
318
|
});
|
|
311
319
|
const result = await res.json();
|
|
320
|
+
api.logger.info(`[chitin] register: status=${res.status} response=${JSON.stringify(result)}`);
|
|
312
321
|
if (!res.ok) return result;
|
|
313
322
|
const cleanup = connectToTable(
|
|
314
323
|
gameServerUrl,
|
|
@@ -319,8 +328,7 @@ var plugin = {
|
|
|
319
328
|
api.logger,
|
|
320
329
|
wsInstances
|
|
321
330
|
);
|
|
322
|
-
activeConnections.set(params.roomCode, { cleanup
|
|
323
|
-
api.logger.info(`[chitin] Registered at ${params.roomCode}, WebSocket connected`);
|
|
331
|
+
activeConnections.set(params.roomCode, { cleanup });
|
|
324
332
|
return { ...result, wsConnected: true };
|
|
325
333
|
}
|
|
326
334
|
});
|
|
@@ -337,13 +345,16 @@ var plugin = {
|
|
|
337
345
|
required: ["roomCode", "action"]
|
|
338
346
|
},
|
|
339
347
|
execute: async (_toolCallId, params) => {
|
|
348
|
+
api.logger.info(`[chitin] poker_action: room=${params.roomCode} action=${params.action} amount=${params.amount ?? "n/a"}`);
|
|
340
349
|
const ws = wsInstances.get(params.roomCode);
|
|
341
350
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
351
|
+
api.logger.error(`[chitin] poker_action: NOT CONNECTED to ${params.roomCode} (ws=${ws ? "exists" : "null"} state=${ws?.readyState})`);
|
|
342
352
|
return { error: "Not connected to table. Call chitin_register first." };
|
|
343
353
|
}
|
|
344
354
|
const action = { type: params.action };
|
|
345
355
|
if (params.amount !== void 0) action.amount = params.amount;
|
|
346
356
|
ws.send(JSON.stringify({ type: "action", action }));
|
|
357
|
+
api.logger.info(`[chitin] poker_action: sent ${JSON.stringify(action)}`);
|
|
347
358
|
return { ok: true, sent: action };
|
|
348
359
|
}
|
|
349
360
|
});
|
|
@@ -359,22 +370,27 @@ var plugin = {
|
|
|
359
370
|
required: ["roomCode", "token"]
|
|
360
371
|
},
|
|
361
372
|
execute: async (_toolCallId, params) => {
|
|
373
|
+
api.logger.info(`[chitin] leave_table: room=${params.roomCode}`);
|
|
374
|
+
const ws = wsInstances.get(params.roomCode);
|
|
375
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
376
|
+
ws.send(JSON.stringify({ type: "leave" }));
|
|
377
|
+
api.logger.info(`[chitin] leave_table: sent leave via WS`);
|
|
378
|
+
}
|
|
362
379
|
const conn = activeConnections.get(params.roomCode);
|
|
363
380
|
if (conn) {
|
|
364
381
|
conn.cleanup();
|
|
365
382
|
activeConnections.delete(params.roomCode);
|
|
366
383
|
}
|
|
367
384
|
wsInstances.delete(params.roomCode);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
385
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
386
|
+
api.logger.info(`[chitin] leave_table: fallback to HTTP leave`);
|
|
387
|
+
const res = await fetch(`${gameServerUrl}/parties/poker/${params.roomCode}/leave`, {
|
|
388
|
+
method: "POST",
|
|
389
|
+
headers: { "Authorization": `Bearer ${params.token}` }
|
|
390
|
+
});
|
|
391
|
+
return await res.json();
|
|
372
392
|
}
|
|
373
|
-
|
|
374
|
-
method: "POST",
|
|
375
|
-
headers: { "Authorization": `Bearer ${params.token}` }
|
|
376
|
-
});
|
|
377
|
-
return await res.json();
|
|
393
|
+
return { ok: true };
|
|
378
394
|
}
|
|
379
395
|
});
|
|
380
396
|
}
|
package/openclaw.plugin.json
CHANGED