@darksol/terminal 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/cli.js +48 -0
- 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/ui/banner.js +5 -2
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darksol/terminal",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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';
|
|
@@ -398,6 +399,16 @@ export function cli(argv) {
|
|
|
398
399
|
.description('Delete an inbox')
|
|
399
400
|
.action((id) => mailDelete(id));
|
|
400
401
|
|
|
402
|
+
// ═══════════════════════════════════════
|
|
403
|
+
// WEB SHELL
|
|
404
|
+
// ═══════════════════════════════════════
|
|
405
|
+
program
|
|
406
|
+
.command('serve')
|
|
407
|
+
.description('🌐 Launch web terminal in browser')
|
|
408
|
+
.option('-p, --port <port>', 'Server port', '18791')
|
|
409
|
+
.option('--no-open', 'Don\'t auto-open browser')
|
|
410
|
+
.action((opts) => startWebShell(opts));
|
|
411
|
+
|
|
401
412
|
// ═══════════════════════════════════════
|
|
402
413
|
// PORTFOLIO SHORTCUT
|
|
403
414
|
// ═══════════════════════════════════════
|
|
@@ -776,6 +787,42 @@ export function cli(argv) {
|
|
|
776
787
|
}
|
|
777
788
|
});
|
|
778
789
|
|
|
790
|
+
// ═══════════════════════════════════════
|
|
791
|
+
// FUZZY / NATURAL LANGUAGE FALLBACK
|
|
792
|
+
// ═══════════════════════════════════════
|
|
793
|
+
// If someone types something Commander doesn't recognize,
|
|
794
|
+
// try routing it through AI before saying "unknown command"
|
|
795
|
+
program.on('command:*', async (operands) => {
|
|
796
|
+
const input = operands.join(' ');
|
|
797
|
+
const { hasAnyLLM } = await import('./config/keys.js');
|
|
798
|
+
|
|
799
|
+
if (hasAnyLLM()) {
|
|
800
|
+
const { parseIntent } = await import('./llm/intent.js');
|
|
801
|
+
const { info, error: showError } = await import('./ui/components.js');
|
|
802
|
+
|
|
803
|
+
console.log('');
|
|
804
|
+
info(`"${input}" isn't a command — asking AI...`);
|
|
805
|
+
console.log('');
|
|
806
|
+
|
|
807
|
+
try {
|
|
808
|
+
const result = await parseIntent(input);
|
|
809
|
+
if (result && result.action !== 'error' && result.action !== 'unknown') {
|
|
810
|
+
const { executeIntent } = await import('./llm/intent.js');
|
|
811
|
+
await executeIntent(result);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
} catch {}
|
|
815
|
+
|
|
816
|
+
showError(`Unknown command: ${input}`);
|
|
817
|
+
info('Try: darksol help');
|
|
818
|
+
} else {
|
|
819
|
+
const { error: showError, info } = await import('./ui/components.js');
|
|
820
|
+
showError(`Unknown command: ${input}`);
|
|
821
|
+
info('Tip: Set up AI (darksol setup) for natural language commands');
|
|
822
|
+
info('Run: darksol help');
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
|
|
779
826
|
program.parse(argv);
|
|
780
827
|
}
|
|
781
828
|
|
|
@@ -932,6 +979,7 @@ function showCommandList() {
|
|
|
932
979
|
['mail', 'AgentMail — email for your agent'],
|
|
933
980
|
['facilitator', 'x402 payment facilitator'],
|
|
934
981
|
['skills', 'Agent skill directory'],
|
|
982
|
+
['serve', 'Launch web terminal in browser'],
|
|
935
983
|
['setup', 'Re-run setup wizard'],
|
|
936
984
|
['config', 'Terminal configuration'],
|
|
937
985
|
];
|
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/ui/banner.js
CHANGED
|
@@ -26,7 +26,7 @@ export function showBanner(opts = {}) {
|
|
|
26
26
|
);
|
|
27
27
|
console.log(
|
|
28
28
|
theme.dim(' ║ ') +
|
|
29
|
-
theme.subtle(' v0.4.
|
|
29
|
+
theme.subtle(' v0.4.2') +
|
|
30
30
|
theme.dim(' ') +
|
|
31
31
|
theme.gold('🌑') +
|
|
32
32
|
theme.dim(' ║')
|
|
@@ -44,7 +44,7 @@ export function showBanner(opts = {}) {
|
|
|
44
44
|
|
|
45
45
|
export function showMiniBanner() {
|
|
46
46
|
console.log('');
|
|
47
|
-
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.4.
|
|
47
|
+
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.4.2'));
|
|
48
48
|
console.log(theme.dim(' ─────────────────────────────'));
|
|
49
49
|
console.log('');
|
|
50
50
|
}
|
|
@@ -72,3 +72,6 @@ export function showDivider() {
|
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
@@ -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;
|