uwonbot 1.0.9 → 1.1.0
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/bin/uwonbot.js +1 -1
- package/package.json +1 -1
- package/src/chat.js +51 -5
- package/src/terminalOrb.js +232 -0
package/bin/uwonbot.js
CHANGED
package/package.json
CHANGED
package/src/chat.js
CHANGED
|
@@ -7,6 +7,7 @@ import open from 'open';
|
|
|
7
7
|
import { getConfig } from './config.js';
|
|
8
8
|
import { sendToBrain } from './brain.js';
|
|
9
9
|
import { showMiniBar } from './banner.js';
|
|
10
|
+
import { printOrb, animateOrb } from './terminalOrb.js';
|
|
10
11
|
import {
|
|
11
12
|
hasRegisteredDevices,
|
|
12
13
|
createCLISession,
|
|
@@ -16,6 +17,40 @@ import {
|
|
|
16
17
|
|
|
17
18
|
const WEB_APP_URL = 'https://chartapp-653e1.web.app';
|
|
18
19
|
|
|
20
|
+
function hexToRgb(hex) {
|
|
21
|
+
const h = hex.replace('#', '');
|
|
22
|
+
return [
|
|
23
|
+
parseInt(h.substring(0, 2), 16) || 37,
|
|
24
|
+
parseInt(h.substring(2, 4), 16) || 99,
|
|
25
|
+
parseInt(h.substring(4, 6), 16) || 235,
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function sleep(ms) {
|
|
30
|
+
return new Promise(r => setTimeout(r, ms));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function bootSequence(assistant, brainLabel, brainColor) {
|
|
34
|
+
const c = chalk.hex(brainColor);
|
|
35
|
+
const g = chalk.gray;
|
|
36
|
+
const w = chalk.white;
|
|
37
|
+
|
|
38
|
+
const steps = [
|
|
39
|
+
[300, `${g(' [SYS]')} ${w('INITIATING SYSTEM I...')}`],
|
|
40
|
+
[300, `${g(' [SYS]')} ${c(`NETWARE ${assistant.name.toUpperCase()} v1.0`)}`],
|
|
41
|
+
[400, `${g(' [SYS]')} ${w('PROTOCOL: SCANNING...')}`],
|
|
42
|
+
[300, `${g(' [SYS]')} ${w('RELEASING CONFIGURATION')}`],
|
|
43
|
+
[200, `${g(' [SYS]')} ${w(`Brain: ${brainLabel} | API: connected`)}`],
|
|
44
|
+
[200, `${g(' [SYS]')} ${w('Authentication verified.')}`],
|
|
45
|
+
[500, `${g(' [SYS]')} ${chalk.green('All systems operational. Standing by.')}`],
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
for (const [delay, text] of steps) {
|
|
49
|
+
await sleep(delay);
|
|
50
|
+
console.log(text);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
19
54
|
async function requireBiometricAuth(uid) {
|
|
20
55
|
try {
|
|
21
56
|
const hasDevices = await hasRegisteredDevices(uid);
|
|
@@ -107,12 +142,23 @@ export async function startChat(assistantName, assistant, initialCommand) {
|
|
|
107
142
|
: assistant.brain === 'gemini' ? '#8b5cf6'
|
|
108
143
|
: '#2563eb';
|
|
109
144
|
|
|
145
|
+
const orbColorHex = assistant.orbColor || brainColor;
|
|
146
|
+
const orbRgb = hexToRgb(orbColorHex);
|
|
147
|
+
|
|
148
|
+
console.clear();
|
|
149
|
+
printOrb({
|
|
150
|
+
radius: 10,
|
|
151
|
+
color: orbRgb,
|
|
152
|
+
label: assistant.name.toUpperCase(),
|
|
153
|
+
status: '준비됨',
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await bootSequence(assistant, brainLabel, brainColor);
|
|
157
|
+
|
|
110
158
|
console.log('');
|
|
111
|
-
console.log(chalk.
|
|
112
|
-
console.log(chalk.
|
|
113
|
-
console.log(chalk.
|
|
114
|
-
console.log(chalk.hex('#2563eb')(' ║') + chalk.gray(' Type "exit" to quit, "clear" to reset ') + chalk.hex('#2563eb')('║'));
|
|
115
|
-
console.log(chalk.hex('#2563eb')(' ╚══════════════════════════════════════════╝'));
|
|
159
|
+
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
160
|
+
console.log(chalk.gray(' "exit" 종료 | "clear" 대화 초기화'));
|
|
161
|
+
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
116
162
|
console.log('');
|
|
117
163
|
|
|
118
164
|
const messages = [];
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
const SHADING_CHARS = ' .:-=+*#%@';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Renders a 3D sphere in the terminal using ray-traced shading with true color.
|
|
7
|
+
* @param {object} opts
|
|
8
|
+
* @param {number} [opts.radius=10] - Sphere radius in character cells
|
|
9
|
+
* @param {number[]} [opts.color=[37,99,235]] - RGB base color
|
|
10
|
+
* @param {string} [opts.label] - Text label below the orb
|
|
11
|
+
* @param {string} [opts.status] - Status text below label
|
|
12
|
+
* @param {boolean} [opts.glow=true] - Enable glow effect
|
|
13
|
+
* @returns {string} The rendered orb as a string
|
|
14
|
+
*/
|
|
15
|
+
export function renderOrb(opts = {}) {
|
|
16
|
+
const radius = opts.radius || 10;
|
|
17
|
+
const [baseR, baseG, baseB] = opts.color || [37, 99, 235];
|
|
18
|
+
const label = opts.label || '';
|
|
19
|
+
const status = opts.status || '';
|
|
20
|
+
const glow = opts.glow !== false;
|
|
21
|
+
|
|
22
|
+
const width = radius * 2 + 4;
|
|
23
|
+
const height = radius + 2;
|
|
24
|
+
const lines = [];
|
|
25
|
+
|
|
26
|
+
const lightX = -0.4;
|
|
27
|
+
const lightY = -0.5;
|
|
28
|
+
const lightZ = 0.8;
|
|
29
|
+
const lightLen = Math.sqrt(lightX * lightX + lightY * lightY + lightZ * lightZ);
|
|
30
|
+
const lx = lightX / lightLen;
|
|
31
|
+
const ly = lightY / lightLen;
|
|
32
|
+
const lz = lightZ / lightLen;
|
|
33
|
+
|
|
34
|
+
for (let y = -radius; y <= radius; y++) {
|
|
35
|
+
let line = '';
|
|
36
|
+
const rowY = y / radius;
|
|
37
|
+
|
|
38
|
+
for (let x = -width / 2; x <= width / 2; x++) {
|
|
39
|
+
const rowX = x / (width / 2);
|
|
40
|
+
const distSq = rowX * rowX + rowY * rowY;
|
|
41
|
+
|
|
42
|
+
if (distSq > 1.0) {
|
|
43
|
+
if (glow && distSq < 1.5) {
|
|
44
|
+
const glowIntensity = 1.0 - (distSq - 1.0) / 0.5;
|
|
45
|
+
const gr = Math.round(baseR * glowIntensity * 0.3);
|
|
46
|
+
const gg = Math.round(baseG * glowIntensity * 0.3);
|
|
47
|
+
const gb = Math.round(baseB * glowIntensity * 0.3);
|
|
48
|
+
line += chalk.rgb(gr, gg, gb)('·');
|
|
49
|
+
} else {
|
|
50
|
+
line += ' ';
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const nz = Math.sqrt(1 - distSq);
|
|
56
|
+
const nx = rowX;
|
|
57
|
+
const ny = rowY;
|
|
58
|
+
|
|
59
|
+
const diffuse = Math.max(0, nx * lx + ny * ly + nz * lz);
|
|
60
|
+
|
|
61
|
+
const rx = 2 * (nx * lx + ny * ly + nz * lz) * nx - lx;
|
|
62
|
+
const ry = 2 * (nx * lx + ny * ly + nz * lz) * ny - ly;
|
|
63
|
+
const rz = 2 * (nx * lx + ny * ly + nz * lz) * nz - lz;
|
|
64
|
+
const viewZ = 1.0;
|
|
65
|
+
const specular = Math.pow(Math.max(0, rz / Math.sqrt(rx * rx + ry * ry + rz * rz) * viewZ), 32);
|
|
66
|
+
|
|
67
|
+
const ambient = 0.12;
|
|
68
|
+
const intensity = Math.min(1.0, ambient + diffuse * 0.75 + specular * 0.5);
|
|
69
|
+
|
|
70
|
+
const r = Math.min(255, Math.round(baseR * intensity + specular * 180));
|
|
71
|
+
const g = Math.min(255, Math.round(baseG * intensity + specular * 180));
|
|
72
|
+
const b = Math.min(255, Math.round(baseB * intensity + specular * 180));
|
|
73
|
+
|
|
74
|
+
const charIdx = Math.min(SHADING_CHARS.length - 1, Math.round(intensity * (SHADING_CHARS.length - 1)));
|
|
75
|
+
const ch = SHADING_CHARS[charIdx];
|
|
76
|
+
|
|
77
|
+
line += chalk.rgb(r, g, b)(ch || ' ');
|
|
78
|
+
}
|
|
79
|
+
lines.push(line);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (label) {
|
|
83
|
+
lines.push('');
|
|
84
|
+
const padded = label.length < width * 2
|
|
85
|
+
? ' '.repeat(Math.max(0, Math.floor((width - label.length / 2)))) + label
|
|
86
|
+
: label;
|
|
87
|
+
lines.push(chalk.bold.white(padded));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (status) {
|
|
91
|
+
const padded = status.length < width * 2
|
|
92
|
+
? ' '.repeat(Math.max(0, Math.floor((width - status.length / 2)))) + status
|
|
93
|
+
: status;
|
|
94
|
+
lines.push(chalk.gray(padded));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Animated spinning orb in the terminal.
|
|
102
|
+
* @param {object} opts
|
|
103
|
+
* @param {number[]} [opts.color=[37,99,235]]
|
|
104
|
+
* @param {string} [opts.label]
|
|
105
|
+
* @param {string} [opts.status]
|
|
106
|
+
* @param {number} [opts.radius=10]
|
|
107
|
+
* @param {number} [opts.fps=12]
|
|
108
|
+
* @returns {{ update(opts), stop() }}
|
|
109
|
+
*/
|
|
110
|
+
export function animateOrb(opts = {}) {
|
|
111
|
+
const radius = opts.radius || 10;
|
|
112
|
+
const fps = opts.fps || 12;
|
|
113
|
+
let color = opts.color || [37, 99, 235];
|
|
114
|
+
let label = opts.label || '';
|
|
115
|
+
let statusText = opts.status || '';
|
|
116
|
+
let frame = 0;
|
|
117
|
+
let running = true;
|
|
118
|
+
const totalLines = (radius * 2 + 3) + (label ? 1 : 0) + (statusText ? 1 : 0) + 1;
|
|
119
|
+
|
|
120
|
+
function draw() {
|
|
121
|
+
const angle = (frame * 0.05);
|
|
122
|
+
const lightX = Math.cos(angle) * 0.6;
|
|
123
|
+
const lightY = -0.5;
|
|
124
|
+
const lightZ = Math.sin(angle) * 0.4 + 0.6;
|
|
125
|
+
const lightLen = Math.sqrt(lightX * lightX + lightY * lightY + lightZ * lightZ);
|
|
126
|
+
|
|
127
|
+
const width = radius * 2 + 4;
|
|
128
|
+
const lines = [];
|
|
129
|
+
|
|
130
|
+
for (let y = -radius; y <= radius; y++) {
|
|
131
|
+
let line = ' ';
|
|
132
|
+
const rowY = y / radius;
|
|
133
|
+
|
|
134
|
+
for (let x = -width / 2; x <= width / 2; x++) {
|
|
135
|
+
const rowX = x / (width / 2);
|
|
136
|
+
const distSq = rowX * rowX + rowY * rowY;
|
|
137
|
+
|
|
138
|
+
if (distSq > 1.0) {
|
|
139
|
+
if (distSq < 1.4) {
|
|
140
|
+
const glowIntensity = 1.0 - (distSq - 1.0) / 0.4;
|
|
141
|
+
const pulse = 0.3 + Math.sin(frame * 0.15) * 0.1;
|
|
142
|
+
const gr = Math.round(color[0] * glowIntensity * pulse);
|
|
143
|
+
const gg = Math.round(color[1] * glowIntensity * pulse);
|
|
144
|
+
const gb = Math.round(color[2] * glowIntensity * pulse);
|
|
145
|
+
line += chalk.rgb(gr, gg, gb)('·');
|
|
146
|
+
} else {
|
|
147
|
+
line += ' ';
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const nz = Math.sqrt(1 - distSq);
|
|
153
|
+
const nx = rowX;
|
|
154
|
+
const ny = rowY;
|
|
155
|
+
|
|
156
|
+
const lx = lightX / lightLen;
|
|
157
|
+
const ly = lightY / lightLen;
|
|
158
|
+
const lz = lightZ / lightLen;
|
|
159
|
+
|
|
160
|
+
const diffuse = Math.max(0, nx * lx + ny * ly + nz * lz);
|
|
161
|
+
const dot = nx * lx + ny * ly + nz * lz;
|
|
162
|
+
const rz = 2 * dot * nz - lz;
|
|
163
|
+
const specular = Math.pow(Math.max(0, rz), 40);
|
|
164
|
+
|
|
165
|
+
const ambient = 0.08;
|
|
166
|
+
const intensity = Math.min(1.0, ambient + diffuse * 0.7 + specular * 0.6);
|
|
167
|
+
|
|
168
|
+
const r = Math.min(255, Math.round(color[0] * intensity + specular * 200));
|
|
169
|
+
const g = Math.min(255, Math.round(color[1] * intensity + specular * 200));
|
|
170
|
+
const b = Math.min(255, Math.round(color[2] * intensity + specular * 200));
|
|
171
|
+
|
|
172
|
+
const charIdx = Math.min(SHADING_CHARS.length - 1, Math.round(intensity * (SHADING_CHARS.length - 1)));
|
|
173
|
+
line += chalk.rgb(r, g, b)(SHADING_CHARS[charIdx] || ' ');
|
|
174
|
+
}
|
|
175
|
+
lines.push(line);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (label) {
|
|
179
|
+
lines.push('');
|
|
180
|
+
const pad = Math.max(0, Math.floor((width + 2 - label.length) / 2));
|
|
181
|
+
lines.push(' '.repeat(pad) + chalk.bold.white(label));
|
|
182
|
+
}
|
|
183
|
+
if (statusText) {
|
|
184
|
+
const pad = Math.max(0, Math.floor((width + 2 - statusText.length) / 2));
|
|
185
|
+
lines.push(' '.repeat(pad) + chalk.gray(statusText));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return lines.join('\n');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let prevLineCount = 0;
|
|
192
|
+
|
|
193
|
+
const interval = setInterval(() => {
|
|
194
|
+
if (!running) return;
|
|
195
|
+
frame++;
|
|
196
|
+
|
|
197
|
+
if (prevLineCount > 0) {
|
|
198
|
+
process.stdout.write(`\x1b[${prevLineCount}A\x1b[J`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const output = draw();
|
|
202
|
+
process.stdout.write(output + '\n');
|
|
203
|
+
prevLineCount = output.split('\n').length;
|
|
204
|
+
}, 1000 / fps);
|
|
205
|
+
|
|
206
|
+
const output = draw();
|
|
207
|
+
process.stdout.write(output + '\n');
|
|
208
|
+
prevLineCount = output.split('\n').length;
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
update(newOpts) {
|
|
212
|
+
if (newOpts.color) color = newOpts.color;
|
|
213
|
+
if (newOpts.label !== undefined) label = newOpts.label;
|
|
214
|
+
if (newOpts.status !== undefined) statusText = newOpts.status;
|
|
215
|
+
},
|
|
216
|
+
stop() {
|
|
217
|
+
running = false;
|
|
218
|
+
clearInterval(interval);
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Prints a static orb once.
|
|
225
|
+
*/
|
|
226
|
+
export function printOrb(opts = {}) {
|
|
227
|
+
console.log('');
|
|
228
|
+
console.log(renderOrb(opts));
|
|
229
|
+
console.log('');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export default { renderOrb, animateOrb, printOrb };
|