@darksol/terminal 0.9.2 → 0.11.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/README.md +111 -1
- package/package.json +4 -1
- package/src/browser/actions.js +58 -0
- package/src/cli.js +327 -24
- package/src/config/keys.js +12 -2
- package/src/daemon/index.js +225 -0
- package/src/daemon/manager.js +148 -0
- package/src/daemon/pid.js +80 -0
- package/src/services/browser.js +659 -0
- package/src/services/poker.js +937 -0
- package/src/services/telegram.js +570 -0
- package/src/web/commands.js +387 -1
- package/src/web/server.js +27 -6
- package/src/web/terminal.js +1 -1
package/src/web/commands.js
CHANGED
|
@@ -10,6 +10,8 @@ import { homedir } from 'os';
|
|
|
10
10
|
import { spawn } from 'child_process';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import { getConfiguredModel, getModelSelectionMeta, getProviderDefaultModel } from '../llm/models.js';
|
|
13
|
+
import { pokerNewGame, pokerAction, pokerStatus, pokerHistory } from '../services/poker.js';
|
|
14
|
+
import { sendBrowserCommand, installPlaywrightBrowsers } from '../services/browser.js';
|
|
13
15
|
|
|
14
16
|
// ══════════════════════════════════════════════════
|
|
15
17
|
// CHAT LOG PERSISTENCE
|
|
@@ -207,6 +209,38 @@ export async function handleMenuSelect(id, value, item, ws) {
|
|
|
207
209
|
].map(i => ({ ...i, meta: { provider: value } })));
|
|
208
210
|
return {};
|
|
209
211
|
|
|
212
|
+
case 'poker_mode':
|
|
213
|
+
if (value === 'back') return {};
|
|
214
|
+
if (value === 'status') return await cmdPoker(['status'], ws);
|
|
215
|
+
if (value === 'history') return await cmdPoker(['history'], ws);
|
|
216
|
+
return await startPokerWebGame(value === 'real' ? 'real' : 'free', ws);
|
|
217
|
+
|
|
218
|
+
case 'poker_action': {
|
|
219
|
+
const gameId = item?.meta?.gameId || pokerSessions.get(ws);
|
|
220
|
+
if (!gameId) {
|
|
221
|
+
ws.sendLine(` ${ANSI.red}No active poker game${ANSI.reset}`);
|
|
222
|
+
ws.sendLine('');
|
|
223
|
+
return {};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const before = pokerStatus(gameId);
|
|
227
|
+
const status = await pokerAction(gameId, value);
|
|
228
|
+
pokerSessions.set(ws, status.id);
|
|
229
|
+
await renderPokerState(status, ws, { previous: before });
|
|
230
|
+
if (status.street !== 'finished' && status.currentActor === 'player') {
|
|
231
|
+
sendPokerActionMenu(status, ws);
|
|
232
|
+
} else if (status.street === 'finished') {
|
|
233
|
+
sendPokerPostHandMenu(status, ws);
|
|
234
|
+
}
|
|
235
|
+
return {};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
case 'poker_post_hand':
|
|
239
|
+
if (value === 'again-free') return await startPokerWebGame('free', ws);
|
|
240
|
+
if (value === 'again-real') return await startPokerWebGame('real', ws);
|
|
241
|
+
if (value === 'history') return await cmdPoker(['history'], ws);
|
|
242
|
+
return {};
|
|
243
|
+
|
|
210
244
|
case 'cards_amount':
|
|
211
245
|
if (value === 'back') return {};
|
|
212
246
|
// Store provider+amount, ask for email
|
|
@@ -882,6 +916,10 @@ export async function handleCommand(cmd, ws) {
|
|
|
882
916
|
return await cmdCards(args, ws);
|
|
883
917
|
case 'casino':
|
|
884
918
|
return await cmdCasino(args, ws);
|
|
919
|
+
case 'poker':
|
|
920
|
+
return await cmdPoker(args, ws);
|
|
921
|
+
case 'browser':
|
|
922
|
+
return await cmdBrowser(args, ws);
|
|
885
923
|
case 'facilitator':
|
|
886
924
|
return await cmdFacilitator(args, ws);
|
|
887
925
|
case 'send':
|
|
@@ -905,7 +943,7 @@ export async function handleCommand(cmd, ws) {
|
|
|
905
943
|
return await cmdChatLogs(args, ws);
|
|
906
944
|
default: {
|
|
907
945
|
// Fuzzy: if it looks like natural language, route to AI
|
|
908
|
-
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|bridge|cross-chain|crosschain)\b/i;
|
|
946
|
+
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|poker|holdem|bridge|cross-chain|crosschain|browser|screenshot|click)\b/i;
|
|
909
947
|
if (nlKeywords.test(cmd)) {
|
|
910
948
|
return await cmdAI(cmd.split(/\s+/), ws);
|
|
911
949
|
}
|
|
@@ -919,6 +957,137 @@ export async function handleCommand(cmd, ws) {
|
|
|
919
957
|
// ══════════════════════════════════════════════════
|
|
920
958
|
// PRICE
|
|
921
959
|
// ══════════════════════════════════════════════════
|
|
960
|
+
async function cmdBrowser(args, ws) {
|
|
961
|
+
const sub = args[0]?.toLowerCase() || 'status';
|
|
962
|
+
|
|
963
|
+
if (sub === 'help') {
|
|
964
|
+
ws.sendLine(`${ANSI.gold} ◆ BROWSER AUTOMATION${ANSI.reset}`);
|
|
965
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
966
|
+
ws.sendLine('');
|
|
967
|
+
ws.sendLine(` ${ANSI.green}browser status${ANSI.reset} ${ANSI.dim}Show browser state${ANSI.reset}`);
|
|
968
|
+
ws.sendLine(` ${ANSI.green}browser navigate https://...${ANSI.reset} ${ANSI.dim}Navigate active page${ANSI.reset}`);
|
|
969
|
+
ws.sendLine(` ${ANSI.green}browser screenshot${ANSI.reset} ${ANSI.dim}Save + refresh browser panel${ANSI.reset}`);
|
|
970
|
+
ws.sendLine(` ${ANSI.green}browser click <selector>${ANSI.reset} ${ANSI.dim}Click an element${ANSI.reset}`);
|
|
971
|
+
ws.sendLine(` ${ANSI.green}browser type <selector> <text>${ANSI.reset} ${ANSI.dim}Type text into an input${ANSI.reset}`);
|
|
972
|
+
ws.sendLine(` ${ANSI.green}browser eval <js>${ANSI.reset} ${ANSI.dim}Run JavaScript in page context${ANSI.reset}`);
|
|
973
|
+
ws.sendLine(` ${ANSI.green}browser close${ANSI.reset} ${ANSI.dim}Close the browser service${ANSI.reset}`);
|
|
974
|
+
ws.sendLine('');
|
|
975
|
+
return {};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (sub === 'install') {
|
|
979
|
+
try {
|
|
980
|
+
await installPlaywrightBrowsers();
|
|
981
|
+
ws.sendLine(` ${ANSI.green}✓ Playwright Chromium install complete${ANSI.reset}`);
|
|
982
|
+
ws.sendLine('');
|
|
983
|
+
} catch (err) {
|
|
984
|
+
ws.sendLine(` ${ANSI.red}✗ ${err.message}${ANSI.reset}`);
|
|
985
|
+
ws.sendLine('');
|
|
986
|
+
}
|
|
987
|
+
return {};
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
try {
|
|
991
|
+
if (sub === 'status') {
|
|
992
|
+
const status = await sendBrowserCommand('status');
|
|
993
|
+
ws.sendLine(`${ANSI.gold} ◆ BROWSER STATUS${ANSI.reset}`);
|
|
994
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
995
|
+
ws.sendLine(` ${ANSI.darkGold}Running${ANSI.reset} ${status.running ? ANSI.green + 'yes' : ANSI.dim + 'no'}${ANSI.reset}`);
|
|
996
|
+
ws.sendLine(` ${ANSI.darkGold}Type${ANSI.reset} ${ANSI.white}${status.browserType || 'chromium'}${ANSI.reset}`);
|
|
997
|
+
ws.sendLine(` ${ANSI.darkGold}Profile${ANSI.reset} ${ANSI.white}${status.profile || 'default'}${ANSI.reset}`);
|
|
998
|
+
ws.sendLine(` ${ANSI.darkGold}Mode${ANSI.reset} ${ANSI.white}${status.headed ? 'headed' : 'headless'}${ANSI.reset}`);
|
|
999
|
+
ws.sendLine(` ${ANSI.darkGold}URL${ANSI.reset} ${status.url ? ANSI.white + status.url : ANSI.dim + '(blank)'}${ANSI.reset}`);
|
|
1000
|
+
ws.sendLine(` ${ANSI.darkGold}Title${ANSI.reset} ${status.title ? ANSI.white + status.title : ANSI.dim + '(none)'}${ANSI.reset}`);
|
|
1001
|
+
ws.sendLine(` ${ANSI.darkGold}Pages${ANSI.reset} ${ANSI.white}${status.pageCount || 0}${ANSI.reset}`);
|
|
1002
|
+
ws.sendLine('');
|
|
1003
|
+
return {};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (sub === 'navigate') {
|
|
1007
|
+
const url = args[1];
|
|
1008
|
+
if (!url) {
|
|
1009
|
+
ws.sendLine(` ${ANSI.dim}Usage: browser navigate <url>${ANSI.reset}`);
|
|
1010
|
+
ws.sendLine('');
|
|
1011
|
+
return {};
|
|
1012
|
+
}
|
|
1013
|
+
const status = await sendBrowserCommand('navigate', { url });
|
|
1014
|
+
ws.sendLine(` ${ANSI.green}✓ Navigated to ${status.url}${ANSI.reset}`);
|
|
1015
|
+
ws.sendLine('');
|
|
1016
|
+
ws.send(JSON.stringify({ type: 'browser:refresh' }));
|
|
1017
|
+
return {};
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
if (sub === 'screenshot') {
|
|
1021
|
+
const result = await sendBrowserCommand('screenshot', {});
|
|
1022
|
+
ws.sendLine(` ${ANSI.green}✓ Screenshot saved${ANSI.reset}`);
|
|
1023
|
+
ws.sendLine(` ${ANSI.dim}${result.path}${ANSI.reset}`);
|
|
1024
|
+
ws.sendLine('');
|
|
1025
|
+
ws.send(JSON.stringify({ type: 'browser:refresh' }));
|
|
1026
|
+
return {};
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (sub === 'click') {
|
|
1030
|
+
const selector = args[1];
|
|
1031
|
+
if (!selector) {
|
|
1032
|
+
ws.sendLine(` ${ANSI.dim}Usage: browser click <selector>${ANSI.reset}`);
|
|
1033
|
+
ws.sendLine('');
|
|
1034
|
+
return {};
|
|
1035
|
+
}
|
|
1036
|
+
await sendBrowserCommand('click', { selector });
|
|
1037
|
+
ws.sendLine(` ${ANSI.green}✓ Clicked ${selector}${ANSI.reset}`);
|
|
1038
|
+
ws.sendLine('');
|
|
1039
|
+
return {};
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (sub === 'type') {
|
|
1043
|
+
const selector = args[1];
|
|
1044
|
+
const text = args.slice(2).join(' ');
|
|
1045
|
+
if (!selector || !text) {
|
|
1046
|
+
ws.sendLine(` ${ANSI.dim}Usage: browser type <selector> <text>${ANSI.reset}`);
|
|
1047
|
+
ws.sendLine('');
|
|
1048
|
+
return {};
|
|
1049
|
+
}
|
|
1050
|
+
await sendBrowserCommand('type', { selector, text });
|
|
1051
|
+
ws.sendLine(` ${ANSI.green}✓ Typed into ${selector}${ANSI.reset}`);
|
|
1052
|
+
ws.sendLine('');
|
|
1053
|
+
return {};
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
if (sub === 'eval') {
|
|
1057
|
+
const expression = args.slice(1).join(' ');
|
|
1058
|
+
if (!expression) {
|
|
1059
|
+
ws.sendLine(` ${ANSI.dim}Usage: browser eval <js>${ANSI.reset}`);
|
|
1060
|
+
ws.sendLine('');
|
|
1061
|
+
return {};
|
|
1062
|
+
}
|
|
1063
|
+
const result = await sendBrowserCommand('eval', { expression });
|
|
1064
|
+
ws.sendLine(`${ANSI.gold} ◆ BROWSER EVAL${ANSI.reset}`);
|
|
1065
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
1066
|
+
ws.sendLine(` ${ANSI.white}${String(result.formatted)}${ANSI.reset}`);
|
|
1067
|
+
ws.sendLine('');
|
|
1068
|
+
return {};
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (sub === 'close') {
|
|
1072
|
+
await sendBrowserCommand('close');
|
|
1073
|
+
ws.sendLine(` ${ANSI.green}✓ Browser closed${ANSI.reset}`);
|
|
1074
|
+
ws.sendLine('');
|
|
1075
|
+
ws.send(JSON.stringify({ type: 'browser:refresh' }));
|
|
1076
|
+
return {};
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
ws.sendLine(` ${ANSI.red}✗ Unknown browser subcommand: ${sub}${ANSI.reset}`);
|
|
1080
|
+
ws.sendLine(` ${ANSI.dim}Try: browser help${ANSI.reset}`);
|
|
1081
|
+
ws.sendLine('');
|
|
1082
|
+
} catch (err) {
|
|
1083
|
+
ws.sendLine(` ${ANSI.red}✗ ${err.message}${ANSI.reset}`);
|
|
1084
|
+
ws.sendLine(` ${ANSI.dim}Start it with: darksol browser launch${ANSI.reset}`);
|
|
1085
|
+
ws.sendLine('');
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
return {};
|
|
1089
|
+
}
|
|
1090
|
+
|
|
922
1091
|
async function cmdPrice(tokens, ws) {
|
|
923
1092
|
if (!tokens.length) {
|
|
924
1093
|
return { output: ` ${ANSI.dim}Usage: price ETH AERO VIRTUAL${ANSI.reset}\r\n` };
|
|
@@ -1778,6 +1947,222 @@ async function cmdOracle(args, ws) {
|
|
|
1778
1947
|
return {};
|
|
1779
1948
|
}
|
|
1780
1949
|
|
|
1950
|
+
function pokerColor(card, text) {
|
|
1951
|
+
if (card === '??') return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
1952
|
+
return card[1] === 'h' || card[1] === 'd'
|
|
1953
|
+
? `${ANSI.red}${text}${ANSI.reset}`
|
|
1954
|
+
: `${ANSI.white}${text}${ANSI.reset}`;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
function pokerCardRows(cards, hidden = false) {
|
|
1958
|
+
const suitMap = { s: '♠', h: '♥', d: '♦', c: '♣' };
|
|
1959
|
+
const source = hidden ? ['??', '??'] : cards;
|
|
1960
|
+
const rows = ['', '', '', '', ''];
|
|
1961
|
+
|
|
1962
|
+
for (const card of source) {
|
|
1963
|
+
if (card === '??') {
|
|
1964
|
+
rows[0] += `${ANSI.dim}┌─────┐${ANSI.reset} `;
|
|
1965
|
+
rows[1] += `${ANSI.dim}│░░░░░│${ANSI.reset} `;
|
|
1966
|
+
rows[2] += `${ANSI.dim}│░░▓░░│${ANSI.reset} `;
|
|
1967
|
+
rows[3] += `${ANSI.dim}│░░░░░│${ANSI.reset} `;
|
|
1968
|
+
rows[4] += `${ANSI.dim}└─────┘${ANSI.reset} `;
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
const rank = card[0] === 'T' ? '10' : card[0];
|
|
1973
|
+
const suit = suitMap[card[1]];
|
|
1974
|
+
rows[0] += `${ANSI.dim}┌─────┐${ANSI.reset} `;
|
|
1975
|
+
rows[1] += `${ANSI.dim}│${ANSI.reset}${pokerColor(card, rank.padEnd(2, ' '))}${ANSI.dim} │${ANSI.reset} `;
|
|
1976
|
+
rows[2] += `${ANSI.dim}│ ${ANSI.reset}${pokerColor(card, suit)}${ANSI.dim} │${ANSI.reset} `;
|
|
1977
|
+
rows[3] += `${ANSI.dim}│ ${ANSI.reset}${pokerColor(card, rank.padStart(2, ' '))}${ANSI.dim}│${ANSI.reset} `;
|
|
1978
|
+
rows[4] += `${ANSI.dim}└─────┘${ANSI.reset} `;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
return rows;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
function sendPokerCards(label, cards, ws, hidden = false) {
|
|
1985
|
+
ws.sendLine(` ${ANSI.darkGold}${label}${ANSI.reset}`);
|
|
1986
|
+
for (const row of pokerCardRows(cards, hidden)) {
|
|
1987
|
+
ws.sendLine(` ${row}`);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
function sendPokerActionMenu(status, ws) {
|
|
1992
|
+
ws.sendMenu('poker_action', `◆ Poker Actions (${status.street})`, status.availableActions.map((action) => ({
|
|
1993
|
+
value: action,
|
|
1994
|
+
label: action,
|
|
1995
|
+
desc: action === 'all-in' ? 'Commit the rest of your stack' : '',
|
|
1996
|
+
meta: { gameId: status.id },
|
|
1997
|
+
})));
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
function sendPokerPostHandMenu(status, ws) {
|
|
2001
|
+
ws.sendMenu('poker_post_hand', '◆ Next Hand', [
|
|
2002
|
+
{ value: 'again-free', label: 'Play Free', desc: 'Start another free hand', meta: { gameId: status.id } },
|
|
2003
|
+
{ value: 'again-real', label: 'Play Real', desc: 'Start another $1 USDC hand', meta: { gameId: status.id } },
|
|
2004
|
+
{ value: 'history', label: 'History', desc: 'Recent poker hands', meta: { gameId: status.id } },
|
|
2005
|
+
{ value: 'back', label: '← Back', desc: '', meta: { gameId: status.id } },
|
|
2006
|
+
]);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
async function renderPokerState(status, ws, opts = {}) {
|
|
2010
|
+
const previous = opts.previous || null;
|
|
2011
|
+
const header = status.mode === 'real'
|
|
2012
|
+
? `${ANSI.gold} ◆ GTO POKER ARENA${ANSI.reset} ${ANSI.dim}REAL MODE · $${status.buyInUsdc} in / $${status.payoutUsdc} out${ANSI.reset}`
|
|
2013
|
+
: `${ANSI.gold} ◆ GTO POKER ARENA${ANSI.reset} ${ANSI.dim}FREE MODE${ANSI.reset}`;
|
|
2014
|
+
|
|
2015
|
+
if (!previous) {
|
|
2016
|
+
ws.sendLine(header);
|
|
2017
|
+
ws.sendLine(` ${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
2018
|
+
ws.sendLine(` ${ANSI.dim}Dealing cards...${ANSI.reset}`);
|
|
2019
|
+
ws.sendLine('');
|
|
2020
|
+
await sleep(120);
|
|
2021
|
+
sendPokerCards('House', ['??'], ws, true);
|
|
2022
|
+
ws.sendLine('');
|
|
2023
|
+
await sleep(120);
|
|
2024
|
+
sendPokerCards('You', [status.player.hole[0]], ws);
|
|
2025
|
+
ws.sendLine('');
|
|
2026
|
+
await sleep(120);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
if (previous && status.community.length > previous.community.length) {
|
|
2030
|
+
const label = status.community.length === 3 ? 'Flop' : status.community.length === 4 ? 'Turn' : 'River';
|
|
2031
|
+
ws.sendLine(` ${ANSI.dim}${label} coming in...${ANSI.reset}`);
|
|
2032
|
+
ws.sendLine('');
|
|
2033
|
+
await sleep(140);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
if (previous && previous.house.holeHidden && !status.house.holeHidden) {
|
|
2037
|
+
ws.sendLine(` ${ANSI.gold} ◆ SHOWDOWN${ANSI.reset}`);
|
|
2038
|
+
ws.sendLine(` ${ANSI.dim}Revealing the house cards...${ANSI.reset}`);
|
|
2039
|
+
ws.sendLine('');
|
|
2040
|
+
await sleep(180);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
ws.sendLine(header);
|
|
2044
|
+
ws.sendLine(` ${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
2045
|
+
ws.sendLine(` ${ANSI.darkGold}Street${ANSI.reset} ${ANSI.white}${status.street.toUpperCase()}${ANSI.reset}`);
|
|
2046
|
+
ws.sendLine(` ${ANSI.darkGold}Pot${ANSI.reset} ${ANSI.white}${status.pot} chips${ANSI.reset}`);
|
|
2047
|
+
ws.sendLine(` ${ANSI.darkGold}Current Bet${ANSI.reset} ${ANSI.white}${status.currentBet} chips${ANSI.reset}`);
|
|
2048
|
+
ws.sendLine(` ${ANSI.darkGold}Your Stack${ANSI.reset} ${ANSI.white}${status.player.stack} chips${ANSI.reset}`);
|
|
2049
|
+
ws.sendLine(` ${ANSI.darkGold}House Stack${ANSI.reset} ${ANSI.white}${status.house.stack} chips${ANSI.reset}`);
|
|
2050
|
+
ws.sendLine('');
|
|
2051
|
+
|
|
2052
|
+
sendPokerCards('House', status.house.hole, ws, status.house.holeHidden);
|
|
2053
|
+
ws.sendLine('');
|
|
2054
|
+
ws.sendLine(` ${ANSI.darkGold}Board${ANSI.reset}`);
|
|
2055
|
+
if (status.community.length) {
|
|
2056
|
+
for (const row of pokerCardRows(status.community)) {
|
|
2057
|
+
ws.sendLine(` ${row}`);
|
|
2058
|
+
}
|
|
2059
|
+
} else {
|
|
2060
|
+
ws.sendLine(` ${ANSI.dim}No community cards yet${ANSI.reset}`);
|
|
2061
|
+
}
|
|
2062
|
+
ws.sendLine('');
|
|
2063
|
+
sendPokerCards('You', status.player.hole, ws);
|
|
2064
|
+
ws.sendLine('');
|
|
2065
|
+
|
|
2066
|
+
if (status.street === 'finished') {
|
|
2067
|
+
const outcome = status.winner === 'player'
|
|
2068
|
+
? `${ANSI.green}YOU WIN${ANSI.reset}`
|
|
2069
|
+
: status.winner === 'house'
|
|
2070
|
+
? `${ANSI.red}HOUSE WINS${ANSI.reset}`
|
|
2071
|
+
: `${ANSI.gold}PUSH${ANSI.reset}`;
|
|
2072
|
+
ws.sendLine(` ${ANSI.darkGold}Result${ANSI.reset} ${outcome}`);
|
|
2073
|
+
ws.sendLine(` ${ANSI.darkGold}Summary${ANSI.reset} ${ANSI.white}${status.summary || '-'}${ANSI.reset}`);
|
|
2074
|
+
ws.sendLine(` ${ANSI.darkGold}Your Hand${ANSI.reset} ${ANSI.white}${status.player.hand?.name || '-'}${ANSI.reset}`);
|
|
2075
|
+
ws.sendLine(` ${ANSI.darkGold}House Hand${ANSI.reset} ${ANSI.white}${status.house.hand?.name || '-'}${ANSI.reset}`);
|
|
2076
|
+
ws.sendLine(` ${ANSI.darkGold}Payout${ANSI.reset} ${ANSI.white}${status.mode === 'real' && status.winner === 'player' ? `$${status.payoutUsdc} USDC` : status.mode === 'real' ? '$0 USDC' : 'fun only'}${ANSI.reset}`);
|
|
2077
|
+
if (status.payment?.payoutTxHash) {
|
|
2078
|
+
ws.sendLine(` ${ANSI.darkGold}Payout TX${ANSI.reset} ${ANSI.white}${status.payment.payoutTxHash.slice(0, 18)}...${ANSI.reset}`);
|
|
2079
|
+
}
|
|
2080
|
+
if (status.payment?.payoutError) {
|
|
2081
|
+
ws.sendLine(` ${ANSI.darkGold}Payout${ANSI.reset} ${ANSI.red}${status.payment.payoutError}${ANSI.reset}`);
|
|
2082
|
+
}
|
|
2083
|
+
ws.sendLine('');
|
|
2084
|
+
return {};
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
if (status.house.lastAction) {
|
|
2088
|
+
ws.sendLine(` ${ANSI.dim}House last action: ${status.house.lastAction}${ANSI.reset}`);
|
|
2089
|
+
}
|
|
2090
|
+
ws.sendLine(` ${ANSI.dim}Available actions: ${status.availableActions.join(', ')}${ANSI.reset}`);
|
|
2091
|
+
ws.sendLine('');
|
|
2092
|
+
return {};
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
async function startPokerWebGame(mode, ws) {
|
|
2096
|
+
const status = await pokerNewGame({ mode });
|
|
2097
|
+
pokerSessions.set(ws, status.id);
|
|
2098
|
+
await renderPokerState(status, ws);
|
|
2099
|
+
if (status.street !== 'finished' && status.currentActor === 'player') {
|
|
2100
|
+
sendPokerActionMenu(status, ws);
|
|
2101
|
+
} else if (status.street === 'finished') {
|
|
2102
|
+
sendPokerPostHandMenu(status, ws);
|
|
2103
|
+
}
|
|
2104
|
+
return {};
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
async function cmdPoker(args, ws) {
|
|
2108
|
+
const sub = (args[0] || '').toLowerCase();
|
|
2109
|
+
|
|
2110
|
+
if (sub === 'free' || sub === 'real') {
|
|
2111
|
+
return await startPokerWebGame(sub, ws);
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
if (sub === 'status') {
|
|
2115
|
+
const status = pokerStatus(pokerSessions.get(ws));
|
|
2116
|
+
if (!status) {
|
|
2117
|
+
ws.sendLine(` ${ANSI.dim}No active poker game${ANSI.reset}`);
|
|
2118
|
+
ws.sendLine('');
|
|
2119
|
+
return {};
|
|
2120
|
+
}
|
|
2121
|
+
await renderPokerState(status, ws);
|
|
2122
|
+
if (status.street !== 'finished' && status.currentActor === 'player') {
|
|
2123
|
+
sendPokerActionMenu(status, ws);
|
|
2124
|
+
} else if (status.street === 'finished') {
|
|
2125
|
+
sendPokerPostHandMenu(status, ws);
|
|
2126
|
+
}
|
|
2127
|
+
return {};
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
if (sub === 'history') {
|
|
2131
|
+
const items = pokerHistory();
|
|
2132
|
+
ws.sendLine(`${ANSI.gold} ◆ POKER HISTORY${ANSI.reset}`);
|
|
2133
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
2134
|
+
if (!items.length) {
|
|
2135
|
+
ws.sendLine(` ${ANSI.dim}No poker hands played yet${ANSI.reset}`);
|
|
2136
|
+
ws.sendLine('');
|
|
2137
|
+
return {};
|
|
2138
|
+
}
|
|
2139
|
+
for (const item of items.slice(0, 10)) {
|
|
2140
|
+
const verdict = item.winner === 'player'
|
|
2141
|
+
? `${ANSI.green}W${ANSI.reset}`
|
|
2142
|
+
: item.winner === 'house'
|
|
2143
|
+
? `${ANSI.red}L${ANSI.reset}`
|
|
2144
|
+
: `${ANSI.gold}P${ANSI.reset}`;
|
|
2145
|
+
ws.sendLine(` ${verdict} ${ANSI.white}${item.mode.toUpperCase().padEnd(5)}${ANSI.reset} ${item.summary}`);
|
|
2146
|
+
}
|
|
2147
|
+
ws.sendLine('');
|
|
2148
|
+
return {};
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
ws.sendLine(`${ANSI.gold} ◆ GTO POKER ARENA${ANSI.reset}`);
|
|
2152
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
2153
|
+
ws.sendLine(` ${ANSI.white}Heads-up Texas Hold'em versus the house.${ANSI.reset}`);
|
|
2154
|
+
ws.sendLine(` ${ANSI.dim}Free mode skips payment checks. Real mode uses x402 with a $1 USDC buy-in.${ANSI.reset}`);
|
|
2155
|
+
ws.sendLine('');
|
|
2156
|
+
ws.sendMenu('poker_mode', '◆ Choose Poker Mode', [
|
|
2157
|
+
{ value: 'free', label: 'Free Mode', desc: 'Play for fun' },
|
|
2158
|
+
{ value: 'real', label: 'Real Mode', desc: '$1 buy-in · $2 payout on win' },
|
|
2159
|
+
{ value: 'status', label: 'Status', desc: 'Show current hand' },
|
|
2160
|
+
{ value: 'history', label: 'History', desc: 'Recent hands' },
|
|
2161
|
+
{ value: 'back', label: '← Back', desc: '' },
|
|
2162
|
+
]);
|
|
2163
|
+
return {};
|
|
2164
|
+
}
|
|
2165
|
+
|
|
1781
2166
|
async function cmdCasino(args, ws) {
|
|
1782
2167
|
ws.sendLine(`${ANSI.gold} ◆ THE CLAWSINO 🎰${ANSI.reset}`);
|
|
1783
2168
|
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
@@ -1919,6 +2304,7 @@ function saveSelectedModel(model, provider = getConfig('llm.provider') || 'opena
|
|
|
1919
2304
|
|
|
1920
2305
|
// Persistent chat engine per WebSocket connection
|
|
1921
2306
|
const chatEngines = new WeakMap();
|
|
2307
|
+
const pokerSessions = new WeakMap();
|
|
1922
2308
|
|
|
1923
2309
|
async function cmdAI(args, ws) {
|
|
1924
2310
|
const input = args.join(' ').trim();
|
package/src/web/server.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
2
|
import { WebSocketServer } from 'ws';
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { dirname, join } from 'path';
|
|
6
6
|
import open from 'open';
|
|
7
7
|
import { theme } from '../ui/theme.js';
|
|
8
8
|
import { getRecentMemories } from '../memory/index.js';
|
|
9
9
|
import { getSoul, hasSoul } from '../soul/index.js';
|
|
10
|
+
import { getBrowserScreenshotPath, sendBrowserCommand } from '../services/browser.js';
|
|
10
11
|
import { createRequire } from 'module';
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
const { version: PKG_VERSION } = require('../../package.json');
|
|
@@ -15,11 +16,11 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
15
16
|
const __dirname = dirname(__filename);
|
|
16
17
|
|
|
17
18
|
// ══════════════════════════════════════════════════
|
|
18
|
-
// DARKSOL WEB SHELL
|
|
19
|
+
// DARKSOL WEB SHELL - Terminal in the browser
|
|
19
20
|
// ══════════════════════════════════════════════════
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* Command handler registry
|
|
23
|
+
* Command handler registry - maps command strings to async functions
|
|
23
24
|
* that return { output: string } with ANSI stripped for web
|
|
24
25
|
*/
|
|
25
26
|
import { handleCommand, getAIStatus } from './commands.js';
|
|
@@ -36,7 +37,7 @@ export async function startWebShell(opts = {}) {
|
|
|
36
37
|
const css = readFileSync(join(__dirname, 'terminal.css'), 'utf-8');
|
|
37
38
|
const js = readFileSync(join(__dirname, 'terminal.js'), 'utf-8');
|
|
38
39
|
|
|
39
|
-
const server = createServer((req, res) => {
|
|
40
|
+
const server = createServer(async (req, res) => {
|
|
40
41
|
try {
|
|
41
42
|
const pathname = req.url.split('?')[0];
|
|
42
43
|
|
|
@@ -52,6 +53,24 @@ export async function startWebShell(opts = {}) {
|
|
|
52
53
|
} else if (pathname === '/health') {
|
|
53
54
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
54
55
|
res.end(JSON.stringify({ status: 'ok', version: PKG_VERSION }));
|
|
56
|
+
} else if (pathname === '/browser/status') {
|
|
57
|
+
try {
|
|
58
|
+
const status = await sendBrowserCommand('status');
|
|
59
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
60
|
+
res.end(JSON.stringify(status));
|
|
61
|
+
} catch {
|
|
62
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
63
|
+
res.end(JSON.stringify({ running: false }));
|
|
64
|
+
}
|
|
65
|
+
} else if (pathname === '/browser/screenshot') {
|
|
66
|
+
const screenshotPath = getBrowserScreenshotPath();
|
|
67
|
+
if (!existsSync(screenshotPath)) {
|
|
68
|
+
res.writeHead(404);
|
|
69
|
+
res.end('No screenshot available');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
res.writeHead(200, { 'Content-Type': 'image/png', 'Cache-Control': 'no-store' });
|
|
73
|
+
res.end(readFileSync(screenshotPath));
|
|
55
74
|
} else {
|
|
56
75
|
res.writeHead(404);
|
|
57
76
|
res.end('Not found');
|
|
@@ -174,7 +193,7 @@ export async function startWebShell(opts = {}) {
|
|
|
174
193
|
ws.send(JSON.stringify({
|
|
175
194
|
type: 'menu',
|
|
176
195
|
id: 'main_menu',
|
|
177
|
-
title: '◆ Help Menu
|
|
196
|
+
title: '◆ Help Menu - Select Command',
|
|
178
197
|
items: [
|
|
179
198
|
{ value: 'ai', label: '🧠 AI Chat', desc: 'Natural language assistant' },
|
|
180
199
|
{ value: 'wallet', label: '👛 Wallet', desc: 'Picker + balance + actions' },
|
|
@@ -185,6 +204,7 @@ export async function startWebShell(opts = {}) {
|
|
|
185
204
|
{ value: 'portfolio', label: '📊 Portfolio', desc: 'Multi-chain balances' },
|
|
186
205
|
{ value: 'trade', label: '🔄 Trade', desc: 'Swap / snipe click-through flows' },
|
|
187
206
|
{ value: 'market', label: '📈 Market', desc: 'Price + liquidity intel' },
|
|
207
|
+
{ value: 'poker', label: '🃏 Poker', desc: 'Heads-up holdem arena' },
|
|
188
208
|
{ value: 'mail', label: '📧 Mail', desc: 'AgentMail status/inbox' },
|
|
189
209
|
{ value: 'cards', label: '💳 Cards', desc: 'Order prepaid Visa/MC' },
|
|
190
210
|
{ value: 'oracle', label: '🎲 Oracle', desc: 'Randomness service' },
|
|
@@ -286,7 +306,7 @@ function getBanner() {
|
|
|
286
306
|
`${gold} ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚══════╝${reset}`,
|
|
287
307
|
'',
|
|
288
308
|
`${dim} ╔══════════════════════════════════════════════════════════╗${reset}`,
|
|
289
|
-
`${dim} ║${reset} ${gold}${white} DARKSOL TERMINAL${reset}${dim}
|
|
309
|
+
`${dim} ║${reset} ${gold}${white} DARKSOL TERMINAL${reset}${dim} - ${reset}${dim}Ghost in the machine with teeth${reset}${dim} ║${reset}`,
|
|
290
310
|
`${dim} ║${reset}${dim} v${PKG_VERSION}${' '.repeat(Math.max(0, 52 - PKG_VERSION.length))}${reset}${gold}🌑${reset}${dim} ║${reset}`,
|
|
291
311
|
`${dim} ╚══════════════════════════════════════════════════════════╝${reset}`,
|
|
292
312
|
'',
|
|
@@ -332,6 +352,7 @@ function getHelp() {
|
|
|
332
352
|
['mail inbox', 'Check email inbox'],
|
|
333
353
|
['oracle roll', 'On-chain random oracle'],
|
|
334
354
|
['casino status', 'Casino status'],
|
|
355
|
+
['poker', 'GTO Poker Arena'],
|
|
335
356
|
['config', 'Show configuration'],
|
|
336
357
|
['', ''],
|
|
337
358
|
['', `${gold}GENERAL${reset}`],
|
package/src/web/terminal.js
CHANGED
|
@@ -16,7 +16,7 @@ const A = {
|
|
|
16
16
|
|
|
17
17
|
const COMMANDS = [
|
|
18
18
|
'ai', 'price', 'watch', 'gas', 'portfolio', 'history', 'market',
|
|
19
|
-
'wallet', 'send', 'receive', 'agent', 'cards', 'mail', 'keys', 'oracle', 'casino',
|
|
19
|
+
'wallet', 'send', 'receive', 'agent', 'cards', 'mail', 'keys', 'oracle', 'casino', 'poker',
|
|
20
20
|
'facilitator', 'config', 'logs', 'help', 'clear', 'banner', 'exit',
|
|
21
21
|
];
|
|
22
22
|
|