@darksol/terminal 0.4.1 → 0.4.3
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 +27 -2
- package/package.json +3 -2
- package/src/cli.js +54 -1
- package/src/services/builders.js +4 -7
- package/src/services/cards.js +7 -8
- package/src/services/casino.js +6 -8
- package/src/services/facilitator.js +4 -7
- package/src/services/oracle.js +6 -10
- package/src/trading/swap.js +28 -0
- package/src/ui/banner.js +9 -17
- package/src/utils/fetch.js +28 -0
- package/src/web/commands.js +532 -0
- package/src/web/index.html +49 -0
- package/src/web/server.js +247 -0
- package/src/web/terminal.css +177 -0
- package/src/web/terminal.js +262 -0
package/README.md
CHANGED
|
@@ -30,15 +30,30 @@ darksol
|
|
|
30
30
|
# Create a wallet (AES-256-GCM encrypted)
|
|
31
31
|
darksol wallet create main
|
|
32
32
|
|
|
33
|
-
# Check balance
|
|
33
|
+
# Check balance + multi-chain portfolio
|
|
34
34
|
darksol wallet balance
|
|
35
|
+
darksol portfolio
|
|
35
36
|
|
|
36
|
-
#
|
|
37
|
+
# Token prices & live monitoring
|
|
38
|
+
darksol price ETH AERO VIRTUAL
|
|
39
|
+
darksol watch AERO --above 2.0
|
|
40
|
+
|
|
41
|
+
# Gas estimates
|
|
42
|
+
darksol gas base
|
|
43
|
+
|
|
44
|
+
# Swap tokens (Uniswap V3 with slippage protection)
|
|
37
45
|
darksol trade swap -i ETH -o USDC -a 0.1
|
|
38
46
|
|
|
39
47
|
# AI trading assistant
|
|
40
48
|
darksol ai chat
|
|
41
49
|
|
|
50
|
+
# Agent email
|
|
51
|
+
darksol mail setup
|
|
52
|
+
darksol mail send --to user@example.com --subject "Hello"
|
|
53
|
+
|
|
54
|
+
# Web terminal in browser
|
|
55
|
+
darksol serve
|
|
56
|
+
|
|
42
57
|
# Start agent signer for OpenClaw
|
|
43
58
|
darksol agent start main
|
|
44
59
|
```
|
|
@@ -58,6 +73,16 @@ darksol agent start main
|
|
|
58
73
|
| `market` | Market intel, top movers, token analysis | x402 micropayments |
|
|
59
74
|
| `oracle` | On-chain random number oracle | $0.05–$0.25 |
|
|
60
75
|
| `casino` | The Clawsino — on-chain betting | $1 flat bets |
|
|
76
|
+
| `portfolio` | Multi-chain balance view (5 EVM chains) | Free |
|
|
77
|
+
| `gas` | Gas prices & cost estimates | Free |
|
|
78
|
+
| `price` | Quick token price check (DexScreener) | Free |
|
|
79
|
+
| `watch` | Live price monitoring with alerts | Free |
|
|
80
|
+
| `history` | Transaction history via block explorers | Free |
|
|
81
|
+
| `mail` | AgentMail — email for AI agents | Free tier |
|
|
82
|
+
| `serve` | Web terminal in browser (xterm.js) | Free |
|
|
83
|
+
| `facilitator` | x402 payment facilitator | Free |
|
|
84
|
+
| `cards` | Prepaid Visa/MC cards | Service fees |
|
|
85
|
+
| `builders` | ERC-8021 builder code directory | Free |
|
|
61
86
|
| `cards` | Crypto → prepaid Visa/MC (no KYC) | 3% markup |
|
|
62
87
|
| `builders` | ERC-8021 builder leaderboard | Free |
|
|
63
88
|
| `facilitator` | x402 payment verification & settlement | Free |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darksol/terminal",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
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": {
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
"open": "^11.0.0",
|
|
44
44
|
"ora": "^8.1.0",
|
|
45
45
|
"terminal-link": "^3.0.0",
|
|
46
|
-
"update-notifier": "^7.3.1"
|
|
46
|
+
"update-notifier": "^7.3.1",
|
|
47
|
+
"ws": "^8.19.0"
|
|
47
48
|
},
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">=18.0.0"
|
package/src/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { showHistory } from './wallet/history.js';
|
|
|
9
9
|
import { showGas } from './services/gas.js';
|
|
10
10
|
import { watchPrice, checkPrices } from './services/watch.js';
|
|
11
11
|
import { mailSetup, mailCreate, mailInboxes, mailSend, mailList, mailRead, mailReply, mailForward, mailThreads, mailDelete, mailUse, mailStats, mailStatus } from './services/mail.js';
|
|
12
|
+
import { startWebShell } from './web/server.js';
|
|
12
13
|
import { executeSwap } from './trading/swap.js';
|
|
13
14
|
import { snipeToken, watchSnipe } from './trading/snipe.js';
|
|
14
15
|
import { createDCA, listDCA, cancelDCA, runDCA } from './trading/dca.js';
|
|
@@ -25,6 +26,9 @@ import { parseIntent, startChat, adviseStrategy, analyzeToken, executeIntent } f
|
|
|
25
26
|
import { startAgentSigner, showAgentDocs } from './wallet/agent-signer.js';
|
|
26
27
|
import { listSkills, installSkill, skillInfo, uninstallSkill } from './services/skills.js';
|
|
27
28
|
import { runSetupWizard, checkFirstRun } from './setup/wizard.js';
|
|
29
|
+
import { createRequire } from 'module';
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const { version: PKG_VERSION } = require('../package.json');
|
|
28
32
|
|
|
29
33
|
export function cli(argv) {
|
|
30
34
|
const program = new Command();
|
|
@@ -32,7 +36,7 @@ export function cli(argv) {
|
|
|
32
36
|
program
|
|
33
37
|
.name('darksol')
|
|
34
38
|
.description(theme.gold('DARKSOL Terminal') + theme.dim(' — Ghost in the machine with teeth 🌑'))
|
|
35
|
-
.version(
|
|
39
|
+
.version(PKG_VERSION)
|
|
36
40
|
;
|
|
37
41
|
|
|
38
42
|
// ═══════════════════════════════════════
|
|
@@ -398,6 +402,16 @@ export function cli(argv) {
|
|
|
398
402
|
.description('Delete an inbox')
|
|
399
403
|
.action((id) => mailDelete(id));
|
|
400
404
|
|
|
405
|
+
// ═══════════════════════════════════════
|
|
406
|
+
// WEB SHELL
|
|
407
|
+
// ═══════════════════════════════════════
|
|
408
|
+
program
|
|
409
|
+
.command('serve')
|
|
410
|
+
.description('🌐 Launch web terminal in browser')
|
|
411
|
+
.option('-p, --port <port>', 'Server port', '18791')
|
|
412
|
+
.option('--no-open', 'Don\'t auto-open browser')
|
|
413
|
+
.action((opts) => startWebShell(opts));
|
|
414
|
+
|
|
401
415
|
// ═══════════════════════════════════════
|
|
402
416
|
// PORTFOLIO SHORTCUT
|
|
403
417
|
// ═══════════════════════════════════════
|
|
@@ -710,6 +724,8 @@ export function cli(argv) {
|
|
|
710
724
|
['Output', cfg.output],
|
|
711
725
|
['Slippage', `${cfg.slippage}%`],
|
|
712
726
|
['Gas Multiplier', `${cfg.gasMultiplier}x`],
|
|
727
|
+
['Mail', cfg.mailEmail || theme.dim('(not set)')],
|
|
728
|
+
['Version', PKG_VERSION],
|
|
713
729
|
['Config File', configPath()],
|
|
714
730
|
]);
|
|
715
731
|
console.log('');
|
|
@@ -776,6 +792,42 @@ export function cli(argv) {
|
|
|
776
792
|
}
|
|
777
793
|
});
|
|
778
794
|
|
|
795
|
+
// ═══════════════════════════════════════
|
|
796
|
+
// FUZZY / NATURAL LANGUAGE FALLBACK
|
|
797
|
+
// ═══════════════════════════════════════
|
|
798
|
+
// If someone types something Commander doesn't recognize,
|
|
799
|
+
// try routing it through AI before saying "unknown command"
|
|
800
|
+
program.on('command:*', async (operands) => {
|
|
801
|
+
const input = operands.join(' ');
|
|
802
|
+
const { hasAnyLLM } = await import('./config/keys.js');
|
|
803
|
+
|
|
804
|
+
if (hasAnyLLM()) {
|
|
805
|
+
const { parseIntent } = await import('./llm/intent.js');
|
|
806
|
+
const { info, error: showError } = await import('./ui/components.js');
|
|
807
|
+
|
|
808
|
+
console.log('');
|
|
809
|
+
info(`"${input}" isn't a command — asking AI...`);
|
|
810
|
+
console.log('');
|
|
811
|
+
|
|
812
|
+
try {
|
|
813
|
+
const result = await parseIntent(input);
|
|
814
|
+
if (result && result.action !== 'error' && result.action !== 'unknown') {
|
|
815
|
+
const { executeIntent } = await import('./llm/intent.js');
|
|
816
|
+
await executeIntent(result);
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
} catch {}
|
|
820
|
+
|
|
821
|
+
showError(`Unknown command: ${input}`);
|
|
822
|
+
info('Try: darksol help');
|
|
823
|
+
} else {
|
|
824
|
+
const { error: showError, info } = await import('./ui/components.js');
|
|
825
|
+
showError(`Unknown command: ${input}`);
|
|
826
|
+
info('Tip: Set up AI (darksol setup) for natural language commands');
|
|
827
|
+
info('Run: darksol help');
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
|
|
779
831
|
program.parse(argv);
|
|
780
832
|
}
|
|
781
833
|
|
|
@@ -932,6 +984,7 @@ function showCommandList() {
|
|
|
932
984
|
['mail', 'AgentMail — email for your agent'],
|
|
933
985
|
['facilitator', 'x402 payment facilitator'],
|
|
934
986
|
['skills', 'Agent skill directory'],
|
|
987
|
+
['serve', 'Launch web terminal in browser'],
|
|
935
988
|
['setup', 'Re-run setup wizard'],
|
|
936
989
|
['config', 'Terminal configuration'],
|
|
937
990
|
];
|
package/src/services/builders.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { fetchJSON } from '../utils/fetch.js';
|
|
2
2
|
import { getServiceURL } from '../config/store.js';
|
|
3
3
|
import { theme } from '../ui/theme.js';
|
|
4
4
|
import { spinner, table, kvDisplay, error } from '../ui/components.js';
|
|
@@ -9,8 +9,7 @@ const getURL = () => getServiceURL('builders') || 'https://builders.darksol.net'
|
|
|
9
9
|
export async function buildersLeaderboard(opts = {}) {
|
|
10
10
|
const spin = spinner('Loading builder leaderboard...').start();
|
|
11
11
|
try {
|
|
12
|
-
const
|
|
13
|
-
const data = await resp.json();
|
|
12
|
+
const data = await fetchJSON(`${getURL()}/api/leaderboard?limit=${opts.limit || 20}`);
|
|
14
13
|
spin.succeed('Leaderboard loaded');
|
|
15
14
|
|
|
16
15
|
showSection('ERC-8021 BUILDER LEADERBOARD');
|
|
@@ -34,8 +33,7 @@ export async function buildersLeaderboard(opts = {}) {
|
|
|
34
33
|
export async function buildersLookup(code) {
|
|
35
34
|
const spin = spinner(`Looking up builder: ${code}...`).start();
|
|
36
35
|
try {
|
|
37
|
-
const
|
|
38
|
-
const data = await resp.json();
|
|
36
|
+
const data = await fetchJSON(`${getURL()}/api/builders/${code}`);
|
|
39
37
|
spin.succeed('Builder found');
|
|
40
38
|
|
|
41
39
|
showSection(`BUILDER — ${code}`);
|
|
@@ -49,8 +47,7 @@ export async function buildersLookup(code) {
|
|
|
49
47
|
export async function buildersFeed(opts = {}) {
|
|
50
48
|
const spin = spinner('Loading builder feed...').start();
|
|
51
49
|
try {
|
|
52
|
-
const
|
|
53
|
-
const data = await resp.json();
|
|
50
|
+
const data = await fetchJSON(`${getURL()}/api/feed?limit=${opts.limit || 20}`);
|
|
54
51
|
spin.succeed('Feed loaded');
|
|
55
52
|
|
|
56
53
|
showSection('BUILDER FEED');
|
package/src/services/cards.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
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, table } from '../ui/components.js';
|
|
4
|
+
import { spinner, kvDisplay, error, info, table } from '../ui/components.js';
|
|
5
5
|
import { showSection } from '../ui/banner.js';
|
|
6
6
|
|
|
7
7
|
const getURL = () => getServiceURL('cards') || 'https://acp.darksol.net/cards';
|
|
@@ -9,8 +9,7 @@ const getURL = () => getServiceURL('cards') || 'https://acp.darksol.net/cards';
|
|
|
9
9
|
export async function cardsCatalog() {
|
|
10
10
|
const spin = spinner('Loading card catalog...').start();
|
|
11
11
|
try {
|
|
12
|
-
const
|
|
13
|
-
const data = await resp.json();
|
|
12
|
+
const data = await fetchJSON(`${getURL()}/api/cards/catalog`);
|
|
14
13
|
spin.succeed('Catalog loaded');
|
|
15
14
|
|
|
16
15
|
showSection('PREPAID CARDS');
|
|
@@ -29,18 +28,18 @@ export async function cardsCatalog() {
|
|
|
29
28
|
} catch (err) {
|
|
30
29
|
spin.fail('Catalog failed');
|
|
31
30
|
error(err.message);
|
|
31
|
+
info('Cards service: https://acp.darksol.net/cards');
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export async function cardsOrder(provider, amount) {
|
|
36
36
|
const spin = spinner('Processing card order...').start();
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
38
|
+
const data = await fetchJSON(`${getURL()}/api/cards/order`, {
|
|
39
39
|
method: 'POST',
|
|
40
40
|
headers: { 'Content-Type': 'application/json' },
|
|
41
41
|
body: JSON.stringify({ provider, amount }),
|
|
42
42
|
});
|
|
43
|
-
const data = await resp.json();
|
|
44
43
|
spin.succeed('Order placed');
|
|
45
44
|
|
|
46
45
|
showSection('CARD ORDER');
|
|
@@ -48,14 +47,14 @@ export async function cardsOrder(provider, amount) {
|
|
|
48
47
|
} catch (err) {
|
|
49
48
|
spin.fail('Order failed');
|
|
50
49
|
error(err.message);
|
|
50
|
+
info('The cards order API may not be live yet. Check: https://acp.darksol.net/cards');
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export async function cardsStatus(orderId) {
|
|
55
55
|
const spin = spinner('Checking order...').start();
|
|
56
56
|
try {
|
|
57
|
-
const
|
|
58
|
-
const data = await resp.json();
|
|
57
|
+
const data = await fetchJSON(`${getURL()}/api/cards/status?orderId=${orderId}`);
|
|
59
58
|
spin.succeed('Status loaded');
|
|
60
59
|
|
|
61
60
|
showSection(`CARD ORDER — ${orderId}`);
|
package/src/services/casino.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fetchJSON } from '../utils/fetch.js';
|
|
1
2
|
import fetch from 'node-fetch';
|
|
2
3
|
import { getServiceURL } from '../config/store.js';
|
|
3
4
|
import { theme } from '../ui/theme.js';
|
|
@@ -9,7 +10,7 @@ const getURL = () => getServiceURL('casino') || 'https://casino.darksol.net';
|
|
|
9
10
|
export async function casinoBet(game, opts = {}) {
|
|
10
11
|
const spin = spinner(`Placing ${game} bet...`).start();
|
|
11
12
|
try {
|
|
12
|
-
const
|
|
13
|
+
const data = await fetchJSON(`${getURL()}/api/bet`, {
|
|
13
14
|
method: 'POST',
|
|
14
15
|
headers: { 'Content-Type': 'application/json' },
|
|
15
16
|
body: JSON.stringify({
|
|
@@ -19,7 +20,6 @@ export async function casinoBet(game, opts = {}) {
|
|
|
19
20
|
wallet: opts.wallet,
|
|
20
21
|
}),
|
|
21
22
|
});
|
|
22
|
-
const data = await resp.json();
|
|
23
23
|
spin.succeed('Bet placed');
|
|
24
24
|
|
|
25
25
|
showSection(`CASINO — ${game.toUpperCase()}`);
|
|
@@ -40,8 +40,7 @@ export async function casinoBet(game, opts = {}) {
|
|
|
40
40
|
export async function casinoTables() {
|
|
41
41
|
const spin = spinner('Loading tables...').start();
|
|
42
42
|
try {
|
|
43
|
-
const
|
|
44
|
-
const data = await resp.json();
|
|
43
|
+
const data = await fetchJSON(`${getURL()}/api/tables`);
|
|
45
44
|
spin.succeed('Tables loaded');
|
|
46
45
|
|
|
47
46
|
showSection('CASINO TABLES');
|
|
@@ -66,8 +65,7 @@ export async function casinoTables() {
|
|
|
66
65
|
export async function casinoStats() {
|
|
67
66
|
const spin = spinner('Loading stats...').start();
|
|
68
67
|
try {
|
|
69
|
-
const
|
|
70
|
-
const data = await resp.json();
|
|
68
|
+
const data = await fetchJSON(`${getURL()}/api/stats`);
|
|
71
69
|
spin.succeed('Stats loaded');
|
|
72
70
|
|
|
73
71
|
showSection('CASINO STATS');
|
|
@@ -81,8 +79,7 @@ export async function casinoStats() {
|
|
|
81
79
|
export async function casinoReceipt(id) {
|
|
82
80
|
const spin = spinner(`Loading receipt ${id}...`).start();
|
|
83
81
|
try {
|
|
84
|
-
const
|
|
85
|
-
const data = await resp.json();
|
|
82
|
+
const data = await fetchJSON(`${getURL()}/api/receipt/${id}`);
|
|
86
83
|
spin.succeed('Receipt loaded');
|
|
87
84
|
|
|
88
85
|
showSection(`CASINO RECEIPT — ${id}`);
|
|
@@ -92,3 +89,4 @@ export async function casinoReceipt(id) {
|
|
|
92
89
|
error(err.message);
|
|
93
90
|
}
|
|
94
91
|
}
|
|
92
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { fetchJSON } from '../utils/fetch.js';
|
|
2
2
|
import { getServiceURL } from '../config/store.js';
|
|
3
3
|
import { theme } from '../ui/theme.js';
|
|
4
4
|
import { spinner, kvDisplay, error } from '../ui/components.js';
|
|
@@ -9,8 +9,7 @@ const getURL = () => getServiceURL('facilitator') || 'https://facilitator.darkso
|
|
|
9
9
|
export async function facilitatorHealth() {
|
|
10
10
|
const spin = spinner('Checking facilitator...').start();
|
|
11
11
|
try {
|
|
12
|
-
const
|
|
13
|
-
const data = await resp.json();
|
|
12
|
+
const data = await fetchJSON(`${getURL()}/api/health`);
|
|
14
13
|
spin.succeed('Facilitator online');
|
|
15
14
|
|
|
16
15
|
showSection('FACILITATOR STATUS');
|
|
@@ -24,12 +23,11 @@ export async function facilitatorHealth() {
|
|
|
24
23
|
export async function facilitatorVerify(payment) {
|
|
25
24
|
const spin = spinner('Verifying payment...').start();
|
|
26
25
|
try {
|
|
27
|
-
const
|
|
26
|
+
const data = await fetchJSON(`${getURL()}/api/verify`, {
|
|
28
27
|
method: 'POST',
|
|
29
28
|
headers: { 'Content-Type': 'application/json' },
|
|
30
29
|
body: JSON.stringify(typeof payment === 'string' ? JSON.parse(payment) : payment),
|
|
31
30
|
});
|
|
32
|
-
const data = await resp.json();
|
|
33
31
|
spin.succeed(data.valid ? 'Payment valid' : 'Payment invalid');
|
|
34
32
|
|
|
35
33
|
showSection('PAYMENT VERIFICATION');
|
|
@@ -43,12 +41,11 @@ export async function facilitatorVerify(payment) {
|
|
|
43
41
|
export async function facilitatorSettle(payment) {
|
|
44
42
|
const spin = spinner('Settling on-chain...').start();
|
|
45
43
|
try {
|
|
46
|
-
const
|
|
44
|
+
const data = await fetchJSON(`${getURL()}/api/settle`, {
|
|
47
45
|
method: 'POST',
|
|
48
46
|
headers: { 'Content-Type': 'application/json' },
|
|
49
47
|
body: JSON.stringify(typeof payment === 'string' ? JSON.parse(payment) : payment),
|
|
50
48
|
});
|
|
51
|
-
const data = await resp.json();
|
|
52
49
|
spin.succeed('Settlement complete');
|
|
53
50
|
|
|
54
51
|
showSection('SETTLEMENT');
|
package/src/services/oracle.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fetchJSON } from '../utils/fetch.js';
|
|
1
2
|
import fetch from 'node-fetch';
|
|
2
3
|
import { getServiceURL } from '../config/store.js';
|
|
3
4
|
import { theme } from '../ui/theme.js';
|
|
@@ -9,8 +10,7 @@ const getURL = () => getServiceURL('oracle') || 'https://acp.darksol.net/oracle'
|
|
|
9
10
|
export async function oracleFlip() {
|
|
10
11
|
const spin = spinner('Flipping coin...').start();
|
|
11
12
|
try {
|
|
12
|
-
const
|
|
13
|
-
const data = await resp.json();
|
|
13
|
+
const data = await fetchJSON(`${getURL()}/api/coin`);
|
|
14
14
|
spin.succeed('Coin flipped');
|
|
15
15
|
showSection('ORACLE — COIN FLIP');
|
|
16
16
|
kvDisplay([
|
|
@@ -26,8 +26,7 @@ export async function oracleFlip() {
|
|
|
26
26
|
export async function oracleDice(sides = 6) {
|
|
27
27
|
const spin = spinner(`Rolling d${sides}...`).start();
|
|
28
28
|
try {
|
|
29
|
-
const
|
|
30
|
-
const data = await resp.json();
|
|
29
|
+
const data = await fetchJSON(`${getURL()}/api/dice?sides=${sides}`);
|
|
31
30
|
spin.succeed('Dice rolled');
|
|
32
31
|
showSection(`ORACLE — D${sides}`);
|
|
33
32
|
kvDisplay([
|
|
@@ -44,8 +43,7 @@ export async function oracleDice(sides = 6) {
|
|
|
44
43
|
export async function oracleNumber(min = 1, max = 100) {
|
|
45
44
|
const spin = spinner(`Generating number ${min}-${max}...`).start();
|
|
46
45
|
try {
|
|
47
|
-
const
|
|
48
|
-
const data = await resp.json();
|
|
46
|
+
const data = await fetchJSON(`${getURL()}/api/number?min=${min}&max=${max}`);
|
|
49
47
|
spin.succeed('Number generated');
|
|
50
48
|
showSection('ORACLE — RANDOM NUMBER');
|
|
51
49
|
kvDisplay([
|
|
@@ -62,12 +60,11 @@ export async function oracleNumber(min = 1, max = 100) {
|
|
|
62
60
|
export async function oracleShuffle(items) {
|
|
63
61
|
const spin = spinner('Shuffling...').start();
|
|
64
62
|
try {
|
|
65
|
-
const
|
|
63
|
+
const data = await fetchJSON(`${getURL()}/api/shuffle`, {
|
|
66
64
|
method: 'POST',
|
|
67
65
|
headers: { 'Content-Type': 'application/json' },
|
|
68
66
|
body: JSON.stringify({ items }),
|
|
69
67
|
});
|
|
70
|
-
const data = await resp.json();
|
|
71
68
|
spin.succeed('Shuffled');
|
|
72
69
|
showSection('ORACLE — SHUFFLE');
|
|
73
70
|
console.log(theme.gold(' Result: ') + (data.result || data.value || []).join(', '));
|
|
@@ -80,8 +77,7 @@ export async function oracleShuffle(items) {
|
|
|
80
77
|
export async function oracleHealth() {
|
|
81
78
|
const spin = spinner('Checking oracle...').start();
|
|
82
79
|
try {
|
|
83
|
-
const
|
|
84
|
-
const data = await resp.json();
|
|
80
|
+
const data = await fetchJSON(`${getURL()}/api/health`);
|
|
85
81
|
spin.succeed('Oracle online');
|
|
86
82
|
showSection('ORACLE STATUS');
|
|
87
83
|
kvDisplay(Object.entries(data).map(([k, v]) => [k, String(v)]));
|
package/src/trading/swap.js
CHANGED
|
@@ -18,6 +18,12 @@ const ROUTERS = {
|
|
|
18
18
|
arbitrum: {
|
|
19
19
|
uniswapV3: '0xE592427A0AEce92De3Edee1F18E0157C05861564',
|
|
20
20
|
},
|
|
21
|
+
optimism: {
|
|
22
|
+
uniswapV3: '0xE592427A0AEce92De3Edee1F18E0157C05861564',
|
|
23
|
+
},
|
|
24
|
+
polygon: {
|
|
25
|
+
uniswapV3: '0xE592427A0AEce92De3Edee1F18E0157C05861564',
|
|
26
|
+
},
|
|
21
27
|
};
|
|
22
28
|
|
|
23
29
|
// Common token addresses per chain
|
|
@@ -38,6 +44,26 @@ const TOKENS = {
|
|
|
38
44
|
USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
39
45
|
DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
|
40
46
|
},
|
|
47
|
+
arbitrum: {
|
|
48
|
+
ETH: ethers.ZeroAddress,
|
|
49
|
+
WETH: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
|
|
50
|
+
USDC: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
|
|
51
|
+
USDT: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
|
|
52
|
+
ARB: '0x912CE59144191C1204E64559FE8253a0e49E6548',
|
|
53
|
+
},
|
|
54
|
+
optimism: {
|
|
55
|
+
ETH: ethers.ZeroAddress,
|
|
56
|
+
WETH: '0x4200000000000000000000000000000000000006',
|
|
57
|
+
USDC: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
|
|
58
|
+
OP: '0x4200000000000000000000000000000000000042',
|
|
59
|
+
},
|
|
60
|
+
polygon: {
|
|
61
|
+
ETH: ethers.ZeroAddress,
|
|
62
|
+
WETH: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
|
|
63
|
+
WMATIC: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
|
|
64
|
+
USDC: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
|
|
65
|
+
USDT: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
|
|
66
|
+
},
|
|
41
67
|
};
|
|
42
68
|
|
|
43
69
|
// ERC20 ABI for approvals and balance checks
|
|
@@ -66,6 +92,8 @@ const QUOTERS = {
|
|
|
66
92
|
base: '0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a',
|
|
67
93
|
ethereum: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
68
94
|
arbitrum: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
95
|
+
optimism: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
96
|
+
polygon: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
69
97
|
};
|
|
70
98
|
|
|
71
99
|
// Resolve token symbol to address
|
package/src/ui/banner.js
CHANGED
|
@@ -2,6 +2,9 @@ import figlet from 'figlet';
|
|
|
2
2
|
import gradient from 'gradient-string';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { theme } from './theme.js';
|
|
5
|
+
import { createRequire } from 'module';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { version } = require('../../package.json');
|
|
5
8
|
|
|
6
9
|
const darksol_gradient = gradient(['#B8860B', '#FFD700', '#FFF8DC', '#FFD700', '#B8860B']);
|
|
7
10
|
|
|
@@ -11,6 +14,9 @@ export function showBanner(opts = {}) {
|
|
|
11
14
|
horizontalLayout: 'fitted',
|
|
12
15
|
});
|
|
13
16
|
|
|
17
|
+
const vStr = `v${version}`;
|
|
18
|
+
const pad = ' '.repeat(Math.max(0, 48 - vStr.length));
|
|
19
|
+
|
|
14
20
|
console.log('');
|
|
15
21
|
console.log(darksol_gradient(banner));
|
|
16
22
|
console.log('');
|
|
@@ -26,8 +32,8 @@ export function showBanner(opts = {}) {
|
|
|
26
32
|
);
|
|
27
33
|
console.log(
|
|
28
34
|
theme.dim(' ║ ') +
|
|
29
|
-
theme.subtle(
|
|
30
|
-
theme.dim(
|
|
35
|
+
theme.subtle(` ${vStr}`) +
|
|
36
|
+
theme.dim(pad) +
|
|
31
37
|
theme.gold('🌑') +
|
|
32
38
|
theme.dim(' ║')
|
|
33
39
|
);
|
|
@@ -44,7 +50,7 @@ export function showBanner(opts = {}) {
|
|
|
44
50
|
|
|
45
51
|
export function showMiniBanner() {
|
|
46
52
|
console.log('');
|
|
47
|
-
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(
|
|
53
|
+
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(` v${version}`));
|
|
48
54
|
console.log(theme.dim(' ─────────────────────────────'));
|
|
49
55
|
console.log('');
|
|
50
56
|
}
|
|
@@ -58,17 +64,3 @@ export function showSection(title) {
|
|
|
58
64
|
export function showDivider() {
|
|
59
65
|
console.log(theme.dim(' ' + '─'.repeat(50)));
|
|
60
66
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fetch JSON safely — handles HTML error pages, non-JSON responses,
|
|
5
|
+
* and invalid JSON gracefully instead of crashing with cryptic errors.
|
|
6
|
+
*/
|
|
7
|
+
export async function fetchJSON(url, options = {}) {
|
|
8
|
+
const resp = await fetch(url, options);
|
|
9
|
+
const contentType = resp.headers.get('content-type') || '';
|
|
10
|
+
const text = await resp.text();
|
|
11
|
+
|
|
12
|
+
if (!contentType.includes('application/json') && !contentType.includes('text/json')) {
|
|
13
|
+
const preview = text.substring(0, 60).replace(/\n/g, ' ');
|
|
14
|
+
throw new Error(
|
|
15
|
+
`Expected JSON but got ${contentType.split(';')[0] || 'unknown'} (HTTP ${resp.status}). ` +
|
|
16
|
+
`Response: "${preview}..."`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(text);
|
|
22
|
+
} catch {
|
|
23
|
+
const preview = text.substring(0, 60).replace(/\n/g, ' ');
|
|
24
|
+
throw new Error(`Invalid JSON (HTTP ${resp.status}): "${preview}..."`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default fetchJSON;
|