@darksol/terminal 0.6.0 → 0.6.1
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 +1 -1
- package/src/config/store.js +1 -1
- package/src/services/oracle.js +103 -20
- package/src/web/commands.js +20 -6
package/package.json
CHANGED
package/src/config/store.js
CHANGED
|
@@ -29,7 +29,7 @@ const config = new Conf({
|
|
|
29
29
|
services: {
|
|
30
30
|
type: 'object',
|
|
31
31
|
default: {
|
|
32
|
-
oracle: 'https://acp.darksol.net/oracle',
|
|
32
|
+
oracle: 'https://acp.darksol.net/api/oracle',
|
|
33
33
|
casino: 'https://casino.darksol.net',
|
|
34
34
|
cards: 'https://acp.darksol.net',
|
|
35
35
|
facilitator: 'https://facilitator.darksol.net',
|
package/src/services/oracle.js
CHANGED
|
@@ -1,16 +1,97 @@
|
|
|
1
1
|
import { fetchJSON } from '../utils/fetch.js';
|
|
2
2
|
import fetch from 'node-fetch';
|
|
3
|
-
import { getServiceURL } from '../config/store.js';
|
|
3
|
+
import { getServiceURL, getConfig } from '../config/store.js';
|
|
4
4
|
import { theme } from '../ui/theme.js';
|
|
5
|
-
import { spinner, kvDisplay, success, error } from '../ui/components.js';
|
|
5
|
+
import { spinner, kvDisplay, success, error, warn, info } from '../ui/components.js';
|
|
6
6
|
import { showSection } from '../ui/banner.js';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// Oracle lives under acp.darksol.net/api/oracle/
|
|
9
|
+
// Endpoints: health (free), coin/dice/number/shuffle (x402 — $0.05 USDC on Base)
|
|
10
|
+
const getURL = () => {
|
|
11
|
+
const custom = getServiceURL('oracle');
|
|
12
|
+
if (custom) return custom;
|
|
13
|
+
return 'https://acp.darksol.net/api/oracle';
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function handleX402(response) {
|
|
17
|
+
if (response === 402 || (response && response.status === 402)) {
|
|
18
|
+
warn('Oracle requires x402 payment ($0.05 USDC on Base)');
|
|
19
|
+
info('Use with agent signer: darksol signer start → requests auto-pay');
|
|
20
|
+
info('Or pay manually via facilitator: darksol facilitator');
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function oracleRequest(path, opts = {}) {
|
|
27
|
+
const url = `${getURL()}${path}`;
|
|
28
|
+
const resp = await fetch(url, opts);
|
|
29
|
+
|
|
30
|
+
if (resp.status === 402) {
|
|
31
|
+
// Return x402 info so caller can handle
|
|
32
|
+
const paymentHeader = resp.headers.get('payment-required');
|
|
33
|
+
let paymentInfo = null;
|
|
34
|
+
if (paymentHeader) {
|
|
35
|
+
try {
|
|
36
|
+
paymentInfo = JSON.parse(Buffer.from(paymentHeader, 'base64').toString());
|
|
37
|
+
} catch {}
|
|
38
|
+
}
|
|
39
|
+
return { x402: true, paymentInfo, status: 402 };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const ct = resp.headers.get('content-type') || '';
|
|
43
|
+
if (!ct.includes('json')) {
|
|
44
|
+
throw new Error(`Oracle returned non-JSON response (${resp.status})`);
|
|
45
|
+
}
|
|
46
|
+
return await resp.json();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function oracleHealth() {
|
|
50
|
+
const spin = spinner('Checking oracle...').start();
|
|
51
|
+
try {
|
|
52
|
+
const data = await oracleRequest('/health');
|
|
53
|
+
if (data.x402) {
|
|
54
|
+
spin.succeed('Oracle online (health should be free)');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
spin.succeed('Oracle online');
|
|
58
|
+
|
|
59
|
+
showSection('ORACLE STATUS');
|
|
60
|
+
kvDisplay([
|
|
61
|
+
['Status', data.status === 'ok' ? theme.success('● Online') : theme.error('○ ' + data.status)],
|
|
62
|
+
['Contract', data.contract || '-'],
|
|
63
|
+
['Chain', data.chain || 'base'],
|
|
64
|
+
['Block', String(data.blockNumber || '-')],
|
|
65
|
+
]);
|
|
66
|
+
console.log('');
|
|
67
|
+
info('Endpoints require x402 payment ($0.05 USDC on Base)');
|
|
68
|
+
info('Games: coin flip, dice, random number, shuffle');
|
|
69
|
+
info('Docs: https://acp.darksol.net/oracle');
|
|
70
|
+
} catch (err) {
|
|
71
|
+
spin.fail('Oracle unreachable');
|
|
72
|
+
error(err.message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
9
75
|
|
|
10
76
|
export async function oracleFlip() {
|
|
11
77
|
const spin = spinner('Flipping coin...').start();
|
|
12
78
|
try {
|
|
13
|
-
const data = await
|
|
79
|
+
const data = await oracleRequest('/coin');
|
|
80
|
+
if (data.x402) {
|
|
81
|
+
spin.info('Payment required');
|
|
82
|
+
handleX402(data);
|
|
83
|
+
if (data.paymentInfo) {
|
|
84
|
+
const accepts = data.paymentInfo.accepts?.[0];
|
|
85
|
+
if (accepts) {
|
|
86
|
+
kvDisplay([
|
|
87
|
+
['Amount', `$${(parseInt(accepts.amount) / 1e6).toFixed(2)} USDC`],
|
|
88
|
+
['Network', 'Base'],
|
|
89
|
+
['Pay To', accepts.payTo || '-'],
|
|
90
|
+
]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
14
95
|
spin.succeed('Coin flipped');
|
|
15
96
|
showSection('ORACLE — COIN FLIP');
|
|
16
97
|
kvDisplay([
|
|
@@ -26,7 +107,12 @@ export async function oracleFlip() {
|
|
|
26
107
|
export async function oracleDice(sides = 6) {
|
|
27
108
|
const spin = spinner(`Rolling d${sides}...`).start();
|
|
28
109
|
try {
|
|
29
|
-
const data = await
|
|
110
|
+
const data = await oracleRequest(`/dice?sides=${sides}`);
|
|
111
|
+
if (data.x402) {
|
|
112
|
+
spin.info('Payment required');
|
|
113
|
+
handleX402(data);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
30
116
|
spin.succeed('Dice rolled');
|
|
31
117
|
showSection(`ORACLE — D${sides}`);
|
|
32
118
|
kvDisplay([
|
|
@@ -43,7 +129,12 @@ export async function oracleDice(sides = 6) {
|
|
|
43
129
|
export async function oracleNumber(min = 1, max = 100) {
|
|
44
130
|
const spin = spinner(`Generating number ${min}-${max}...`).start();
|
|
45
131
|
try {
|
|
46
|
-
const data = await
|
|
132
|
+
const data = await oracleRequest(`/number?min=${min}&max=${max}`);
|
|
133
|
+
if (data.x402) {
|
|
134
|
+
spin.info('Payment required');
|
|
135
|
+
handleX402(data);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
47
138
|
spin.succeed('Number generated');
|
|
48
139
|
showSection('ORACLE — RANDOM NUMBER');
|
|
49
140
|
kvDisplay([
|
|
@@ -60,11 +151,16 @@ export async function oracleNumber(min = 1, max = 100) {
|
|
|
60
151
|
export async function oracleShuffle(items) {
|
|
61
152
|
const spin = spinner('Shuffling...').start();
|
|
62
153
|
try {
|
|
63
|
-
const data = await
|
|
154
|
+
const data = await oracleRequest('/shuffle', {
|
|
64
155
|
method: 'POST',
|
|
65
156
|
headers: { 'Content-Type': 'application/json' },
|
|
66
157
|
body: JSON.stringify({ items }),
|
|
67
158
|
});
|
|
159
|
+
if (data.x402) {
|
|
160
|
+
spin.info('Payment required');
|
|
161
|
+
handleX402(data);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
68
164
|
spin.succeed('Shuffled');
|
|
69
165
|
showSection('ORACLE — SHUFFLE');
|
|
70
166
|
console.log(theme.gold(' Result: ') + (data.result || data.value || []).join(', '));
|
|
@@ -73,16 +169,3 @@ export async function oracleShuffle(items) {
|
|
|
73
169
|
error(err.message);
|
|
74
170
|
}
|
|
75
171
|
}
|
|
76
|
-
|
|
77
|
-
export async function oracleHealth() {
|
|
78
|
-
const spin = spinner('Checking oracle...').start();
|
|
79
|
-
try {
|
|
80
|
-
const data = await fetchJSON(`${getURL()}/api/health`);
|
|
81
|
-
spin.succeed('Oracle online');
|
|
82
|
-
showSection('ORACLE STATUS');
|
|
83
|
-
kvDisplay(Object.entries(data).map(([k, v]) => [k, String(v)]));
|
|
84
|
-
} catch (err) {
|
|
85
|
-
spin.fail('Oracle unreachable');
|
|
86
|
-
error(err.message);
|
|
87
|
-
}
|
|
88
|
-
}
|
package/src/web/commands.js
CHANGED
|
@@ -1121,17 +1121,31 @@ async function executeCardOrder(orderMeta, ws) {
|
|
|
1121
1121
|
// ORACLE
|
|
1122
1122
|
// ══════════════════════════════════════════════════
|
|
1123
1123
|
async function cmdOracle(args, ws) {
|
|
1124
|
+
ws.sendLine(`${ANSI.gold} ◆ RANDOM ORACLE 🎲${ANSI.reset}`);
|
|
1125
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
1124
1126
|
try {
|
|
1125
|
-
const resp = await fetch('https://acp.darksol.net/oracle');
|
|
1127
|
+
const resp = await fetch('https://acp.darksol.net/api/oracle/health');
|
|
1128
|
+
const ct = resp.headers.get('content-type') || '';
|
|
1129
|
+
if (!ct.includes('json')) throw new Error('not json');
|
|
1126
1130
|
const data = await resp.json();
|
|
1127
1131
|
|
|
1128
|
-
ws.sendLine(`${ANSI.
|
|
1129
|
-
ws.sendLine(
|
|
1130
|
-
ws.sendLine(` ${ANSI.darkGold}
|
|
1131
|
-
ws.sendLine(` ${ANSI.darkGold}
|
|
1132
|
+
ws.sendLine(` ${ANSI.darkGold}Status${ANSI.reset} ${data.status === 'ok' ? `${ANSI.green}● Online${ANSI.reset}` : `${ANSI.red}○ ${data.status}${ANSI.reset}`}`);
|
|
1133
|
+
ws.sendLine(` ${ANSI.darkGold}Contract${ANSI.reset} ${ANSI.dim}${data.contract || '-'}${ANSI.reset}`);
|
|
1134
|
+
ws.sendLine(` ${ANSI.darkGold}Chain${ANSI.reset} ${ANSI.white}${data.chain || 'base'}${ANSI.reset}`);
|
|
1135
|
+
ws.sendLine(` ${ANSI.darkGold}Block${ANSI.reset} ${ANSI.white}${data.blockNumber || '-'}${ANSI.reset}`);
|
|
1136
|
+
ws.sendLine('');
|
|
1137
|
+
ws.sendLine(` ${ANSI.gold}ENDPOINTS${ANSI.reset} ${ANSI.dim}x402-gated ($0.05 USDC on Base)${ANSI.reset}`);
|
|
1138
|
+
ws.sendLine(` ${ANSI.white}🪙 /coin${ANSI.reset} ${ANSI.dim}Fair coin flip${ANSI.reset}`);
|
|
1139
|
+
ws.sendLine(` ${ANSI.white}🎲 /dice${ANSI.reset} ${ANSI.dim}Roll with N sides${ANSI.reset}`);
|
|
1140
|
+
ws.sendLine(` ${ANSI.white}🔢 /number${ANSI.reset} ${ANSI.dim}Random in range${ANSI.reset}`);
|
|
1141
|
+
ws.sendLine(` ${ANSI.white}🔀 /shuffle${ANSI.reset} ${ANSI.dim}Shuffle a list${ANSI.reset}`);
|
|
1142
|
+
ws.sendLine('');
|
|
1143
|
+
ws.sendLine(` ${ANSI.dim}CLI: darksol oracle flip / dice / number / shuffle${ANSI.reset}`);
|
|
1144
|
+
ws.sendLine(` ${ANSI.dim}Docs: https://acp.darksol.net/oracle${ANSI.reset}`);
|
|
1132
1145
|
ws.sendLine('');
|
|
1133
1146
|
} catch {
|
|
1134
|
-
ws.sendLine(` ${ANSI.
|
|
1147
|
+
ws.sendLine(` ${ANSI.red}○ Oracle unreachable${ANSI.reset}`);
|
|
1148
|
+
ws.sendLine(` ${ANSI.dim}Check: https://acp.darksol.net/oracle${ANSI.reset}`);
|
|
1135
1149
|
ws.sendLine('');
|
|
1136
1150
|
}
|
|
1137
1151
|
return {};
|