upfynai-code 2.8.0 → 2.8.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "upfynai-code",
3
- "version": "2.8.0",
3
+ "version": "2.8.2",
4
4
  "description": "Unified AI coding interface — access AI chat, terminal, file explorer, git, and visual canvas from any browser. Connect your local machine and code from anywhere.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,6 +17,7 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "start": "node bin/cli.js",
20
+ "postinstall": "node scripts/postinstall.js",
20
21
  "prepublishOnly": "node scripts/prepublish.js"
21
22
  },
22
23
  "keywords": [
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { playInstallAnimation } from '../src/animation.js';
3
+
4
+ try {
5
+ await playInstallAnimation();
6
+ } catch {
7
+ // Silently fail — animation is cosmetic, don't break installs
8
+ console.log('\n ✓ upfynai-code installed. Run `uc --help` to get started.\n');
9
+ }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * CLI spaceship-to-star animation for Upfyn-Code.
3
+ * Plays after install (postinstall) and before `uc connect` starts.
4
+ */
5
+
6
+ const STAR_FRAMES = ['✦', '✧', '✦', '★'];
7
+
8
+ // Spaceship frames (4 animation states)
9
+ const SHIP = [
10
+ ' ╱▏▔▔╲ ',
11
+ ' ╱ ▏══ ╲ ',
12
+ '╱ ▏▁▁ ╲ ',
13
+ ' ╱▏▔▔╲ ',
14
+ ];
15
+
16
+ // Simpler inline ship for the travel animation
17
+ const SHIP_R = [
18
+ ' ▄▄ ',
19
+ ' ◁━━██━━▷ ',
20
+ ' ▀▀ ',
21
+ ];
22
+
23
+ const SHIP_SMALL = '◁━━▶';
24
+ const SHIP_TRAVEL = '⟫⟫';
25
+ const EXHAUST_CHARS = ['░', '▒', '▓', '═', '~', '·'];
26
+
27
+ function sleep(ms) {
28
+ return new Promise(r => setTimeout(r, ms));
29
+ }
30
+
31
+ function clearLines(n) {
32
+ for (let i = 0; i < n; i++) {
33
+ process.stdout.write('\x1b[1A\x1b[2K');
34
+ }
35
+ }
36
+
37
+ function dim(s) { return `\x1b[2m${s}\x1b[0m`; }
38
+ function cyan(s) { return `\x1b[36m${s}\x1b[0m`; }
39
+ function yellow(s) { return `\x1b[33m${s}\x1b[0m`; }
40
+ function bold(s) { return `\x1b[1m${s}\x1b[0m`; }
41
+ function magenta(s) { return `\x1b[35m${s}\x1b[0m`; }
42
+ function white(s) { return `\x1b[97m${s}\x1b[0m`; }
43
+ function green(s) { return `\x1b[32m${s}\x1b[0m`; }
44
+ function blue(s) { return `\x1b[34m${s}\x1b[0m`; }
45
+
46
+ /**
47
+ * Generate a star field background line
48
+ */
49
+ function starFieldLine(width, density = 0.08, frame = 0) {
50
+ let line = '';
51
+ for (let i = 0; i < width; i++) {
52
+ if (Math.random() < density) {
53
+ const stars = ['.', '·', '∘', '°', '✧'];
54
+ const s = stars[Math.floor(Math.random() * stars.length)];
55
+ line += dim(s);
56
+ } else {
57
+ line += ' ';
58
+ }
59
+ }
60
+ return line;
61
+ }
62
+
63
+ /**
64
+ * Main spaceship-to-star animation
65
+ * @param {'install'|'connect'} mode
66
+ */
67
+ export async function playSpaceshipAnimation(mode = 'connect') {
68
+ const cols = Math.min(process.stdout.columns || 70, 80);
69
+ const totalFrames = 28;
70
+ const LINES = 11; // total lines we'll use
71
+
72
+ const starX = cols - 6;
73
+
74
+ // Phase 1: Launch sequence text
75
+ const tagline = mode === 'install'
76
+ ? 'Installation complete!'
77
+ : 'Launching relay bridge...';
78
+
79
+ console.log('');
80
+ console.log(dim(' ─'.repeat(Math.floor(cols / 3))));
81
+ console.log('');
82
+
83
+ // Phase 2: Spaceship travels across the screen toward a star
84
+ for (let frame = 0; frame < totalFrames; frame++) {
85
+ const progress = frame / (totalFrames - 1); // 0 → 1
86
+ const shipX = Math.floor(progress * (cols - 16)) + 2;
87
+
88
+ // Build exhaust trail
89
+ const exhaustLen = Math.min(shipX, Math.floor(progress * 20));
90
+ let exhaust = '';
91
+ for (let e = 0; e < exhaustLen; e++) {
92
+ const intensity = 1 - (e / exhaustLen);
93
+ if (intensity > 0.7) exhaust = '▓' + exhaust;
94
+ else if (intensity > 0.4) exhaust = '▒' + exhaust;
95
+ else if (intensity > 0.2) exhaust = '░' + exhaust;
96
+ else exhaust = '·' + exhaust;
97
+ }
98
+
99
+ // Star pulse
100
+ const starChar = STAR_FRAMES[frame % STAR_FRAMES.length];
101
+ const starGlow = progress > 0.7 ? yellow('✦ ') : '';
102
+
103
+ // Build the 5 display lines
104
+ const lines = [];
105
+
106
+ // Line 1: starfield
107
+ lines.push(' ' + starFieldLine(cols - 4, 0.05, frame));
108
+
109
+ // Line 2: top space
110
+ lines.push(' ' + starFieldLine(cols - 4, 0.03, frame));
111
+
112
+ // Line 3: ship row (main action)
113
+ let shipRow = '';
114
+ const beforeShip = Math.max(0, shipX - exhaustLen);
115
+ shipRow += ' '.repeat(beforeShip);
116
+ shipRow += dim(exhaust);
117
+ shipRow += cyan(' ◁━━▶');
118
+ // Fill to star position
119
+ const afterShip = Math.max(0, starX - shipX - 7);
120
+ // Dots between ship and star
121
+ let midSpace = '';
122
+ for (let d = 0; d < afterShip; d++) {
123
+ midSpace += Math.random() < 0.06 ? dim('·') : ' ';
124
+ }
125
+ shipRow += midSpace;
126
+ if (progress < 0.92) {
127
+ shipRow += starGlow + yellow(starChar);
128
+ } else {
129
+ shipRow += yellow('✦★✦');
130
+ }
131
+ lines.push(shipRow);
132
+
133
+ // Line 4: thrust glow
134
+ let thrustRow = ' '.repeat(Math.max(0, shipX + 1));
135
+ if (frame % 2 === 0) {
136
+ thrustRow += dim(magenta('~≈~'));
137
+ } else {
138
+ thrustRow += dim(magenta('≈~≈'));
139
+ }
140
+ lines.push(thrustRow);
141
+
142
+ // Line 5: starfield
143
+ lines.push(' ' + starFieldLine(cols - 4, 0.04, frame));
144
+
145
+ // Line 6: message (centered)
146
+ const msg = progress < 0.3
147
+ ? dim(` ${tagline}`)
148
+ : progress < 0.6
149
+ ? cyan(` ⟫ Navigating to the stars...`)
150
+ : progress < 0.9
151
+ ? magenta(` ⟫⟫ Almost there...`)
152
+ : green(` ★ ${mode === 'install' ? 'Ready for launch!' : 'Connection established!'}`);
153
+ lines.push(msg);
154
+
155
+ // Line 7: progress bar
156
+ const barWidth = cols - 10;
157
+ const filled = Math.floor(progress * barWidth);
158
+ const bar = ' ' + dim('[')
159
+ + cyan('█'.repeat(filled))
160
+ + dim('░'.repeat(barWidth - filled))
161
+ + dim(']')
162
+ + dim(` ${Math.floor(progress * 100)}%`);
163
+ lines.push(bar);
164
+
165
+ // Print
166
+ if (frame > 0) clearLines(lines.length);
167
+ for (const l of lines) console.log(l);
168
+
169
+ // Speed: start slow, middle fast, end slow
170
+ const delay = progress < 0.2 ? 120
171
+ : progress > 0.85 ? 150
172
+ : 60;
173
+ await sleep(delay);
174
+ }
175
+
176
+ // Phase 3: Final flash
177
+ await sleep(200);
178
+ clearLines(7);
179
+
180
+ // Arrival burst
181
+ const burstLines = [];
182
+ burstLines.push(' ' + starFieldLine(cols - 4, 0.06));
183
+ burstLines.push('');
184
+ burstLines.push(
185
+ ' '.repeat(Math.floor(cols / 2 - 12))
186
+ + yellow('· ✧ · ★ ')
187
+ + bold(white('UPFYN'))
188
+ + yellow(' ★ · ✧ ·')
189
+ );
190
+ burstLines.push('');
191
+
192
+ if (mode === 'install') {
193
+ burstLines.push(
194
+ ' '.repeat(Math.floor(cols / 2 - 20))
195
+ + green('✓ ') + bold('upfynai-code') + dim(' installed successfully')
196
+ );
197
+ burstLines.push('');
198
+ burstLines.push(dim(' Quick start:'));
199
+ burstLines.push(cyan(' uc login ') + dim('— authenticate'));
200
+ burstLines.push(cyan(' uc connect ') + dim('— bridge to cloud'));
201
+ burstLines.push(cyan(' uc --local ') + dim('— start local server'));
202
+ } else {
203
+ burstLines.push(
204
+ ' '.repeat(Math.floor(cols / 2 - 16))
205
+ + green('✓ ') + bold('Relay bridge activated')
206
+ );
207
+ }
208
+
209
+ burstLines.push('');
210
+ burstLines.push(dim(' ─'.repeat(Math.floor(cols / 3))));
211
+ burstLines.push('');
212
+
213
+ for (const l of burstLines) console.log(l);
214
+ }
215
+
216
+ /**
217
+ * Postinstall animation (shorter variant)
218
+ */
219
+ export async function playInstallAnimation() {
220
+ return playSpaceshipAnimation('install');
221
+ }
222
+
223
+ /**
224
+ * Connect animation
225
+ */
226
+ export async function playConnectAnimation() {
227
+ return playSpaceshipAnimation('connect');
228
+ }
package/src/connect.js CHANGED
@@ -9,6 +9,7 @@ import { readConfig, writeConfig, displayUrl } from './config.js';
9
9
  import { getToken, validateToken } from './auth.js';
10
10
  import { getPersistentShell } from './persistent-shell.js';
11
11
  import { needsPermission, requestPermission, handlePermissionResponse } from './permissions.js';
12
+ import { playConnectAnimation } from './animation.js';
12
13
 
13
14
  // Resolve agents: dist/agents/ (npm package) or ../../shared/agents/ (monorepo)
14
15
  const __connectDir = dirname(fileURLToPath(import.meta.url));
@@ -220,6 +221,9 @@ export async function connect(options = {}) {
220
221
 
221
222
  const wsUrl = serverUrl.replace(/^http/, 'ws') + '/relay?token=' + encodeURIComponent(relayKey);
222
223
 
224
+ // Play spaceship launch animation
225
+ try { await playConnectAnimation(); } catch { /* cosmetic — don't block connect */ }
226
+
223
227
  console.log(chalk.bold('\n Upfyn-Code Relay Client\n'));
224
228
  console.log(` Server: ${chalk.cyan(displayUrl(serverUrl))}`);
225
229
  console.log(` Machine: ${chalk.dim(os.hostname())}`);
@@ -229,7 +233,13 @@ export async function connect(options = {}) {
229
233
  const MAX_RECONNECT = 10;
230
234
 
231
235
  function doConnect() {
232
- const ws = new WebSocket(wsUrl);
236
+ const ws = new WebSocket(wsUrl, {
237
+ headers: {
238
+ 'x-upfyn-machine': os.hostname(),
239
+ 'x-upfyn-platform': process.platform,
240
+ 'x-upfyn-cwd': process.cwd(),
241
+ },
242
+ });
233
243
 
234
244
  ws.on('open', () => {
235
245
  reconnectAttempts = 0;