@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darksol/terminal",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
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": {
@@ -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',
@@ -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
- const getURL = () => getServiceURL('oracle') || 'https://acp.darksol.net/oracle';
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 fetchJSON(`${getURL()}/api/coin`);
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 fetchJSON(`${getURL()}/api/dice?sides=${sides}`);
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 fetchJSON(`${getURL()}/api/number?min=${min}&max=${max}`);
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 fetchJSON(`${getURL()}/api/shuffle`, {
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
- }
@@ -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.gold} ORACLE${ANSI.reset}`);
1129
- ws.sendLine(`${ANSI.dim} ${''.repeat(50)}${ANSI.reset}`);
1130
- ws.sendLine(` ${ANSI.darkGold}Status${ANSI.reset} ${data.status || 'unknown'}`);
1131
- ws.sendLine(` ${ANSI.darkGold}Endpoint${ANSI.reset} ${ANSI.blue}acp.darksol.net/oracle${ANSI.reset}`);
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.dim}Oracle unreachable${ANSI.reset}`);
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 {};