@darksol/terminal 0.5.8 → 0.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darksol/terminal",
3
- "version": "0.5.8",
3
+ "version": "0.6.0",
4
4
  "description": "DARKSOL Terminal — unified CLI for all DARKSOL services. Market intel, trading, oracle, casino, and more.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -15,7 +15,7 @@ import { snipeToken, watchSnipe } from './trading/snipe.js';
15
15
  import { createDCA, listDCA, cancelDCA, runDCA } from './trading/dca.js';
16
16
  import { topMovers, tokenDetail, compareTokens } from './services/market.js';
17
17
  import { oracleFlip, oracleDice, oracleNumber, oracleShuffle, oracleHealth } from './services/oracle.js';
18
- import { casinoBet, casinoTables, casinoStats, casinoReceipt } from './services/casino.js';
18
+ import { casinoBet, casinoTables, casinoStats, casinoReceipt, casinoHealth, casinoVerify } from './services/casino.js';
19
19
  import { cardsCatalog, cardsOrder, cardsStatus } from './services/cards.js';
20
20
  import { facilitatorHealth, facilitatorVerify, facilitatorSettle } from './services/facilitator.js';
21
21
  import { buildersLeaderboard, buildersLookup, buildersFeed } from './services/builders.js';
@@ -238,15 +238,26 @@ export function cli(argv) {
238
238
  .description('The Clawsino — on-chain betting');
239
239
 
240
240
  casino
241
- .command('bet <game> [choice]')
242
- .description('Place a bet (coin-flip, dice, hilo, slots)')
243
- .option('-n, --number <n>', 'Number for dice over/under')
244
- .option('-w, --wallet <addr>', 'Wallet address')
245
- .action((game, choice, opts) => casinoBet(game, { choice, ...opts }));
241
+ .command('status')
242
+ .description('House status, balance, games')
243
+ .action(() => casinoHealth());
244
+
245
+ casino
246
+ .command('bet [game]')
247
+ .description('Place a $1 USDC bet (interactive if game omitted)')
248
+ .option('-c, --choice <choice>', 'heads/tails, higher/lower')
249
+ .option('-d, --direction <dir>', 'over/under (dice)')
250
+ .option('-t, --threshold <n>', 'Dice threshold 2-5')
251
+ .option('-w, --wallet <addr>', 'Payout wallet address')
252
+ .action((game, opts) => casinoBet(game, {
253
+ choice: opts.choice,
254
+ direction: opts.direction,
255
+ threshold: opts.threshold ? parseInt(opts.threshold) : undefined,
256
+ }, { wallet: opts.wallet }));
246
257
 
247
258
  casino
248
259
  .command('tables')
249
- .description('View game tables')
260
+ .description('Recent bets')
250
261
  .action(() => casinoTables());
251
262
 
252
263
  casino
@@ -256,9 +267,14 @@ export function cli(argv) {
256
267
 
257
268
  casino
258
269
  .command('receipt <id>')
259
- .description('Verify bet receipt')
270
+ .description('Get bet receipt')
260
271
  .action((id) => casinoReceipt(id));
261
272
 
273
+ casino
274
+ .command('verify <id>')
275
+ .description('Verify bet on-chain')
276
+ .action((id) => casinoVerify(id));
277
+
262
278
  // ═══════════════════════════════════════
263
279
  // CARDS COMMANDS
264
280
  // ═══════════════════════════════════════
@@ -31,7 +31,7 @@ const config = new Conf({
31
31
  default: {
32
32
  oracle: 'https://acp.darksol.net/oracle',
33
33
  casino: 'https://casino.darksol.net',
34
- cards: 'https://acp.darksol.net/cards',
34
+ cards: 'https://acp.darksol.net',
35
35
  facilitator: 'https://facilitator.darksol.net',
36
36
  builders: 'https://builders.darksol.net',
37
37
  market: 'https://acp.darksol.net/market',
package/src/llm/intent.js CHANGED
@@ -65,8 +65,16 @@ ACTIONS (use the most specific one):
65
65
  - "analyze" — deep analysis of a token
66
66
  - "gas" — gas price check
67
67
  - "cards" — order a prepaid Visa/Mastercard with crypto (e.g. "order a $50 card", "get me a prepaid card")
68
+ - "casino" — play a casino game (coinflip, dice, hilo, slots). All bets are $1 USDC. (e.g. "flip a coin", "bet on heads", "play slots", "roll dice over 3")
68
69
  - "unknown" — can't determine what the user wants
69
70
 
71
+ CASINO GAMES:
72
+ - coinflip: { "choice": "heads" | "tails" } → 1.90x payout
73
+ - dice: { "direction": "over"|"under", "threshold": 2-5 } → variable payout
74
+ - hilo: { "choice": "higher" | "lower" } → ~2.06x payout
75
+ - slots: {} → Match-3: 5.00x, Match-2: 1.50x
76
+ All bets are exactly $1 USDC. House edge: 5%. Results verified on-chain.
77
+
70
78
  CARDS ORDERING:
71
79
  When the user wants to order a prepaid card, you MUST collect:
72
80
  1. amount — ONLY these denominations: $10, $25, $50, $100, $250, $500, $1000
@@ -91,7 +99,7 @@ If they mention AgentMail or "my email", suggest using their configured agent em
91
99
 
92
100
  When parsing, respond with ONLY valid JSON:
93
101
  {
94
- "action": "swap|send|snipe|dca|price|balance|info|analyze|gas|cards|unknown",
102
+ "action": "swap|send|snipe|dca|price|balance|info|analyze|gas|cards|casino|unknown",
95
103
  "tokenIn": "symbol or address (for swaps)",
96
104
  "tokenOut": "symbol or address (for swaps)",
97
105
  "token": "symbol (for send/price/analyze)",
@@ -100,6 +108,8 @@ When parsing, respond with ONLY valid JSON:
100
108
  "email": "delivery email (for cards)",
101
109
  "provider": "card provider (for cards, default: swype)",
102
110
  "ticker": "payment crypto (for cards, default: usdc)",
111
+ "gameType": "casino game: coinflip|dice|hilo|slots",
112
+ "betParams": "casino bet parameters object",
103
113
  "chain": "chain name if specified, null if not",
104
114
  "interval": "for DCA: 1h, 4h, 1d, etc.",
105
115
  "orders": "for DCA: number of orders",
@@ -147,6 +157,7 @@ COMMAND MAPPING:
147
157
  - balance → darksol wallet balance
148
158
  - gas → darksol gas <chain>
149
159
  - cards → darksol cards order -p <provider> -a <amount> -e <email> --ticker <crypto>
160
+ - casino → darksol casino bet <game> -c <choice>
150
161
  - analyze → darksol ai analyze <token>`;
151
162
 
152
163
  // ──────────────────────────────────────────────────
@@ -292,7 +303,7 @@ export async function startChat(opts = {}) {
292
303
  }
293
304
 
294
305
  // Try to detect actionable intent
295
- const actionKeywords = /\b(swap|send|transfer|buy|sell|snipe|dca|price|balance|gas|card|cards|order|prepaid|visa|mastercard)\b/i;
306
+ const actionKeywords = /\b(swap|send|transfer|buy|sell|snipe|dca|price|balance|gas|card|cards|order|prepaid|visa|mastercard|casino|bet|coinflip|coin|flip|dice|slots|hilo|gamble|play)\b/i;
296
307
  const isActionable = actionKeywords.test(input);
297
308
 
298
309
  let result;
@@ -628,6 +639,17 @@ export async function executeIntent(intent, opts = {}) {
628
639
  return { success: true, action: 'gas' };
629
640
  }
630
641
 
642
+ case 'casino': {
643
+ const { casinoBet } = await import('../services/casino.js');
644
+ const gameType = intent.gameType || 'coinflip';
645
+ const betParams = intent.betParams || {};
646
+ // Map common AI outputs
647
+ if (intent.choice) betParams.choice = intent.choice;
648
+ if (intent.direction) betParams.direction = intent.direction;
649
+ if (intent.threshold) betParams.threshold = intent.threshold;
650
+ return await casinoBet(gameType, betParams, { wallet: opts.wallet });
651
+ }
652
+
631
653
  case 'cards': {
632
654
  if (!intent.amount) {
633
655
  info('What denomination? We have $10, $25, $50, $100, $250, $500, $1000');
@@ -1,92 +1,291 @@
1
1
  import { fetchJSON } from '../utils/fetch.js';
2
2
  import fetch from 'node-fetch';
3
3
  import { getServiceURL } from '../config/store.js';
4
+ import { getConfig } from '../config/store.js';
4
5
  import { theme } from '../ui/theme.js';
5
- import { spinner, kvDisplay, success, error, table } from '../ui/components.js';
6
+ import { spinner, kvDisplay, success, error, warn, info, table } from '../ui/components.js';
6
7
  import { showSection } from '../ui/banner.js';
7
8
 
8
9
  const getURL = () => getServiceURL('casino') || 'https://casino.darksol.net';
9
10
 
10
- export async function casinoBet(game, opts = {}) {
11
- const spin = spinner(`Placing ${game} bet...`).start();
11
+ // Available games with their bet params
12
+ const GAMES = {
13
+ coinflip: {
14
+ name: '🪙 Coin Flip',
15
+ payout: '1.90x',
16
+ params: { choice: ['heads', 'tails'] },
17
+ desc: 'Call heads or tails',
18
+ },
19
+ dice: {
20
+ name: '🎲 Dice',
21
+ payout: 'variable',
22
+ params: { direction: ['over', 'under'], threshold: [2, 3, 4, 5] },
23
+ desc: 'Over/under a number (2-5)',
24
+ },
25
+ hilo: {
26
+ name: '🃏 Hi-Lo',
27
+ payout: '~2.06x',
28
+ params: { choice: ['higher', 'lower'] },
29
+ desc: 'Higher or lower',
30
+ },
31
+ slots: {
32
+ name: '🎰 Slots',
33
+ payout: '1.50-5.00x',
34
+ params: {},
35
+ desc: 'Match symbols — no params needed',
36
+ },
37
+ };
38
+
39
+ export { GAMES };
40
+
41
+ /**
42
+ * Check casino health + x402 payment info
43
+ */
44
+ export async function casinoHealth() {
45
+ const spin = spinner('Checking casino...').start();
46
+ try {
47
+ const stats = await fetchJSON(`${getURL()}/api/stats`);
48
+ spin.succeed('Casino online');
49
+
50
+ showSection('THE CLAWSINO 🎰');
51
+ kvDisplay([
52
+ ['Status', stats.acceptingBets ? theme.success('● Open') : theme.error('○ Closed')],
53
+ ['House Balance', `$${stats.houseBalanceUsdc || '0'} USDC`],
54
+ ['Total Bets', String(stats.totalBets || 0)],
55
+ ['Total Wagered', `$${stats.totalWageredUsdc || '0'} USDC`],
56
+ ['Total Payouts', `$${stats.totalPayoutsUsdc || '0'} USDC`],
57
+ ['Win Rate', stats.winRate || '0%'],
58
+ ]);
59
+
60
+ console.log('');
61
+ showSection('GAMES');
62
+ for (const [id, g] of Object.entries(GAMES)) {
63
+ console.log(` ${theme.gold(g.name.padEnd(20))} ${theme.dim(g.payout.padEnd(12))} ${g.desc}`);
64
+ }
65
+ console.log('');
66
+ info('All bets are $1 USDC. House edge: 5%.');
67
+ info('Docs: https://casino.darksol.net/docs');
68
+ } catch (err) {
69
+ spin.fail('Casino unreachable');
70
+ error(err.message);
71
+ info('Check: https://casino.darksol.net/docs');
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Place a bet — requires wallet address for payouts
77
+ */
78
+ export async function casinoBet(gameType, betParams = {}, opts = {}) {
79
+ const inquirer = (await import('inquirer')).default;
80
+ const game = GAMES[gameType];
81
+
82
+ // Validate or prompt for game type
83
+ if (!game) {
84
+ if (gameType) warn(`Unknown game: ${gameType}`);
85
+ const { picked } = await inquirer.prompt([{
86
+ type: 'list',
87
+ name: 'picked',
88
+ message: theme.gold('Pick a game:'),
89
+ choices: Object.entries(GAMES).map(([id, g]) => ({ name: `${g.name} ${g.payout} — ${g.desc}`, value: id })),
90
+ }]);
91
+ gameType = picked;
92
+ }
93
+
94
+ const gameInfo = GAMES[gameType];
95
+
96
+ // Collect bet params based on game
97
+ if (gameType === 'coinflip' && !betParams.choice) {
98
+ const { choice } = await inquirer.prompt([{
99
+ type: 'list',
100
+ name: 'choice',
101
+ message: theme.gold('Heads or tails?'),
102
+ choices: ['heads', 'tails'],
103
+ }]);
104
+ betParams.choice = choice;
105
+ }
106
+
107
+ if (gameType === 'dice') {
108
+ if (!betParams.direction) {
109
+ const { direction } = await inquirer.prompt([{
110
+ type: 'list',
111
+ name: 'direction',
112
+ message: theme.gold('Over or under?'),
113
+ choices: ['over', 'under'],
114
+ }]);
115
+ betParams.direction = direction;
116
+ }
117
+ if (!betParams.threshold) {
118
+ const { threshold } = await inquirer.prompt([{
119
+ type: 'list',
120
+ name: 'threshold',
121
+ message: theme.gold('Threshold (2-5):'),
122
+ choices: ['2', '3', '4', '5'],
123
+ }]);
124
+ betParams.threshold = parseInt(threshold);
125
+ }
126
+ }
127
+
128
+ if (gameType === 'hilo' && !betParams.choice) {
129
+ const { choice } = await inquirer.prompt([{
130
+ type: 'list',
131
+ name: 'choice',
132
+ message: theme.gold('Higher or lower?'),
133
+ choices: ['higher', 'lower'],
134
+ }]);
135
+ betParams.choice = choice;
136
+ }
137
+
138
+ // Get wallet address
139
+ let agentWallet = opts.wallet;
140
+ if (!agentWallet) {
141
+ const activeWallet = getConfig('activeWallet');
142
+ if (activeWallet) {
143
+ try {
144
+ const { loadWallet } = await import('../wallet/keystore.js');
145
+ const w = loadWallet(activeWallet);
146
+ agentWallet = w.address;
147
+ } catch {}
148
+ }
149
+ }
150
+ if (!agentWallet) {
151
+ const { addr } = await inquirer.prompt([{
152
+ type: 'input',
153
+ name: 'addr',
154
+ message: theme.gold('Your wallet address (for payouts):'),
155
+ validate: v => v.startsWith('0x') && v.length === 42 ? true : 'Enter a valid 0x address',
156
+ }]);
157
+ agentWallet = addr;
158
+ }
159
+
160
+ // Confirm
161
+ console.log('');
162
+ kvDisplay([
163
+ ['Game', gameInfo.name],
164
+ ['Bet', '$1 USDC'],
165
+ ['Params', JSON.stringify(betParams)],
166
+ ['Payout Wallet', agentWallet],
167
+ ['Payout', gameInfo.payout],
168
+ ]);
169
+ console.log('');
170
+
171
+ const { confirm } = await inquirer.prompt([{
172
+ type: 'confirm',
173
+ name: 'confirm',
174
+ message: theme.gold('Place $1 USDC bet?'),
175
+ default: false,
176
+ }]);
177
+
178
+ if (!confirm) {
179
+ warn('Bet cancelled');
180
+ return;
181
+ }
182
+
183
+ const spin = spinner(`Playing ${gameInfo.name}...`).start();
12
184
  try {
13
185
  const data = await fetchJSON(`${getURL()}/api/bet`, {
14
186
  method: 'POST',
15
187
  headers: { 'Content-Type': 'application/json' },
16
- body: JSON.stringify({
17
- game,
18
- choice: opts.choice,
19
- number: opts.number,
20
- wallet: opts.wallet,
21
- }),
188
+ body: JSON.stringify({ gameType, betParams, agentWallet }),
22
189
  });
23
- spin.succeed('Bet placed');
24
190
 
25
- showSection(`CASINO — ${game.toUpperCase()}`);
191
+ if (data.won) {
192
+ spin.succeed(theme.success(`YOU WON! $${data.payoutAmount} USDC`));
193
+ } else {
194
+ spin.fail('You lost.');
195
+ }
196
+
197
+ console.log('');
198
+ showSection(`${gameInfo.name} RESULT`);
26
199
  kvDisplay([
27
- ['Game', game],
28
- ['Your Call', opts.choice || opts.number || 'N/A'],
29
- ['Result', data.result ? theme.gold.bold(data.result) : 'N/A'],
30
- ['Won', data.won ? theme.success.bold('YES!') : theme.error('No')],
31
- ['Payout', data.payout ? `$${data.payout}` : '$0'],
32
- ['TX', data.txHash || 'N/A'],
200
+ ['Bet ID', data.id || '-'],
201
+ ['Result', data.result || '-'],
202
+ ['Won', data.won ? theme.success('YES! 🎉') : theme.error('No')],
203
+ ['Payout', data.won ? `$${data.payoutAmount} USDC` : '$0'],
204
+ ['Oracle TX', data.oracleTxHash ? data.oracleTxHash.slice(0, 20) + '...' : '-'],
205
+ ['Payout TX', data.payoutTxHash ? data.payoutTxHash.slice(0, 20) + '...' : '-'],
33
206
  ]);
207
+
208
+ if (data.id) {
209
+ console.log('');
210
+ info(`Verify on-chain: darksol casino verify ${data.id}`);
211
+ }
212
+ console.log('');
34
213
  } catch (err) {
35
214
  spin.fail('Bet failed');
36
215
  error(err.message);
216
+ if (err.message.includes('not accepting') || err.message.includes('closed')) {
217
+ info('The casino may be temporarily closed. Check: darksol casino status');
218
+ }
37
219
  }
38
220
  }
39
221
 
222
+ /**
223
+ * Show recent bets
224
+ */
40
225
  export async function casinoTables() {
41
- const spin = spinner('Loading tables...').start();
226
+ const spin = spinner('Loading recent bets...').start();
42
227
  try {
43
228
  const data = await fetchJSON(`${getURL()}/api/tables`);
44
- spin.succeed('Tables loaded');
45
-
46
- showSection('CASINO TABLES');
47
- const tables = data.tables || data;
48
- if (Array.isArray(tables)) {
49
- const rows = tables.map(t => [
50
- theme.gold(t.name || t.game),
51
- t.multiplier || 'N/A',
52
- t.minBet || '$1',
53
- t.status || 'Open',
54
- ]);
55
- table(['Game', 'Multiplier', 'Min Bet', 'Status'], rows);
56
- } else {
57
- kvDisplay(Object.entries(tables).map(([k, v]) => [k, String(v)]));
229
+ spin.succeed('Loaded');
230
+
231
+ const items = data.items || [];
232
+ if (items.length === 0) {
233
+ info('No recent bets');
234
+ return;
58
235
  }
236
+
237
+ showSection('RECENT BETS');
238
+ const rows = items.map(b => [
239
+ b.gameType || '-',
240
+ b.result || '-',
241
+ b.won ? theme.success('Won') : theme.error('Lost'),
242
+ b.payoutAmount ? `$${b.payoutAmount}` : '$0',
243
+ b.agentWallet ? b.agentWallet.slice(0, 8) + '...' : '-',
244
+ ]);
245
+ table(['Game', 'Result', 'Won', 'Payout', 'Wallet'], rows);
59
246
  } catch (err) {
60
- spin.fail('Failed to load tables');
247
+ spin.fail('Failed');
61
248
  error(err.message);
62
249
  }
63
250
  }
64
251
 
252
+ /**
253
+ * Get stats
254
+ */
65
255
  export async function casinoStats() {
66
- const spin = spinner('Loading stats...').start();
256
+ return await casinoHealth();
257
+ }
258
+
259
+ /**
260
+ * Get receipt for a bet
261
+ */
262
+ export async function casinoReceipt(id) {
263
+ const spin = spinner(`Loading receipt ${id}...`).start();
67
264
  try {
68
- const data = await fetchJSON(`${getURL()}/api/stats`);
69
- spin.succeed('Stats loaded');
265
+ const data = await fetchJSON(`${getURL()}/api/receipt/${id}`);
266
+ spin.succeed('Receipt loaded');
70
267
 
71
- showSection('CASINO STATS');
268
+ showSection(`BET RECEIPT — ${id}`);
72
269
  kvDisplay(Object.entries(data).map(([k, v]) => [k, typeof v === 'object' ? JSON.stringify(v) : String(v)]));
73
270
  } catch (err) {
74
- spin.fail('Failed to load stats');
271
+ spin.fail('Receipt not found');
75
272
  error(err.message);
76
273
  }
77
274
  }
78
275
 
79
- export async function casinoReceipt(id) {
80
- const spin = spinner(`Loading receipt ${id}...`).start();
276
+ /**
277
+ * Verify a bet on-chain
278
+ */
279
+ export async function casinoVerify(id) {
280
+ const spin = spinner(`Verifying ${id}...`).start();
81
281
  try {
82
- const data = await fetchJSON(`${getURL()}/api/receipt/${id}`);
83
- spin.succeed('Receipt loaded');
282
+ const data = await fetchJSON(`${getURL()}/api/verify/${id}`);
283
+ spin.succeed('Verified');
84
284
 
85
- showSection(`CASINO RECEIPT — ${id}`);
285
+ showSection(`ON-CHAIN VERIFICATION — ${id}`);
86
286
  kvDisplay(Object.entries(data).map(([k, v]) => [k, String(v)]));
87
287
  } catch (err) {
88
- spin.fail('Receipt not found');
288
+ spin.fail('Verification failed');
89
289
  error(err.message);
90
290
  }
91
291
  }
92
-
@@ -1,29 +1,55 @@
1
1
  import { fetchJSON } from '../utils/fetch.js';
2
2
  import { getServiceURL } from '../config/store.js';
3
3
  import { theme } from '../ui/theme.js';
4
- import { spinner, kvDisplay, error } from '../ui/components.js';
4
+ import { spinner, kvDisplay, error, info } from '../ui/components.js';
5
5
  import { showSection } from '../ui/banner.js';
6
6
 
7
+ // Facilitator root returns service info (no /api/health path)
7
8
  const getURL = () => getServiceURL('facilitator') || 'https://facilitator.darksol.net';
8
9
 
9
10
  export async function facilitatorHealth() {
10
11
  const spin = spinner('Checking facilitator...').start();
11
12
  try {
12
- const data = await fetchJSON(`${getURL()}/api/health`);
13
+ const data = await fetchJSON(`${getURL()}/`);
13
14
  spin.succeed('Facilitator online');
14
15
 
15
- showSection('FACILITATOR STATUS');
16
- kvDisplay(Object.entries(data).map(([k, v]) => [k, typeof v === 'object' ? JSON.stringify(v) : String(v)]));
16
+ showSection('x402 FACILITATOR');
17
+ kvDisplay([
18
+ ['Service', data.service || 'DARKSOL Facilitator'],
19
+ ['Version', data.version || '-'],
20
+ ['Protocol', data.protocol || 'x402'],
21
+ ['Fee', data.fee || '0%'],
22
+ ['Chains', Array.isArray(data.chains) ? data.chains.map(c => c.chain || c).join(', ') : (data.chains || 'Base, Polygon')],
23
+ ['Status', theme.success('● Online')],
24
+ ]);
25
+ if (data.description) {
26
+ console.log('');
27
+ console.log(theme.dim(` ${data.description}`));
28
+ }
29
+ if (Array.isArray(data.chains)) {
30
+ console.log('');
31
+ for (const c of data.chains) {
32
+ console.log(` ${theme.gold((c.chain || '').padEnd(10))} ${theme.dim(c.facilitator || '')} ${c.status === 'operational' ? theme.success('●') : theme.error('○')} ${c.settlements || 0} settlements, $${c.volumeUSDC || '0'} USDC`);
33
+ }
34
+ } else if (data.contracts) {
35
+ console.log('');
36
+ for (const [chain, addr] of Object.entries(data.contracts)) {
37
+ console.log(` ${theme.gold(chain.padEnd(10))} ${theme.dim(addr)}`);
38
+ }
39
+ }
40
+ console.log('');
41
+ info('Docs: https://acp.darksol.net/facilitator');
17
42
  } catch (err) {
18
43
  spin.fail('Facilitator unreachable');
19
44
  error(err.message);
45
+ info('Check: https://facilitator.darksol.net');
20
46
  }
21
47
  }
22
48
 
23
49
  export async function facilitatorVerify(payment) {
24
50
  const spin = spinner('Verifying payment...').start();
25
51
  try {
26
- const data = await fetchJSON(`${getURL()}/api/verify`, {
52
+ const data = await fetchJSON(`${getURL()}/verify`, {
27
53
  method: 'POST',
28
54
  headers: { 'Content-Type': 'application/json' },
29
55
  body: JSON.stringify(typeof payment === 'string' ? JSON.parse(payment) : payment),
@@ -41,7 +67,7 @@ export async function facilitatorVerify(payment) {
41
67
  export async function facilitatorSettle(payment) {
42
68
  const spin = spinner('Settling on-chain...').start();
43
69
  try {
44
- const data = await fetchJSON(`${getURL()}/api/settle`, {
70
+ const data = await fetchJSON(`${getURL()}/settle`, {
45
71
  method: 'POST',
46
72
  headers: { 'Content-Type': 'application/json' },
47
73
  body: JSON.stringify(typeof payment === 'string' ? JSON.parse(payment) : payment),
@@ -457,7 +457,7 @@ export async function handleCommand(cmd, ws) {
457
457
  return await cmdChatLogs(args, ws);
458
458
  default: {
459
459
  // Fuzzy: if it looks like natural language, route to AI
460
- const nlKeywords = /\b(swap|buy|sell|send|transfer|price|what|how|should|analyze|check|balance|gas|dca|order|card|prepaid|visa|mastercard)\b/i;
460
+ const nlKeywords = /\b(swap|buy|sell|send|transfer|price|what|how|should|analyze|check|balance|gas|dca|order|card|prepaid|visa|mastercard|bet|coinflip|flip|dice|slots|hilo|gamble|play|casino)\b/i;
461
461
  if (nlKeywords.test(cmd)) {
462
462
  return await cmdAI(cmd.split(/\s+/), ws);
463
463
  }
@@ -1138,34 +1138,55 @@ async function cmdOracle(args, ws) {
1138
1138
  }
1139
1139
 
1140
1140
  async function cmdCasino(args, ws) {
1141
+ ws.sendLine(`${ANSI.gold} ◆ THE CLAWSINO 🎰${ANSI.reset}`);
1142
+ ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
1141
1143
  try {
1142
- const resp = await fetch('https://casino.darksol.net/health');
1144
+ const resp = await fetch('https://casino.darksol.net/api/stats');
1145
+ const ct = resp.headers.get('content-type') || '';
1146
+ if (!ct.includes('json')) throw new Error('not json');
1143
1147
  const data = await resp.json();
1144
1148
 
1145
- ws.sendLine(`${ANSI.gold} CASINO${ANSI.reset}`);
1146
- ws.sendLine(`${ANSI.dim} ${''.repeat(50)}${ANSI.reset}`);
1147
- ws.sendLine(` ${ANSI.darkGold}Status${ANSI.reset} ${data.status || 'unknown'}`);
1148
- ws.sendLine(` ${ANSI.darkGold}Endpoint${ANSI.reset} ${ANSI.blue}casino.darksol.net${ANSI.reset}`);
1149
+ ws.sendLine(` ${ANSI.darkGold}Status${ANSI.reset} ${data.acceptingBets ? `${ANSI.green} Open${ANSI.reset}` : `${ANSI.red}○ Closed${ANSI.reset}`}`);
1150
+ ws.sendLine(` ${ANSI.darkGold}House${ANSI.reset} ${ANSI.white}$${data.houseBalanceUsdc || '0'} USDC${ANSI.reset}`);
1151
+ ws.sendLine(` ${ANSI.darkGold}Total Bets${ANSI.reset} ${ANSI.white}${data.totalBets || 0}${ANSI.reset}`);
1152
+ ws.sendLine(` ${ANSI.darkGold}Win Rate${ANSI.reset} ${ANSI.white}${data.winRate || '0%'}${ANSI.reset}`);
1153
+ ws.sendLine('');
1154
+ ws.sendLine(` ${ANSI.gold}GAMES${ANSI.reset} ${ANSI.dim}All bets $1 USDC • 5% house edge${ANSI.reset}`);
1155
+ ws.sendLine(` ${ANSI.white}🪙 Coin Flip${ANSI.reset} ${ANSI.dim}1.90x — heads or tails${ANSI.reset}`);
1156
+ ws.sendLine(` ${ANSI.white}🎲 Dice${ANSI.reset} ${ANSI.dim}variable — over/under 2-5${ANSI.reset}`);
1157
+ ws.sendLine(` ${ANSI.white}🃏 Hi-Lo${ANSI.reset} ${ANSI.dim}~2.06x — higher or lower${ANSI.reset}`);
1158
+ ws.sendLine(` ${ANSI.white}🎰 Slots${ANSI.reset} ${ANSI.dim}1.50-5.00x — match symbols${ANSI.reset}`);
1159
+ ws.sendLine('');
1160
+ ws.sendLine(` ${ANSI.dim}Play via CLI: darksol casino bet${ANSI.reset}`);
1161
+ ws.sendLine(` ${ANSI.dim}Or ask AI: "flip a coin" / "bet on heads"${ANSI.reset}`);
1149
1162
  ws.sendLine('');
1150
1163
  } catch {
1151
- ws.sendLine(` ${ANSI.dim}Casino unreachable${ANSI.reset}`);
1164
+ ws.sendLine(` ${ANSI.red}Casino unreachable${ANSI.reset}`);
1165
+ ws.sendLine(` ${ANSI.dim}Check: https://casino.darksol.net/docs${ANSI.reset}`);
1152
1166
  ws.sendLine('');
1153
1167
  }
1154
1168
  return {};
1155
1169
  }
1156
1170
 
1157
1171
  async function cmdFacilitator(args, ws) {
1172
+ ws.sendLine(`${ANSI.gold} ◆ x402 FACILITATOR${ANSI.reset}`);
1173
+ ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
1158
1174
  try {
1159
- const resp = await fetch('https://facilitator.darksol.net/health');
1175
+ const resp = await fetch('https://facilitator.darksol.net/');
1176
+ const ct = resp.headers.get('content-type') || '';
1177
+ if (!ct.includes('json')) throw new Error('not json');
1160
1178
  const data = await resp.json();
1161
1179
 
1162
- ws.sendLine(`${ANSI.gold} FACILITATOR${ANSI.reset}`);
1163
- ws.sendLine(`${ANSI.dim} ${''.repeat(50)}${ANSI.reset}`);
1164
- ws.sendLine(` ${ANSI.darkGold}Status${ANSI.reset} ${data.status || 'unknown'}`);
1165
- ws.sendLine(` ${ANSI.darkGold}Endpoint${ANSI.reset} ${ANSI.blue}facilitator.darksol.net${ANSI.reset}`);
1180
+ ws.sendLine(` ${ANSI.darkGold}Status${ANSI.reset} ${ANSI.green}● Online${ANSI.reset}`);
1181
+ ws.sendLine(` ${ANSI.darkGold}Service${ANSI.reset} ${ANSI.white}${data.service || 'DARKSOL Facilitator'}${ANSI.reset}`);
1182
+ ws.sendLine(` ${ANSI.darkGold}Protocol${ANSI.reset} ${ANSI.white}${data.protocol || 'x402'}${ANSI.reset}`);
1183
+ ws.sendLine(` ${ANSI.darkGold}Fee${ANSI.reset} ${ANSI.green}${data.fee || '0%'}${ANSI.reset}`);
1184
+ ws.sendLine(` ${ANSI.darkGold}Chains${ANSI.reset} ${ANSI.white}${Array.isArray(data.chains) ? data.chains.map(c => `${c.chain} (${c.status})`) .join(', ') : 'Base, Polygon'}${ANSI.reset}`);
1185
+ ws.sendLine(` ${ANSI.darkGold}Docs${ANSI.reset} ${ANSI.blue}acp.darksol.net/facilitator${ANSI.reset}`);
1166
1186
  ws.sendLine('');
1167
1187
  } catch {
1168
- ws.sendLine(` ${ANSI.dim}Facilitator unreachable${ANSI.reset}`);
1188
+ ws.sendLine(` ${ANSI.red}Facilitator unreachable${ANSI.reset}`);
1189
+ ws.sendLine(` ${ANSI.dim}Check: https://acp.darksol.net/facilitator${ANSI.reset}`);
1169
1190
  ws.sendLine('');
1170
1191
  }
1171
1192
  return {};