@darksol/terminal 0.1.1 → 0.2.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.
@@ -0,0 +1,229 @@
1
+ import fetch from 'node-fetch';
2
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, cpSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ import { homedir } from 'os';
5
+ import { fileURLToPath } from 'url';
6
+ import { theme } from '../ui/theme.js';
7
+ import { spinner, kvDisplay, success, error, warn, info, table } from '../ui/components.js';
8
+ import { showSection } from '../ui/banner.js';
9
+
10
+ const OPENCLAW_SKILLS_DIR = join(homedir(), '.openclaw', 'skills');
11
+
12
+ // ──────────────────────────────────────────────────
13
+ // DARKSOL SKILL CATALOG
14
+ // ──────────────────────────────────────────────────
15
+
16
+ const SKILL_CATALOG = [
17
+ {
18
+ name: 'darksol-terminal',
19
+ description: 'DARKSOL Terminal — unified CLI for trading, wallets, scripts, AI assistant, agent signing',
20
+ version: '0.2.0',
21
+ source: 'local', // bundled with the package
22
+ category: 'trading',
23
+ installed: () => existsSync(join(OPENCLAW_SKILLS_DIR, 'darksol-terminal', 'SKILL.md')),
24
+ },
25
+ {
26
+ name: 'darksol-facilitator',
27
+ description: 'Free on-chain x402 payment facilitator — verify and settle micropayments',
28
+ version: '1.0.0',
29
+ source: 'url',
30
+ url: 'https://facilitator.darksol.net/skill/SKILL.md',
31
+ category: 'payments',
32
+ installed: () => existsSync(join(OPENCLAW_SKILLS_DIR, 'darksol-facilitator', 'SKILL.md')),
33
+ },
34
+ {
35
+ name: 'darksol-prepaid-cards',
36
+ description: 'Crypto → prepaid Visa/MC cards, no KYC, agent-native REST API',
37
+ version: '1.0.0',
38
+ source: 'url',
39
+ url: 'https://acp.darksol.net/dist/darksol-prepaid-cards.skill',
40
+ skillMdUrl: 'https://acp.darksol.net/cards/skill/SKILL.md',
41
+ category: 'payments',
42
+ installed: () => existsSync(join(OPENCLAW_SKILLS_DIR, 'darksol-prepaid-cards', 'SKILL.md')),
43
+ },
44
+ {
45
+ name: 'random-oracle',
46
+ description: 'On-chain random oracle — verifiable randomness via x402',
47
+ version: '1.0.0',
48
+ source: 'url',
49
+ url: 'https://acp.darksol.net/oracle/skill/SKILL.md',
50
+ category: 'oracle',
51
+ installed: () => existsSync(join(OPENCLAW_SKILLS_DIR, 'random-oracle', 'SKILL.md')),
52
+ },
53
+ {
54
+ name: 'the-clawsino',
55
+ description: 'On-chain agent casino — coin flip, dice, hi-lo, slots via x402',
56
+ version: '1.0.0',
57
+ source: 'url',
58
+ url: 'https://casino.darksol.net/skill/SKILL.md',
59
+ category: 'gaming',
60
+ installed: () => existsSync(join(OPENCLAW_SKILLS_DIR, 'the-clawsino', 'SKILL.md')),
61
+ },
62
+ ];
63
+
64
+ // ──────────────────────────────────────────────────
65
+ // LIST SKILLS
66
+ // ──────────────────────────────────────────────────
67
+
68
+ export function listSkills() {
69
+ showSection('DARKSOL SKILLS DIRECTORY');
70
+
71
+ const rows = SKILL_CATALOG.map(s => {
72
+ const isInstalled = s.installed();
73
+ return [
74
+ isInstalled ? theme.success('● ') + theme.gold(s.name) : theme.dim('○ ') + s.name,
75
+ s.description.slice(0, 55) + (s.description.length > 55 ? '...' : ''),
76
+ s.version,
77
+ isInstalled ? theme.success('Installed') : theme.dim('Available'),
78
+ ];
79
+ });
80
+
81
+ table(['Skill', 'Description', 'Version', 'Status'], rows);
82
+
83
+ console.log('');
84
+ info('Install: darksol skills install <name>');
85
+ info('Info: darksol skills info <name>');
86
+ info('Skills install to: ' + OPENCLAW_SKILLS_DIR);
87
+ }
88
+
89
+ // ──────────────────────────────────────────────────
90
+ // INSTALL SKILL
91
+ // ──────────────────────────────────────────────────
92
+
93
+ export async function installSkill(name) {
94
+ const skill = SKILL_CATALOG.find(s => s.name === name);
95
+
96
+ if (!skill) {
97
+ error(`Unknown skill: ${name}`);
98
+ info('Available: ' + SKILL_CATALOG.map(s => s.name).join(', '));
99
+ return;
100
+ }
101
+
102
+ if (skill.installed()) {
103
+ warn(`${name} is already installed`);
104
+ const inquirer = (await import('inquirer')).default;
105
+ const { reinstall } = await inquirer.prompt([{
106
+ type: 'confirm',
107
+ name: 'reinstall',
108
+ message: theme.gold('Reinstall / update?'),
109
+ default: false,
110
+ }]);
111
+ if (!reinstall) return;
112
+ }
113
+
114
+ const spin = spinner(`Installing ${name}...`).start();
115
+
116
+ try {
117
+ const targetDir = join(OPENCLAW_SKILLS_DIR, name);
118
+ mkdirSync(targetDir, { recursive: true });
119
+
120
+ if (skill.source === 'local') {
121
+ // Copy from the npm package's bundled skill directory
122
+ const __dirname = dirname(fileURLToPath(import.meta.url));
123
+ const bundledSkillDir = join(__dirname, '..', '..', 'skill');
124
+
125
+ if (existsSync(join(bundledSkillDir, 'SKILL.md'))) {
126
+ const skillContent = readFileSync(join(bundledSkillDir, 'SKILL.md'), 'utf8');
127
+ writeFileSync(join(targetDir, 'SKILL.md'), skillContent);
128
+ } else {
129
+ throw new Error('Bundled SKILL.md not found in package');
130
+ }
131
+ } else if (skill.source === 'url') {
132
+ // Fetch from remote
133
+ const url = skill.skillMdUrl || skill.url;
134
+ const resp = await fetch(url);
135
+ if (!resp.ok) throw new Error(`Failed to fetch: ${resp.status}`);
136
+ const content = await resp.text();
137
+
138
+ // Handle .skill files (may be a zip or just SKILL.md content)
139
+ if (url.endsWith('.skill')) {
140
+ // .skill files are typically just the SKILL.md content
141
+ writeFileSync(join(targetDir, 'SKILL.md'), content);
142
+ } else {
143
+ writeFileSync(join(targetDir, 'SKILL.md'), content);
144
+ }
145
+ }
146
+
147
+ spin.succeed(`${name} installed`);
148
+
149
+ showSection(`INSTALLED: ${name}`);
150
+ kvDisplay([
151
+ ['Name', name],
152
+ ['Version', skill.version],
153
+ ['Category', skill.category],
154
+ ['Location', targetDir],
155
+ ]);
156
+ console.log('');
157
+ success('Skill is now available to OpenClaw and other agents');
158
+
159
+ } catch (err) {
160
+ spin.fail(`Failed to install ${name}`);
161
+ error(err.message);
162
+ }
163
+ }
164
+
165
+ // ──────────────────────────────────────────────────
166
+ // SKILL INFO
167
+ // ──────────────────────────────────────────────────
168
+
169
+ export async function skillInfo(name) {
170
+ const skill = SKILL_CATALOG.find(s => s.name === name);
171
+
172
+ if (!skill) {
173
+ error(`Unknown skill: ${name}`);
174
+ return;
175
+ }
176
+
177
+ const isInstalled = skill.installed();
178
+
179
+ showSection(`SKILL: ${name}`);
180
+ kvDisplay([
181
+ ['Name', skill.name],
182
+ ['Description', skill.description],
183
+ ['Version', skill.version],
184
+ ['Category', skill.category],
185
+ ['Status', isInstalled ? theme.success('Installed') : theme.dim('Not installed')],
186
+ ['Source', skill.source === 'local' ? 'Bundled with @darksol/terminal' : skill.url || 'Remote'],
187
+ ]);
188
+
189
+ if (isInstalled) {
190
+ const skillPath = join(OPENCLAW_SKILLS_DIR, name, 'SKILL.md');
191
+ console.log('');
192
+ console.log(theme.dim(` Location: ${skillPath}`));
193
+ }
194
+
195
+ console.log('');
196
+ if (!isInstalled) {
197
+ info(`Install: darksol skills install ${name}`);
198
+ }
199
+ }
200
+
201
+ // ──────────────────────────────────────────────────
202
+ // UNINSTALL SKILL
203
+ // ──────────────────────────────────────────────────
204
+
205
+ export async function uninstallSkill(name) {
206
+ const skill = SKILL_CATALOG.find(s => s.name === name);
207
+ const skillDir = join(OPENCLAW_SKILLS_DIR, name);
208
+
209
+ if (!existsSync(skillDir)) {
210
+ error(`${name} is not installed`);
211
+ return;
212
+ }
213
+
214
+ const inquirer = (await import('inquirer')).default;
215
+ const { confirm } = await inquirer.prompt([{
216
+ type: 'confirm',
217
+ name: 'confirm',
218
+ message: theme.accent(`Uninstall ${name}?`),
219
+ default: false,
220
+ }]);
221
+
222
+ if (!confirm) return;
223
+
224
+ const { rmSync } = await import('fs');
225
+ rmSync(skillDir, { recursive: true, force: true });
226
+ success(`${name} uninstalled`);
227
+ }
228
+
229
+ export { SKILL_CATALOG, OPENCLAW_SKILLS_DIR };
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.1.1') +
29
+ theme.subtle(' v0.2.1') +
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.1.1'));
47
+ console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.2.1'));
48
48
  console.log(theme.dim(' ─────────────────────────────'));
49
49
  console.log('');
50
50
  }
@@ -59,3 +59,5 @@ export function showDivider() {
59
59
  console.log(theme.dim(' ' + '─'.repeat(50)));
60
60
  }
61
61
 
62
+
63
+