clarityxo-sdk 0.1.0

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.
@@ -0,0 +1,482 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli/index.ts
27
+ var import_commander5 = require("commander");
28
+
29
+ // src/cli/commands/board.ts
30
+ var import_commander = require("commander");
31
+ var import_chalk = __toESM(require("chalk"));
32
+ var import_ora = __toESM(require("ora"));
33
+
34
+ // src/contract/read.ts
35
+ var import_transactions = require("@stacks/transactions");
36
+
37
+ // src/constants.ts
38
+ var CONTRACT_NAME = "tictactoe";
39
+ var CONTRACT_ADDRESS = "SP30VGN68PSGVWGNMD0HH2WQMM5T486EK3YGP7Z3Y.clarity-xo-game";
40
+ var DEFAULT_LEADERBOARD_API = "https://clarityxo.onrender.com";
41
+ var CONTRACT_FUNCTIONS = {
42
+ START_NEW_GAME: "start-new-game",
43
+ MAKE_MOVE: "make-move",
44
+ RESIGN_GAME: "resign-game",
45
+ GET_BOARD_STATE: "get-board-state",
46
+ GET_GAME_STATUS: "get-game-status",
47
+ GET_WINNER: "get-winner",
48
+ GET_CURRENT_TURN: "get-current-turn",
49
+ IS_VALID_MOVE: "is-valid-move"
50
+ };
51
+
52
+ // src/utils/network.ts
53
+ var import_network = require("@stacks/network");
54
+ function getStacksNetwork(network) {
55
+ return network === "mainnet" ? new import_network.StacksMainnet() : new import_network.StacksTestnet();
56
+ }
57
+
58
+ // src/utils/cv.ts
59
+ function parseBoardCV(cv) {
60
+ const rows = cv;
61
+ const board = [
62
+ [null, null, null],
63
+ [null, null, null],
64
+ [null, null, null]
65
+ ];
66
+ for (let i = 0; i < 3; i++) {
67
+ const row = rows.list[i].list;
68
+ for (let j = 0; j < 3; j++) {
69
+ const cell = row[j];
70
+ if (cell.type === "some") {
71
+ board[i][j] = cell.value.value;
72
+ } else {
73
+ board[i][j] = null;
74
+ }
75
+ }
76
+ }
77
+ return board;
78
+ }
79
+ function parseGameStatusCV(cv) {
80
+ const value = cv.value;
81
+ if (value === "active") return "active";
82
+ if (value === "finished") return "finished";
83
+ if (value === "not-started") return "not-started";
84
+ throw new Error(`Invalid game status: ${value}`);
85
+ }
86
+ function parseWinnerCV(cv) {
87
+ const value = cv.value;
88
+ if (value === "player") return "player";
89
+ if (value === "ai") return "ai";
90
+ if (value === "draw") return "draw";
91
+ return null;
92
+ }
93
+ function parseTurnCV(cv) {
94
+ const value = cv.value;
95
+ if (value === "player") return "player";
96
+ if (value === "ai") return "ai";
97
+ throw new Error(`Invalid turn: ${value}`);
98
+ }
99
+
100
+ // src/contract/read.ts
101
+ async function getBoardState(config) {
102
+ const network = getStacksNetwork(config.network);
103
+ const contractName = config.contractName || CONTRACT_NAME;
104
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
105
+ const cv = await (0, import_transactions.callReadOnlyFunction)({
106
+ network,
107
+ contractAddress,
108
+ contractName,
109
+ functionName: CONTRACT_FUNCTIONS.GET_BOARD_STATE,
110
+ functionArgs: [],
111
+ senderAddress: contractAddress
112
+ // arbitrary
113
+ });
114
+ return parseBoardCV(cv);
115
+ }
116
+ async function getGameStatus(config) {
117
+ const network = getStacksNetwork(config.network);
118
+ const contractName = config.contractName || CONTRACT_NAME;
119
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
120
+ const cv = await (0, import_transactions.callReadOnlyFunction)({
121
+ network,
122
+ contractAddress,
123
+ contractName,
124
+ functionName: CONTRACT_FUNCTIONS.GET_GAME_STATUS,
125
+ functionArgs: [],
126
+ senderAddress: contractAddress
127
+ });
128
+ return parseGameStatusCV(cv);
129
+ }
130
+ async function getWinner(config) {
131
+ const network = getStacksNetwork(config.network);
132
+ const contractName = config.contractName || CONTRACT_NAME;
133
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
134
+ const cv = await (0, import_transactions.callReadOnlyFunction)({
135
+ network,
136
+ contractAddress,
137
+ contractName,
138
+ functionName: CONTRACT_FUNCTIONS.GET_WINNER,
139
+ functionArgs: [],
140
+ senderAddress: contractAddress
141
+ });
142
+ return parseWinnerCV(cv);
143
+ }
144
+ async function getCurrentTurn(config) {
145
+ const network = getStacksNetwork(config.network);
146
+ const contractName = config.contractName || CONTRACT_NAME;
147
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
148
+ const cv = await (0, import_transactions.callReadOnlyFunction)({
149
+ network,
150
+ contractAddress,
151
+ contractName,
152
+ functionName: CONTRACT_FUNCTIONS.GET_CURRENT_TURN,
153
+ functionArgs: [],
154
+ senderAddress: contractAddress
155
+ });
156
+ return parseTurnCV(cv);
157
+ }
158
+ async function isValidMove(config, row, col) {
159
+ const network = getStacksNetwork(config.network);
160
+ const contractName = config.contractName || CONTRACT_NAME;
161
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
162
+ const cv = await (0, import_transactions.callReadOnlyFunction)({
163
+ network,
164
+ contractAddress,
165
+ contractName,
166
+ functionName: CONTRACT_FUNCTIONS.IS_VALID_MOVE,
167
+ functionArgs: [(0, import_transactions.uintCV)(row), (0, import_transactions.uintCV)(col)],
168
+ senderAddress: contractAddress
169
+ });
170
+ return cv.value;
171
+ }
172
+ async function getFullGameState(config) {
173
+ const [board, status, winner, currentTurn] = await Promise.all([
174
+ getBoardState(config),
175
+ getGameStatus(config),
176
+ getWinner(config),
177
+ getCurrentTurn(config)
178
+ ]);
179
+ return { board, status, winner, currentTurn };
180
+ }
181
+
182
+ // src/contract/write.ts
183
+ var import_transactions2 = require("@stacks/transactions");
184
+ async function startNewGame(config) {
185
+ if (!config.senderKey || !config.senderAddress) {
186
+ throw new Error("senderKey and senderAddress are required for write operations");
187
+ }
188
+ const network = getStacksNetwork(config.network);
189
+ const contractName = config.contractName || CONTRACT_NAME;
190
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
191
+ const tx = await (0, import_transactions2.makeContractCall)({
192
+ network,
193
+ contractAddress,
194
+ contractName,
195
+ functionName: CONTRACT_FUNCTIONS.START_NEW_GAME,
196
+ functionArgs: [],
197
+ senderKey: config.senderKey,
198
+ anchorMode: "any"
199
+ });
200
+ const broadcastResponse = await (0, import_transactions2.broadcastTransaction)(tx, network);
201
+ return { txId: broadcastResponse.txid };
202
+ }
203
+ async function makeMove(config, row, col) {
204
+ if (!config.senderKey || !config.senderAddress) {
205
+ throw new Error("senderKey and senderAddress are required for write operations");
206
+ }
207
+ const network = getStacksNetwork(config.network);
208
+ const contractName = config.contractName || CONTRACT_NAME;
209
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
210
+ const tx = await (0, import_transactions2.makeContractCall)({
211
+ network,
212
+ contractAddress,
213
+ contractName,
214
+ functionName: CONTRACT_FUNCTIONS.MAKE_MOVE,
215
+ functionArgs: [(0, import_transactions2.uintCV)(row), (0, import_transactions2.uintCV)(col)],
216
+ senderKey: config.senderKey,
217
+ anchorMode: "any"
218
+ });
219
+ const broadcastResponse = await (0, import_transactions2.broadcastTransaction)(tx, network);
220
+ return { txId: broadcastResponse.txid };
221
+ }
222
+ async function resignGame(config) {
223
+ if (!config.senderKey || !config.senderAddress) {
224
+ throw new Error("senderKey and senderAddress are required for write operations");
225
+ }
226
+ const network = getStacksNetwork(config.network);
227
+ const contractName = config.contractName || CONTRACT_NAME;
228
+ const contractAddress = config.contractAddress || CONTRACT_ADDRESS;
229
+ const tx = await (0, import_transactions2.makeContractCall)({
230
+ network,
231
+ contractAddress,
232
+ contractName,
233
+ functionName: CONTRACT_FUNCTIONS.RESIGN_GAME,
234
+ functionArgs: [],
235
+ senderKey: config.senderKey,
236
+ anchorMode: "any"
237
+ });
238
+ const broadcastResponse = await (0, import_transactions2.broadcastTransaction)(tx, network);
239
+ return { txId: broadcastResponse.txid };
240
+ }
241
+
242
+ // src/leaderboard/api.ts
243
+ function getBaseUrl(config) {
244
+ return config.leaderboardApiUrl || DEFAULT_LEADERBOARD_API;
245
+ }
246
+ async function getLeaderboard(config, month) {
247
+ const baseUrl = getBaseUrl(config);
248
+ const response = await fetch(`${baseUrl}/api/leaderboard?month=${month}`);
249
+ if (!response.ok) {
250
+ throw new Error(`Failed to fetch leaderboard: ${response.statusText}`);
251
+ }
252
+ return response.json();
253
+ }
254
+ async function submitResult(config, result) {
255
+ const baseUrl = getBaseUrl(config);
256
+ const response = await fetch(`${baseUrl}/api/leaderboard/result`, {
257
+ method: "POST",
258
+ headers: { "Content-Type": "application/json" },
259
+ body: JSON.stringify(result)
260
+ });
261
+ if (!response.ok) {
262
+ throw new Error(`Failed to submit result: ${response.statusText}`);
263
+ }
264
+ }
265
+ async function syncLeaderboard(config) {
266
+ const baseUrl = getBaseUrl(config);
267
+ const response = await fetch(`${baseUrl}/api/sync`, {
268
+ method: "POST"
269
+ });
270
+ if (!response.ok) {
271
+ throw new Error(`Failed to sync leaderboard: ${response.statusText}`);
272
+ }
273
+ }
274
+ async function healthCheck(config) {
275
+ const baseUrl = getBaseUrl(config);
276
+ try {
277
+ const response = await fetch(`${baseUrl}/health`);
278
+ return response.ok;
279
+ } catch {
280
+ return false;
281
+ }
282
+ }
283
+
284
+ // src/client.ts
285
+ var ClarityXOClient = class {
286
+ constructor(config) {
287
+ this.config = config;
288
+ }
289
+ config;
290
+ // Game state
291
+ getBoardState() {
292
+ return getBoardState(this.config);
293
+ }
294
+ getGameStatus() {
295
+ return getGameStatus(this.config);
296
+ }
297
+ getWinner() {
298
+ return getWinner(this.config);
299
+ }
300
+ getCurrentTurn() {
301
+ return getCurrentTurn(this.config);
302
+ }
303
+ isValidMove(row, col) {
304
+ return isValidMove(this.config, row, col);
305
+ }
306
+ getFullGameState() {
307
+ return getFullGameState(this.config);
308
+ }
309
+ // Transactions
310
+ startNewGame() {
311
+ return startNewGame(this.config);
312
+ }
313
+ makeMove(row, col) {
314
+ return makeMove(this.config, row, col);
315
+ }
316
+ resignGame() {
317
+ return resignGame(this.config);
318
+ }
319
+ // Leaderboard
320
+ getLeaderboard(month) {
321
+ return getLeaderboard(this.config, month);
322
+ }
323
+ submitResult(result) {
324
+ return submitResult(this.config, result);
325
+ }
326
+ syncLeaderboard() {
327
+ return syncLeaderboard(this.config);
328
+ }
329
+ healthCheck() {
330
+ return healthCheck(this.config);
331
+ }
332
+ };
333
+ function createClient(config) {
334
+ return new ClarityXOClient(config);
335
+ }
336
+
337
+ // src/cli/commands/board.ts
338
+ var command = new import_commander.Command("board");
339
+ command.description("Display the current game board").option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
340
+ const config = {
341
+ network: options.network,
342
+ contractAddress: options.contract,
343
+ leaderboardApiUrl: options.api
344
+ };
345
+ const spinner = (0, import_ora.default)("Fetching game state...").start();
346
+ try {
347
+ const client = createClient(config);
348
+ const gameState = await client.getFullGameState();
349
+ spinner.stop();
350
+ const { board, status, winner, currentTurn } = gameState;
351
+ const renderCell = (cell) => {
352
+ if (cell === "X") return import_chalk.default.blue.bold("X");
353
+ if (cell === "O") return import_chalk.default.red.bold("O");
354
+ return import_chalk.default.gray("\xB7");
355
+ };
356
+ const renderBoard = (board2) => {
357
+ const lines = [];
358
+ for (let i = 0; i < 3; i++) {
359
+ const row = board2[i].map(renderCell).join(" \u2502 ");
360
+ lines.push(row);
361
+ if (i < 2) {
362
+ lines.push("\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u253C\u2500\u2500\u2500");
363
+ }
364
+ }
365
+ return lines.join("\n");
366
+ };
367
+ console.log(import_chalk.default.cyan.bold("ClarityXO \u2014 " + (config.network === "mainnet" ? "Mainnet" : "Testnet")));
368
+ console.log(import_chalk.default.gray(`Contract: ${config.contractAddress}`));
369
+ console.log();
370
+ console.log(renderBoard(board));
371
+ console.log();
372
+ console.log(`Turn: ${currentTurn === "player" ? "Player" : "AI"} Status: ${status} Winner: ${winner || "\u2014"}`);
373
+ } catch (error) {
374
+ spinner.stop();
375
+ console.error(import_chalk.default.red(`Error: ${error.message}`));
376
+ process.exit(1);
377
+ }
378
+ });
379
+ var board_default = command;
380
+
381
+ // src/cli/commands/status.ts
382
+ var import_commander2 = require("commander");
383
+ var import_chalk2 = __toESM(require("chalk"));
384
+ var import_ora2 = __toESM(require("ora"));
385
+ var command2 = new import_commander2.Command("status");
386
+ command2.description("Display the current game status and turn").option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
387
+ const config = {
388
+ network: options.network,
389
+ contractAddress: options.contract,
390
+ leaderboardApiUrl: options.api
391
+ };
392
+ const spinner = (0, import_ora2.default)("Fetching game status...").start();
393
+ try {
394
+ const client = createClient(config);
395
+ const [status, currentTurn] = await Promise.all([
396
+ client.getGameStatus(),
397
+ client.getCurrentTurn()
398
+ ]);
399
+ spinner.stop();
400
+ console.log(`Status: ${status}`);
401
+ console.log(`Current Turn: ${currentTurn === "player" ? "Player" : "AI"}`);
402
+ } catch (error) {
403
+ spinner.stop();
404
+ console.error(import_chalk2.default.red(`Error: ${error.message}`));
405
+ process.exit(1);
406
+ }
407
+ });
408
+ var status_default = command2;
409
+
410
+ // src/cli/commands/winner.ts
411
+ var import_commander3 = require("commander");
412
+ var import_chalk3 = __toESM(require("chalk"));
413
+ var import_ora3 = __toESM(require("ora"));
414
+ var command3 = new import_commander3.Command("winner");
415
+ command3.description("Display the game winner").option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
416
+ const config = {
417
+ network: options.network,
418
+ contractAddress: options.contract,
419
+ leaderboardApiUrl: options.api
420
+ };
421
+ const spinner = (0, import_ora3.default)("Fetching winner...").start();
422
+ try {
423
+ const client = createClient(config);
424
+ const winner = await client.getWinner();
425
+ spinner.stop();
426
+ if (winner) {
427
+ const color = winner === "player" ? import_chalk3.default.green : winner === "ai" ? import_chalk3.default.red : import_chalk3.default.yellow;
428
+ console.log(color(`Winner: ${winner}`));
429
+ } else {
430
+ console.log(import_chalk3.default.gray("No winner yet"));
431
+ }
432
+ } catch (error) {
433
+ spinner.stop();
434
+ console.error(import_chalk3.default.red(`Error: ${error.message}`));
435
+ process.exit(1);
436
+ }
437
+ });
438
+ var winner_default = command3;
439
+
440
+ // src/cli/commands/leaderboard.ts
441
+ var import_commander4 = require("commander");
442
+ var import_chalk4 = __toESM(require("chalk"));
443
+ var import_ora4 = __toESM(require("ora"));
444
+ var command4 = new import_commander4.Command("leaderboard");
445
+ command4.description("Display the leaderboard for a given month").option("--month <yyyy-mm>", "Month (YYYY-MM)", (/* @__PURE__ */ new Date()).toISOString().slice(0, 7)).option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
446
+ const config = {
447
+ network: options.network,
448
+ contractAddress: options.contract,
449
+ leaderboardApiUrl: options.api
450
+ };
451
+ const spinner = (0, import_ora4.default)("Fetching leaderboard...").start();
452
+ try {
453
+ const client = createClient(config);
454
+ const leaderboard = await client.getLeaderboard(options.month);
455
+ spinner.stop();
456
+ console.log(import_chalk4.default.cyan.bold(`Leaderboard for ${leaderboard.month}`));
457
+ console.log();
458
+ const header = import_chalk4.default.bold("Rank Address Wins Losses Draws Points");
459
+ console.log(header);
460
+ leaderboard.entries.forEach((entry) => {
461
+ const addr = entry.player.slice(0, 12) + "...";
462
+ const line = `${entry.rank.toString().padStart(4)} ${addr.padEnd(25)} ${entry.wins.toString().padStart(5)} ${entry.losses.toString().padStart(7)} ${entry.draws.toString().padStart(6)} ${entry.points.toString().padStart(7)}`;
463
+ console.log(line);
464
+ });
465
+ } catch (error) {
466
+ spinner.stop();
467
+ console.error(import_chalk4.default.red(`Error: ${error.message}`));
468
+ process.exit(1);
469
+ }
470
+ });
471
+ var leaderboard_default = command4;
472
+
473
+ // src/cli/index.ts
474
+ var packageJson = { version: "0.1.0" };
475
+ var program = new import_commander5.Command();
476
+ program.name("clarityxo").description("CLI for ClarityXO Tic-Tac-Toe on Stacks blockchain").version(packageJson.version);
477
+ program.option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL");
478
+ program.addCommand(board_default);
479
+ program.addCommand(status_default);
480
+ program.addCommand(winner_default);
481
+ program.addCommand(leaderboard_default);
482
+ program.parse();
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createClient
4
+ } from "../chunk-Z5L3H7VV.mjs";
5
+
6
+ // src/cli/index.ts
7
+ import { Command as Command5 } from "commander";
8
+
9
+ // src/cli/commands/board.ts
10
+ import { Command } from "commander";
11
+ import chalk from "chalk";
12
+ import ora from "ora";
13
+ var command = new Command("board");
14
+ command.description("Display the current game board").option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
15
+ const config = {
16
+ network: options.network,
17
+ contractAddress: options.contract,
18
+ leaderboardApiUrl: options.api
19
+ };
20
+ const spinner = ora("Fetching game state...").start();
21
+ try {
22
+ const client = createClient(config);
23
+ const gameState = await client.getFullGameState();
24
+ spinner.stop();
25
+ const { board, status, winner, currentTurn } = gameState;
26
+ const renderCell = (cell) => {
27
+ if (cell === "X") return chalk.blue.bold("X");
28
+ if (cell === "O") return chalk.red.bold("O");
29
+ return chalk.gray("\xB7");
30
+ };
31
+ const renderBoard = (board2) => {
32
+ const lines = [];
33
+ for (let i = 0; i < 3; i++) {
34
+ const row = board2[i].map(renderCell).join(" \u2502 ");
35
+ lines.push(row);
36
+ if (i < 2) {
37
+ lines.push("\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u253C\u2500\u2500\u2500");
38
+ }
39
+ }
40
+ return lines.join("\n");
41
+ };
42
+ console.log(chalk.cyan.bold("ClarityXO \u2014 " + (config.network === "mainnet" ? "Mainnet" : "Testnet")));
43
+ console.log(chalk.gray(`Contract: ${config.contractAddress}`));
44
+ console.log();
45
+ console.log(renderBoard(board));
46
+ console.log();
47
+ console.log(`Turn: ${currentTurn === "player" ? "Player" : "AI"} Status: ${status} Winner: ${winner || "\u2014"}`);
48
+ } catch (error) {
49
+ spinner.stop();
50
+ console.error(chalk.red(`Error: ${error.message}`));
51
+ process.exit(1);
52
+ }
53
+ });
54
+ var board_default = command;
55
+
56
+ // src/cli/commands/status.ts
57
+ import { Command as Command2 } from "commander";
58
+ import chalk2 from "chalk";
59
+ import ora2 from "ora";
60
+ var command2 = new Command2("status");
61
+ command2.description("Display the current game status and turn").option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
62
+ const config = {
63
+ network: options.network,
64
+ contractAddress: options.contract,
65
+ leaderboardApiUrl: options.api
66
+ };
67
+ const spinner = ora2("Fetching game status...").start();
68
+ try {
69
+ const client = createClient(config);
70
+ const [status, currentTurn] = await Promise.all([
71
+ client.getGameStatus(),
72
+ client.getCurrentTurn()
73
+ ]);
74
+ spinner.stop();
75
+ console.log(`Status: ${status}`);
76
+ console.log(`Current Turn: ${currentTurn === "player" ? "Player" : "AI"}`);
77
+ } catch (error) {
78
+ spinner.stop();
79
+ console.error(chalk2.red(`Error: ${error.message}`));
80
+ process.exit(1);
81
+ }
82
+ });
83
+ var status_default = command2;
84
+
85
+ // src/cli/commands/winner.ts
86
+ import { Command as Command3 } from "commander";
87
+ import chalk3 from "chalk";
88
+ import ora3 from "ora";
89
+ var command3 = new Command3("winner");
90
+ command3.description("Display the game winner").option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
91
+ const config = {
92
+ network: options.network,
93
+ contractAddress: options.contract,
94
+ leaderboardApiUrl: options.api
95
+ };
96
+ const spinner = ora3("Fetching winner...").start();
97
+ try {
98
+ const client = createClient(config);
99
+ const winner = await client.getWinner();
100
+ spinner.stop();
101
+ if (winner) {
102
+ const color = winner === "player" ? chalk3.green : winner === "ai" ? chalk3.red : chalk3.yellow;
103
+ console.log(color(`Winner: ${winner}`));
104
+ } else {
105
+ console.log(chalk3.gray("No winner yet"));
106
+ }
107
+ } catch (error) {
108
+ spinner.stop();
109
+ console.error(chalk3.red(`Error: ${error.message}`));
110
+ process.exit(1);
111
+ }
112
+ });
113
+ var winner_default = command3;
114
+
115
+ // src/cli/commands/leaderboard.ts
116
+ import { Command as Command4 } from "commander";
117
+ import chalk4 from "chalk";
118
+ import ora4 from "ora";
119
+ var command4 = new Command4("leaderboard");
120
+ command4.description("Display the leaderboard for a given month").option("--month <yyyy-mm>", "Month (YYYY-MM)", (/* @__PURE__ */ new Date()).toISOString().slice(0, 7)).option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL").action(async (options) => {
121
+ const config = {
122
+ network: options.network,
123
+ contractAddress: options.contract,
124
+ leaderboardApiUrl: options.api
125
+ };
126
+ const spinner = ora4("Fetching leaderboard...").start();
127
+ try {
128
+ const client = createClient(config);
129
+ const leaderboard = await client.getLeaderboard(options.month);
130
+ spinner.stop();
131
+ console.log(chalk4.cyan.bold(`Leaderboard for ${leaderboard.month}`));
132
+ console.log();
133
+ const header = chalk4.bold("Rank Address Wins Losses Draws Points");
134
+ console.log(header);
135
+ leaderboard.entries.forEach((entry) => {
136
+ const addr = entry.player.slice(0, 12) + "...";
137
+ const line = `${entry.rank.toString().padStart(4)} ${addr.padEnd(25)} ${entry.wins.toString().padStart(5)} ${entry.losses.toString().padStart(7)} ${entry.draws.toString().padStart(6)} ${entry.points.toString().padStart(7)}`;
138
+ console.log(line);
139
+ });
140
+ } catch (error) {
141
+ spinner.stop();
142
+ console.error(chalk4.red(`Error: ${error.message}`));
143
+ process.exit(1);
144
+ }
145
+ });
146
+ var leaderboard_default = command4;
147
+
148
+ // src/cli/index.ts
149
+ var packageJson = { version: "0.1.0" };
150
+ var program = new Command5();
151
+ program.name("clarityxo").description("CLI for ClarityXO Tic-Tac-Toe on Stacks blockchain").version(packageJson.version);
152
+ program.option("--contract [address]", "Contract address").option("--network <network>", "Network (mainnet or testnet)", "testnet").option("--api <url>", "Leaderboard API URL");
153
+ program.addCommand(board_default);
154
+ program.addCommand(status_default);
155
+ program.addCommand(winner_default);
156
+ program.addCommand(leaderboard_default);
157
+ program.parse();