fluxy-bot 0.3.27 → 0.4.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/bin/cli.js CHANGED
@@ -30,15 +30,32 @@ const c = {
30
30
  yellow: '\x1b[33m',
31
31
  red: '\x1b[31m',
32
32
  white: '\x1b[97m',
33
+ blue: '\x1b[38;2;50;165;247m',
34
+ pink: '\x1b[38;2;219;54;163m',
33
35
  };
34
36
 
35
37
  const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
36
38
  const BAR_WIDTH = 30;
37
39
 
40
+ function gradientChar(i, total) {
41
+ const t = total > 1 ? i / (total - 1) : 0;
42
+ const r = Math.round(50 + t * (219 - 50));
43
+ const g = Math.round(165 + t * (54 - 165));
44
+ const b = Math.round(247 + t * (163 - 247));
45
+ return `\x1b[38;2;${r};${g};${b}m`;
46
+ }
47
+
38
48
  function progressBar(ratio, width = BAR_WIDTH) {
39
49
  const filled = Math.round(ratio * width);
40
50
  const empty = width - filled;
41
- return `${c.cyan}${''.repeat(filled)}${c.dim}${'░'.repeat(empty)}${c.reset}`;
51
+ let bar = '';
52
+ for (let i = 0; i < filled; i++) bar += `${gradientChar(i, width)}█`;
53
+ bar += `${c.dim}${'░'.repeat(empty)}${c.reset}`;
54
+ return bar;
55
+ }
56
+
57
+ function link(url) {
58
+ return `\x1b]8;;${url}\x07${url}\x1b]8;;\x07`;
42
59
  }
43
60
 
44
61
  class Stepper {
@@ -70,9 +87,9 @@ class Stepper {
70
87
 
71
88
  for (let i = 0; i < this.steps.length; i++) {
72
89
  if (i < this.current) {
73
- console.log(` ${c.green}✔${c.reset} ${this.steps[i]}`);
90
+ console.log(` ${c.blue}✔${c.reset} ${this.steps[i]}`);
74
91
  } else if (i === this.current) {
75
- console.log(` ${c.cyan}${SPINNER[this.frame]}${c.reset} ${this.steps[i]}${c.dim}...${c.reset}`);
92
+ console.log(` ${c.pink}${SPINNER[this.frame]}${c.reset} ${this.steps[i]}${c.dim}...${c.reset}`);
76
93
  } else {
77
94
  console.log(` ${c.dim}○ ${this.steps[i]}${c.reset}`);
78
95
  }
@@ -92,18 +109,19 @@ class Stepper {
92
109
 
93
110
  process.stdout.write(`\x1b[${this.steps.length + 2}A`);
94
111
  for (const step of this.steps) {
95
- console.log(` ${c.green}✔${c.reset} ${step}`);
112
+ console.log(` ${c.blue}✔${c.reset} ${step}`);
96
113
  }
97
- console.log(`\n ${progressBar(1)} ${c.green}Done${c.reset}`);
114
+ console.log(`\n ${progressBar(1)} ${c.pink}Done${c.reset}`);
98
115
  }
99
116
  }
100
117
 
101
118
  function banner() {
102
119
  console.log(`
103
- ${c.cyan}${c.bold}╔═══════════════════════════════╗
104
- ║ FLUXY v${pkg.version.padEnd(13)}
105
- ╚═══════════════════════════════╝${c.reset}
106
- ${c.dim}Self-hosted AI bot${c.reset}`);
120
+ ${c.blue}${c.bold} ___ __ ${c.reset}
121
+ ${c.blue}${c.bold} / _/ / / __ __ __ __ __ ${c.reset}
122
+ ${c.pink}${c.bold} / _/ / / / / / / \\ \\/ / / / ${c.reset}
123
+ ${c.pink}${c.bold} /_/ /_/ \\_,_/ /_/\\_\\ /_/ ${c.reset}
124
+ ${c.dim}v${pkg.version} · Self-hosted AI bot${c.reset}`);
107
125
  }
108
126
 
109
127
  function finalMessage(tunnelUrl, relayUrl) {
@@ -112,13 +130,14 @@ function finalMessage(tunnelUrl, relayUrl) {
112
130
 
113
131
  ${c.bold}${c.white}Open your dashboard to finish setup:${c.reset}
114
132
 
115
- ${c.cyan}${c.bold}${tunnelUrl}${c.reset}`);
133
+ ${c.blue}${c.bold}${link(tunnelUrl)}${c.reset}
134
+ ${c.dim}(cmd+click or ctrl+click to open)${c.reset}`);
116
135
 
117
136
  if (relayUrl) {
118
137
  console.log(`
119
138
  ${c.bold}${c.white}Your permanent URL:${c.reset}
120
139
 
121
- ${c.cyan}${c.bold}${relayUrl}${c.reset}`);
140
+ ${c.pink}${c.bold}${link(relayUrl)}${c.reset}`);
122
141
  }
123
142
 
124
143
  console.log(`
@@ -385,10 +404,10 @@ async function status() {
385
404
  const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
386
405
  const res = await fetch(`http://localhost:${config.port}/api/health`);
387
406
  const data = await res.json();
388
- console.log(`\n ${c.green}●${c.reset} Bot is running`);
407
+ console.log(`\n ${c.blue}●${c.reset} Bot is running`);
389
408
  console.log(` ${c.dim}Uptime: ${data.uptime}s${c.reset}`);
390
409
  if (config.relay?.url) {
391
- console.log(` ${c.dim}URL: ${config.relay.url}${c.reset}`);
410
+ console.log(` ${c.dim}URL: ${c.reset}${c.pink}${link(config.relay.url)}${c.reset}`);
392
411
  }
393
412
  console.log(` ${c.dim}Config: ${CONFIG_PATH}${c.reset}\n`);
394
413
  } catch {
@@ -396,12 +415,128 @@ async function status() {
396
415
  }
397
416
  }
398
417
 
418
+ async function update() {
419
+ banner();
420
+
421
+ const currentVersion = pkg.version;
422
+ console.log(`\n ${c.dim}Current version: v${currentVersion}${c.reset}`);
423
+ console.log(` ${c.blue}⠋${c.reset} Checking for updates...\n`);
424
+
425
+ // Fetch latest package info from npm registry
426
+ let latest;
427
+ try {
428
+ const res = await fetch('https://registry.npmjs.org/fluxy-bot/latest');
429
+ if (!res.ok) throw new Error();
430
+ latest = await res.json();
431
+ } catch {
432
+ console.log(` ${c.red}✗${c.reset} Failed to check for updates\n`);
433
+ process.exit(1);
434
+ }
435
+
436
+ if (currentVersion === latest.version) {
437
+ console.log(` ${c.blue}✔${c.reset} Already up to date (v${currentVersion})\n`);
438
+ return;
439
+ }
440
+
441
+ console.log(` ${c.dim}v${currentVersion} → v${latest.version}${c.reset}\n`);
442
+
443
+ const steps = [
444
+ 'Downloading update',
445
+ 'Updating files',
446
+ 'Installing dependencies',
447
+ 'Building interface',
448
+ ];
449
+
450
+ const stepper = new Stepper(steps);
451
+ stepper.start();
452
+
453
+ // Download tarball
454
+ const tarballUrl = latest.dist.tarball;
455
+ const tmpDir = path.join(os.tmpdir(), `fluxy-update-${Date.now()}`);
456
+ fs.mkdirSync(tmpDir, { recursive: true });
457
+ const tarball = path.join(tmpDir, 'fluxy.tgz');
458
+
459
+ try {
460
+ const res = await fetch(tarballUrl);
461
+ if (!res.ok) throw new Error();
462
+ const buf = Buffer.from(await res.arrayBuffer());
463
+ fs.writeFileSync(tarball, buf);
464
+ execSync(`tar xzf "${tarball}" -C "${tmpDir}"`, { stdio: 'ignore' });
465
+ } catch {
466
+ stepper.finish();
467
+ console.log(`\n ${c.red}✗${c.reset} Download failed\n`);
468
+ fs.rmSync(tmpDir, { recursive: true, force: true });
469
+ process.exit(1);
470
+ }
471
+ stepper.advance();
472
+
473
+ const extracted = path.join(tmpDir, 'package');
474
+
475
+ // Update code directories (preserve workspace/ user data)
476
+ for (const dir of ['bin', 'supervisor', 'worker', 'shared', 'scripts']) {
477
+ const src = path.join(extracted, dir);
478
+ if (fs.existsSync(src)) {
479
+ fs.cpSync(src, path.join(DATA_DIR, dir), { recursive: true, force: true });
480
+ }
481
+ }
482
+
483
+ // Copy workspace template only if it doesn't exist yet
484
+ if (!fs.existsSync(path.join(DATA_DIR, 'workspace'))) {
485
+ const wsSrc = path.join(extracted, 'workspace');
486
+ if (fs.existsSync(wsSrc)) {
487
+ fs.cpSync(wsSrc, path.join(DATA_DIR, 'workspace'), { recursive: true });
488
+ }
489
+ }
490
+
491
+ // Update code files (never touches config.json, memory.db, etc.)
492
+ for (const file of ['package.json', 'vite.config.ts', 'vite.fluxy.config.ts', 'tsconfig.json', 'postcss.config.js', 'components.json']) {
493
+ const src = path.join(extracted, file);
494
+ if (fs.existsSync(src)) {
495
+ fs.copyFileSync(src, path.join(DATA_DIR, file));
496
+ }
497
+ }
498
+
499
+ // Update pre-built UI from tarball
500
+ const distSrc = path.join(extracted, 'dist-fluxy');
501
+ const distDst = path.join(DATA_DIR, 'dist-fluxy');
502
+ if (fs.existsSync(distSrc)) {
503
+ if (fs.existsSync(distDst)) fs.rmSync(distDst, { recursive: true });
504
+ fs.cpSync(distSrc, distDst, { recursive: true });
505
+ }
506
+
507
+ stepper.advance();
508
+
509
+ // Install dependencies
510
+ try {
511
+ execSync('npm install --omit=dev', { cwd: DATA_DIR, stdio: 'ignore' });
512
+ } catch {}
513
+ stepper.advance();
514
+
515
+ // Rebuild UI if not in tarball
516
+ if (!fs.existsSync(path.join(distDst, 'onboard.html'))) {
517
+ try {
518
+ if (fs.existsSync(distDst)) fs.rmSync(distDst, { recursive: true });
519
+ execSync('npm run build:fluxy', { cwd: DATA_DIR, stdio: 'ignore' });
520
+ } catch {}
521
+ }
522
+ stepper.advance();
523
+
524
+ // Clean up
525
+ fs.rmSync(tmpDir, { recursive: true, force: true });
526
+
527
+ stepper.finish();
528
+
529
+ console.log(`\n ${c.blue}${c.bold}✔ Updated to v${latest.version}${c.reset}\n`);
530
+ console.log(` ${c.dim}Run ${c.reset}${c.pink}fluxy start${c.reset}${c.dim} to launch.${c.reset}\n`);
531
+ }
532
+
399
533
  // ── Route ──
400
534
 
401
535
  switch (command) {
402
536
  case 'init': init(); break;
403
537
  case 'start': start(); break;
404
538
  case 'status': status(); break;
539
+ case 'update': update(); break;
405
540
  default:
406
541
  fs.existsSync(CONFIG_PATH) ? start() : init();
407
542
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxy-bot",
3
- "version": "0.3.27",
4
- "description": "Self-hosted AI bot run your own AI assistant from anywhere",
3
+ "version": "0.4.1",
4
+ "description": "Self-hosted, self-evolving AI agent with its own dashboard.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -20,7 +20,6 @@
20
20
  "tsconfig.json",
21
21
  "postcss.config.js",
22
22
  "components.json",
23
- "install.sh",
24
23
  "install.ps1"
25
24
  ],
26
25
  "keywords": [
package/scripts/install CHANGED
@@ -15,18 +15,23 @@ NODE_DIR="$TOOLS_DIR/node"
15
15
  BIN_DIR="$FLUXY_HOME/bin"
16
16
  USE_SYSTEM_NODE=false
17
17
 
18
- CYAN='\033[36m'
19
- GREEN='\033[32m'
18
+ # Brand colors: #32A5F7 (blue) and #DB36A3 (pink) via 256-color approximation
19
+ BLUE='\033[38;2;50;165;247m'
20
+ PINK='\033[38;2;219;54;163m'
20
21
  YELLOW='\033[33m'
21
22
  RED='\033[31m'
22
23
  DIM='\033[2m'
23
24
  BOLD='\033[1m'
24
25
  RESET='\033[0m'
25
26
 
26
- printf "\n${CYAN}${BOLD} ╔═══════════════════════════════╗${RESET}\n"
27
- printf "${CYAN}${BOLD} ║ FLUXY ║${RESET}\n"
28
- printf "${CYAN}${BOLD} ╚═══════════════════════════════╝${RESET}\n"
29
- printf "${DIM} Self-hosted AI bot${RESET}\n\n"
27
+ printf "\n"
28
+ printf "${BLUE}${BOLD} ___ __ ${RESET}\n"
29
+ printf "${BLUE}${BOLD} / _/ / / __ __ __ __ __ ${RESET}\n"
30
+ printf "${PINK}${BOLD} / _/ / / / / / / \\ \\/ / / / ${RESET}\n"
31
+ printf "${PINK}${BOLD} /_/ /_/ \\_,_/ /_/\\_\\ /_/ ${RESET}\n"
32
+ printf "\n"
33
+ printf "${DIM} Self-hosted, self-evolving AI agent with its own dashboard.${RESET}\n"
34
+ printf "${DIM} ─────────────────────────────${RESET}\n\n"
30
35
 
31
36
  # ─── Detect platform ────────────────────────────────────────────────────────
32
37
 
@@ -65,7 +70,7 @@ check_system_node() {
65
70
  MAJOR=$(echo "$SYS_NODE_VERSION" | sed 's/^v//' | cut -d. -f1)
66
71
  if [ "$MAJOR" -ge "$MIN_NODE_MAJOR" ] 2>/dev/null; then
67
72
  USE_SYSTEM_NODE=true
68
- printf " ${GREEN}✔${RESET} Node.js ${SYS_NODE_VERSION} (system)\n"
73
+ printf " ${BLUE}✔${RESET} Node.js ${SYS_NODE_VERSION} (system)\n"
69
74
  return 0
70
75
  fi
71
76
  fi
@@ -80,12 +85,12 @@ install_node() {
80
85
  if [ -x "$NODE_DIR/bin/node" ]; then
81
86
  EXISTING=$("$NODE_DIR/bin/node" -v 2>/dev/null || echo "")
82
87
  if [ -n "$EXISTING" ]; then
83
- printf " ${GREEN}✔${RESET} Node.js ${EXISTING} (bundled)\n"
88
+ printf " ${BLUE}✔${RESET} Node.js ${EXISTING} (bundled)\n"
84
89
  return 0
85
90
  fi
86
91
  fi
87
92
 
88
- printf " ${CYAN}↓${RESET} Downloading Node.js v${NODE_VERSION}...\n"
93
+ printf " ${BLUE}↓${RESET} Downloading Node.js v${NODE_VERSION}...\n"
89
94
 
90
95
  NODE_URL="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-${PLATFORM}-${NODEARCH}.tar.xz"
91
96
  TMPFILE=$(mktemp /tmp/node-XXXXXX.tar.xz)
@@ -114,7 +119,7 @@ install_node() {
114
119
  exit 1
115
120
  fi
116
121
 
117
- printf " ${GREEN}✔${RESET} Node.js v${NODE_VERSION} installed\n"
122
+ printf " ${BLUE}✔${RESET} Node.js v${NODE_VERSION} installed\n"
118
123
  }
119
124
 
120
125
  # ─── Install Fluxy ────────────────────────────────────────────────────────
@@ -128,9 +133,14 @@ install_fluxy() {
128
133
  NODE="$NODE_DIR/bin/node"
129
134
  fi
130
135
 
131
- printf " ${CYAN}↓${RESET} Installing fluxy...\n"
136
+ # Fetch version + tarball URL from npm registry
137
+ NPM_VERSION=$("$NPM" view fluxy-bot version 2>/dev/null || echo "")
138
+ if [ -n "$NPM_VERSION" ]; then
139
+ printf " ${DIM}Latest npm version: fluxy-bot@${NPM_VERSION}${RESET}\n"
140
+ fi
141
+
142
+ printf " ${BLUE}↓${RESET} Installing fluxy...\n"
132
143
 
133
- # Get tarball URL from npm registry
134
144
  TARBALL_URL=$("$NPM" view fluxy-bot dist.tarball 2>/dev/null)
135
145
  if [ -z "$TARBALL_URL" ]; then
136
146
  printf " ${RED}✗${RESET} Failed to fetch package info from npm\n"
@@ -154,26 +164,42 @@ install_fluxy() {
154
164
  exit 1
155
165
  fi
156
166
 
157
- # Copy source files to ~/.fluxy/ (preserves existing config.json, memory.db, etc.)
158
- cp -r "$EXTRACTED"/* "$FLUXY_HOME"/
159
- cp -r "$EXTRACTED"/.[!.]* "$FLUXY_HOME"/ 2>/dev/null || true
160
- rm -rf "$TMPDIR"
167
+ # Copy code directories (always safe to overwrite)
168
+ for dir in bin supervisor worker shared scripts; do
169
+ [ -d "$EXTRACTED/$dir" ] && cp -r "$EXTRACTED/$dir" "$FLUXY_HOME/"
170
+ done
161
171
 
162
- # Install dependencies inside ~/.fluxy/
163
- (cd "$FLUXY_HOME" && "$NPM" install --omit=dev 2>/dev/null)
172
+ # Copy workspace template only on first install (preserves user files)
173
+ if [ ! -d "$FLUXY_HOME/workspace" ]; then
174
+ [ -d "$EXTRACTED/workspace" ] && cp -r "$EXTRACTED/workspace" "$FLUXY_HOME/"
175
+ fi
164
176
 
165
- # Build chat interface if not pre-built in the tarball
166
- if [ -d "$FLUXY_HOME/dist-fluxy" ] && [ -f "$FLUXY_HOME/dist-fluxy/onboard.html" ]; then
167
- printf " ${GREEN}✔${RESET} Chat interface ready\n"
168
- else
169
- printf " ${CYAN}↓${RESET} Building chat interface...\n"
177
+ # Copy code files (never touches config.json, memory.db, etc.)
178
+ for f in package.json vite.config.ts vite.fluxy.config.ts tsconfig.json postcss.config.js components.json; do
179
+ [ -f "$EXTRACTED/$f" ] && cp "$EXTRACTED/$f" "$FLUXY_HOME/"
180
+ done
181
+
182
+ # Copy pre-built UI from tarball, or build from source
183
+ if [ -d "$EXTRACTED/dist-fluxy" ]; then
184
+ rm -rf "$FLUXY_HOME/dist-fluxy"
185
+ cp -r "$EXTRACTED/dist-fluxy" "$FLUXY_HOME/"
186
+ printf " ${BLUE}✔${RESET} Chat interface ready\n"
187
+ elif [ ! -f "$FLUXY_HOME/dist-fluxy/onboard.html" ]; then
188
+ printf " ${BLUE}↓${RESET} Building chat interface...\n"
170
189
  if (cd "$FLUXY_HOME" && "$NPM" run build:fluxy 2>/dev/null); then
171
- printf " ${GREEN}✔${RESET} Chat interface built\n"
190
+ printf " ${BLUE}✔${RESET} Chat interface built\n"
172
191
  else
173
192
  printf " ${YELLOW}!${RESET} Chat build skipped — will build on first start\n"
174
193
  fi
194
+ else
195
+ printf " ${BLUE}✔${RESET} Chat interface ready\n"
175
196
  fi
176
197
 
198
+ rm -rf "$TMPDIR"
199
+
200
+ # Install dependencies inside ~/.fluxy/
201
+ (cd "$FLUXY_HOME" && "$NPM" install --omit=dev 2>/dev/null)
202
+
177
203
  # Verify
178
204
  if [ ! -f "$FLUXY_HOME/bin/cli.js" ]; then
179
205
  printf " ${RED}✗${RESET} Installation failed\n"
@@ -182,7 +208,7 @@ install_fluxy() {
182
208
 
183
209
  VERSION=$("$NODE" -e "const p=JSON.parse(require('fs').readFileSync('$FLUXY_HOME/package.json','utf8'));console.log(p.version)" 2>/dev/null || echo "unknown")
184
210
 
185
- printf " ${GREEN}✔${RESET} Fluxy v${VERSION} installed\n"
211
+ printf " ${BLUE}✔${RESET} Fluxy v${VERSION} installed\n"
186
212
  }
187
213
 
188
214
  # ─── Create wrapper script ──────────────────────────────────────────────────
@@ -210,7 +236,7 @@ WRAPPER
210
236
  fi
211
237
 
212
238
  chmod +x "$BIN_DIR/fluxy"
213
- printf " ${GREEN}✔${RESET} Created ${DIM}~/.fluxy/bin/fluxy${RESET}\n"
239
+ printf " ${BLUE}✔${RESET} Created ${DIM}~/.fluxy/bin/fluxy${RESET}\n"
214
240
  }
215
241
 
216
242
  # ─── Add to PATH ────────────────────────────────────────────────────────────
@@ -244,7 +270,7 @@ setup_path() {
244
270
  if ! grep -q "fluxy/bin" "$HOME/.config/fish/config.fish" 2>/dev/null; then
245
271
  echo 'set -gx PATH "$HOME/.fluxy/bin" $PATH' >> "$HOME/.config/fish/config.fish"
246
272
  fi
247
- printf " ${GREEN}✔${RESET} Added to PATH ${DIM}(~/.config/fish/config.fish)${RESET}\n"
273
+ printf " ${BLUE}✔${RESET} Added to PATH ${DIM}(~/.config/fish/config.fish)${RESET}\n"
248
274
  return 0
249
275
  ;;
250
276
  *) PROFILE="$HOME/.profile" ;;
@@ -254,7 +280,7 @@ setup_path() {
254
280
  if ! grep -q "fluxy/bin" "$PROFILE" 2>/dev/null; then
255
281
  printf "\n# Fluxy\n%s\n" "$EXPORT_LINE" >> "$PROFILE"
256
282
  fi
257
- printf " ${GREEN}✔${RESET} Added to PATH ${DIM}(${PROFILE})${RESET}\n"
283
+ printf " ${BLUE}✔${RESET} Added to PATH ${DIM}(${PROFILE})${RESET}\n"
258
284
  fi
259
285
 
260
286
  export PATH="$BIN_DIR:$PATH"
@@ -270,10 +296,18 @@ install_fluxy
270
296
  create_wrapper
271
297
  setup_path
272
298
 
273
- printf "\n ${GREEN}${BOLD}✔ Fluxy is ready!${RESET}\n\n"
274
- printf " Get started:\n\n"
275
- printf " ${CYAN}fluxy init${RESET} Set up your bot\n"
276
- printf " ${CYAN}fluxy start${RESET} Start your bot\n"
277
- printf " ${CYAN}fluxy status${RESET} Check if it's running\n\n"
278
- printf " ${DIM}Run ${RESET}${CYAN}fluxy init${RESET}${DIM} to begin.${RESET}\n"
279
- printf " ${DIM}(Open a new terminal if 'fluxy' isn't found yet)${RESET}\n\n"
299
+ printf "\n"
300
+ printf " ${PINK}${BOLD}✔ Fluxy is ready!${RESET}\n"
301
+ printf "\n"
302
+ printf " ${DIM}─────────────────────────────${RESET}\n"
303
+ printf " ${BOLD}Get started:${RESET}\n"
304
+ printf "\n"
305
+ printf " ${BLUE}fluxy init${RESET} Set up your bot\n"
306
+ printf " ${BLUE}fluxy start${RESET} Start your bot\n"
307
+ printf " ${BLUE}fluxy status${RESET} Check if it's running\n"
308
+ printf "\n"
309
+ printf " ${PINK}>${RESET} Run ${BLUE}fluxy init${RESET} to begin.\n"
310
+ printf " ${DIM}(Open a new terminal if 'fluxy' isn't found yet)${RESET}\n"
311
+ printf "\n"
312
+ printf " ${DIM}\033]8;;https://fluxy.bot\033\\https://fluxy.bot\033]8;;\033\\${RESET}\n"
313
+ printf "\n"
@@ -14,11 +14,43 @@ $NODE_DIR = Join-Path $TOOLS_DIR "node"
14
14
  $BIN_DIR = Join-Path $FLUXY_HOME "bin"
15
15
  $USE_SYSTEM_NODE = $false
16
16
 
17
+ # Ensure UTF-8 output for proper rendering
18
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
19
+
20
+ # Brand colors via ANSI escape sequences: #32A5F7 (blue) and #DB36A3 (pink)
21
+ $BLUE = "`e[38;2;50;165;247m"
22
+ $PINK = "`e[38;2;219;54;163m"
23
+ $BOLD = "`e[1m"
24
+ $RSET = "`e[0m"
25
+
26
+ # Use ANSI sequences for consistent rendering; fallback to plain if no VT support
27
+ $vtSupported = $null -ne $env:WT_SESSION -or $PSVersionTable.PSVersion.Major -ge 7 -or $host.UI.SupportsVirtualTerminal
28
+
29
+ function Write-Check($text) {
30
+ if ($vtSupported) { Write-Host " ${BLUE}✔${RSET} $text" }
31
+ else { Write-Host " ✔ $text" -ForegroundColor Cyan }
32
+ }
33
+
34
+ function Write-Down($text) {
35
+ if ($vtSupported) { Write-Host " ${PINK}↓${RSET} $text" }
36
+ else { Write-Host " ↓ $text" -ForegroundColor Cyan }
37
+ }
38
+
39
+ Write-Host ""
40
+ if ($vtSupported) {
41
+ Write-Host "${BLUE}${BOLD} ___ __ ${RSET}"
42
+ Write-Host "${BLUE}${BOLD} / _/ / / __ __ __ __ __ ${RSET}"
43
+ Write-Host "${PINK}${BOLD} / _/ / / / / / / \ \/ / / / ${RSET}"
44
+ Write-Host "${PINK}${BOLD} /_/ /_/ \_,_/ /_/\_\ /_/ ${RSET}"
45
+ } else {
46
+ Write-Host " ___ __ " -ForegroundColor Cyan
47
+ Write-Host " / _/ / / __ __ __ __ __ " -ForegroundColor Cyan
48
+ Write-Host " / _/ / / / / / / \ \/ / / / " -ForegroundColor Magenta
49
+ Write-Host " /_/ /_/ \_,_/ /_/\_\ /_/ " -ForegroundColor Magenta
50
+ }
17
51
  Write-Host ""
18
- Write-Host " ╔═══════════════════════════════╗" -ForegroundColor Cyan
19
- Write-Host " ║ FLUXY ║" -ForegroundColor Cyan
20
- Write-Host " ╚═══════════════════════════════╝" -ForegroundColor Cyan
21
- Write-Host " Self-hosted AI bot" -ForegroundColor DarkGray
52
+ Write-Host " Self-hosted, self-evolving AI agent with its own dashboard." -ForegroundColor DarkGray
53
+ Write-Host " -----------------------------" -ForegroundColor DarkGray
22
54
  Write-Host ""
23
55
 
24
56
  # ─── Detect platform ────────────────────────────────────────────────────────
@@ -50,7 +82,7 @@ function Check-SystemNode {
50
82
  $major = [int]$Matches[1]
51
83
  if ($major -ge $MIN_NODE_MAJOR) {
52
84
  $script:USE_SYSTEM_NODE = $true
53
- Write-Host "Node.js $ver (system)" -ForegroundColor Green
85
+ Write-Check "Node.js $ver (system)"
54
86
  return $true
55
87
  }
56
88
  }
@@ -69,13 +101,13 @@ function Install-Node {
69
101
  try {
70
102
  $existing = & $nodeBin -v 2>$null
71
103
  if ($existing) {
72
- Write-Host "Node.js $existing (bundled)" -ForegroundColor Green
104
+ Write-Check "Node.js $existing (bundled)"
73
105
  return
74
106
  }
75
107
  } catch {}
76
108
  }
77
109
 
78
- Write-Host "Downloading Node.js v${NODE_VERSION}..." -ForegroundColor Cyan
110
+ Write-Down "Downloading Node.js v${NODE_VERSION}..."
79
111
 
80
112
  $nodeUrl = "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-win-${NODEARCH}.zip"
81
113
  $tmpFile = Join-Path ([System.IO.Path]::GetTempPath()) "node-fluxy.zip"
@@ -103,7 +135,7 @@ function Install-Node {
103
135
  exit 1
104
136
  }
105
137
 
106
- Write-Host "Node.js v${NODE_VERSION} installed" -ForegroundColor Green
138
+ Write-Check "Node.js v${NODE_VERSION} installed"
107
139
  }
108
140
 
109
141
  # ─── Install Fluxy ────────────────────────────────────────────────────────
@@ -117,9 +149,15 @@ function Install-Fluxy {
117
149
  $NODE_BIN = Join-Path $NODE_DIR "node.exe"
118
150
  }
119
151
 
120
- Write-Host " ↓ Installing fluxy..." -ForegroundColor Cyan
152
+ # Fetch version + tarball URL from npm registry
153
+ $npmVersion = ""
154
+ try { $npmVersion = (& $NPM view fluxy-bot version 2>$null).Trim() } catch {}
155
+ if ($npmVersion) {
156
+ Write-Host " Latest npm version: fluxy-bot@${npmVersion}" -ForegroundColor DarkGray
157
+ }
158
+
159
+ Write-Down "Installing fluxy..."
121
160
 
122
- # Get tarball URL from npm registry
123
161
  $tarballUrl = (& $NPM view fluxy-bot dist.tarball 2>$null).Trim()
124
162
  if (-not $tarballUrl) {
125
163
  Write-Host " ✗ Failed to fetch package info from npm" -ForegroundColor Red
@@ -142,10 +180,52 @@ function Install-Fluxy {
142
180
  exit 1
143
181
  }
144
182
 
145
- # Copy source files to ~/.fluxy/ (preserves existing config.json, memory.db, etc.)
146
183
  New-Item -ItemType Directory -Path $FLUXY_HOME -Force | Out-Null
147
- Get-ChildItem -Path $extracted -Force | ForEach-Object {
148
- Copy-Item -Path $_.FullName -Destination $FLUXY_HOME -Recurse -Force
184
+
185
+ # Copy code directories (always safe to overwrite)
186
+ foreach ($dir in @("bin", "supervisor", "worker", "shared", "scripts")) {
187
+ $src = Join-Path $extracted $dir
188
+ if (Test-Path $src) {
189
+ Copy-Item -Path $src -Destination $FLUXY_HOME -Recurse -Force
190
+ }
191
+ }
192
+
193
+ # Copy workspace template only on first install (preserves user files)
194
+ $wsDst = Join-Path $FLUXY_HOME "workspace"
195
+ if (-not (Test-Path $wsDst)) {
196
+ $wsSrc = Join-Path $extracted "workspace"
197
+ if (Test-Path $wsSrc) {
198
+ Copy-Item -Path $wsSrc -Destination $FLUXY_HOME -Recurse
199
+ }
200
+ }
201
+
202
+ # Copy code files (never touches config.json, memory.db, etc.)
203
+ foreach ($file in @("package.json", "vite.config.ts", "vite.fluxy.config.ts", "tsconfig.json", "postcss.config.js", "components.json")) {
204
+ $src = Join-Path $extracted $file
205
+ if (Test-Path $src) {
206
+ Copy-Item -Path $src -Destination (Join-Path $FLUXY_HOME $file) -Force
207
+ }
208
+ }
209
+
210
+ # Copy pre-built UI from tarball, or build from source
211
+ $distSrc = Join-Path $extracted "dist-fluxy"
212
+ $distDst = Join-Path $FLUXY_HOME "dist-fluxy"
213
+ if (Test-Path $distSrc) {
214
+ if (Test-Path $distDst) { Remove-Item $distDst -Recurse -Force }
215
+ Copy-Item -Path $distSrc -Destination $distDst -Recurse
216
+ Write-Check "Chat interface ready"
217
+ } elseif (-not (Test-Path (Join-Path $distDst "onboard.html"))) {
218
+ Write-Down "Building chat interface..."
219
+ Push-Location $FLUXY_HOME
220
+ try {
221
+ & $NPM run build:fluxy 2>$null
222
+ Write-Check "Chat interface built"
223
+ } catch {
224
+ Write-Host " ! Chat build failed — will build on first start" -ForegroundColor Yellow
225
+ }
226
+ Pop-Location
227
+ } else {
228
+ Write-Check "Chat interface ready"
149
229
  }
150
230
  } finally {
151
231
  Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
@@ -156,21 +236,6 @@ function Install-Fluxy {
156
236
  try {
157
237
  & $NPM install --omit=dev 2>$null
158
238
  } catch {}
159
-
160
- # Build fluxy chat + onboard (served as static files)
161
- $distFluxy = Join-Path $FLUXY_HOME "dist-fluxy"
162
- $onboardHtml = Join-Path $distFluxy "onboard.html"
163
- if ((Test-Path $distFluxy) -and (Test-Path $onboardHtml)) {
164
- Write-Host " ✔ Chat interface (pre-built)" -ForegroundColor Green
165
- } else {
166
- Write-Host " ↓ Building chat interface..." -ForegroundColor Cyan
167
- try {
168
- & $NPM run build:fluxy 2>$null
169
- Write-Host " ✔ Chat interface built" -ForegroundColor Green
170
- } catch {
171
- Write-Host " ! Chat interface build failed — it will be built on first start" -ForegroundColor Yellow
172
- }
173
- }
174
239
  Pop-Location
175
240
 
176
241
  # Verify
@@ -186,7 +251,7 @@ function Install-Fluxy {
186
251
  $script:VERSION = $pkgJson.version
187
252
  } catch {}
188
253
 
189
- Write-Host "Fluxy v${VERSION} installed" -ForegroundColor Green
254
+ Write-Check "Fluxy v${VERSION} installed"
190
255
  }
191
256
 
192
257
  # ─── Create wrapper script ──────────────────────────────────────────────────
@@ -211,7 +276,7 @@ node "%USERPROFILE%\.fluxy\bin\cli.js" %*
211
276
  }
212
277
 
213
278
  Set-Content -Path $wrapperPath -Value $wrapper -Encoding ASCII
214
- Write-Host "Created ~/.fluxy/bin/fluxy.cmd" -ForegroundColor Green
279
+ Write-Check "Created ~/.fluxy/bin/fluxy.cmd"
215
280
  }
216
281
 
217
282
  # ─── Add to PATH ────────────────────────────────────────────────────────────
@@ -221,9 +286,9 @@ function Setup-Path {
221
286
  if ($userPath -notlike "*$BIN_DIR*") {
222
287
  [Environment]::SetEnvironmentVariable("Path", "$BIN_DIR;$userPath", "User")
223
288
  $env:Path = "$BIN_DIR;$env:Path"
224
- Write-Host "Added to PATH" -ForegroundColor Green
289
+ Write-Check "Added to PATH"
225
290
  } else {
226
- Write-Host "PATH already configured" -ForegroundColor Green
291
+ Write-Check "PATH already configured"
227
292
  }
228
293
  }
229
294
 
@@ -238,16 +303,32 @@ Create-Wrapper
238
303
  Setup-Path
239
304
 
240
305
  Write-Host ""
241
- Write-Host " ✔ Fluxy is ready!" -ForegroundColor Green
306
+ if ($vtSupported) {
307
+ Write-Host " ${PINK}${BOLD}✔ Fluxy is ready!${RSET}"
308
+ } else {
309
+ Write-Host " ✔ Fluxy is ready!" -ForegroundColor Magenta
310
+ }
242
311
  Write-Host ""
312
+ Write-Host " -----------------------------" -ForegroundColor DarkGray
243
313
  Write-Host " Get started:"
244
314
  Write-Host ""
245
- Write-Host " fluxy init " -ForegroundColor Cyan -NoNewline; Write-Host "Set up your bot"
246
- Write-Host " fluxy start " -ForegroundColor Cyan -NoNewline; Write-Host "Start your bot"
247
- Write-Host " fluxy status " -ForegroundColor Cyan -NoNewline; Write-Host "Check if it's running"
248
- Write-Host ""
249
- Write-Host " Run " -ForegroundColor DarkGray -NoNewline
250
- Write-Host "fluxy init" -ForegroundColor Cyan -NoNewline
251
- Write-Host " to begin." -ForegroundColor DarkGray
315
+ if ($vtSupported) {
316
+ Write-Host " ${BLUE}fluxy init${RSET} Set up your bot"
317
+ Write-Host " ${BLUE}fluxy start${RSET} Start your bot"
318
+ Write-Host " ${BLUE}fluxy status${RSET} Check if it's running"
319
+ Write-Host ""
320
+ Write-Host " ${PINK}>${RSET} Run ${BLUE}fluxy init${RSET} to begin."
321
+ } else {
322
+ Write-Host " fluxy init " -ForegroundColor Cyan -NoNewline; Write-Host "Set up your bot"
323
+ Write-Host " fluxy start " -ForegroundColor Cyan -NoNewline; Write-Host "Start your bot"
324
+ Write-Host " fluxy status " -ForegroundColor Cyan -NoNewline; Write-Host "Check if it's running"
325
+ Write-Host ""
326
+ Write-Host " > " -ForegroundColor Magenta -NoNewline
327
+ Write-Host "Run " -NoNewline
328
+ Write-Host "fluxy init" -ForegroundColor Cyan -NoNewline
329
+ Write-Host " to begin."
330
+ }
252
331
  Write-Host " (Open a new terminal if 'fluxy' isn't found yet)" -ForegroundColor DarkGray
253
332
  Write-Host ""
333
+ Write-Host " https://fluxy.bot" -ForegroundColor DarkGray
334
+ Write-Host ""
@@ -15,18 +15,23 @@ NODE_DIR="$TOOLS_DIR/node"
15
15
  BIN_DIR="$FLUXY_HOME/bin"
16
16
  USE_SYSTEM_NODE=false
17
17
 
18
- CYAN='\033[36m'
19
- GREEN='\033[32m'
18
+ # Brand colors: #32A5F7 (blue) and #DB36A3 (pink) via 256-color approximation
19
+ BLUE='\033[38;2;50;165;247m'
20
+ PINK='\033[38;2;219;54;163m'
20
21
  YELLOW='\033[33m'
21
22
  RED='\033[31m'
22
23
  DIM='\033[2m'
23
24
  BOLD='\033[1m'
24
25
  RESET='\033[0m'
25
26
 
26
- printf "\n${CYAN}${BOLD} ╔═══════════════════════════════╗${RESET}\n"
27
- printf "${CYAN}${BOLD} ║ FLUXY ║${RESET}\n"
28
- printf "${CYAN}${BOLD} ╚═══════════════════════════════╝${RESET}\n"
29
- printf "${DIM} Self-hosted AI bot${RESET}\n\n"
27
+ printf "\n"
28
+ printf "${BLUE}${BOLD} ___ __ ${RESET}\n"
29
+ printf "${BLUE}${BOLD} / _/ / / __ __ __ __ __ ${RESET}\n"
30
+ printf "${PINK}${BOLD} / _/ / / / / / / \\ \\/ / / / ${RESET}\n"
31
+ printf "${PINK}${BOLD} /_/ /_/ \\_,_/ /_/\\_\\ /_/ ${RESET}\n"
32
+ printf "\n"
33
+ printf "${DIM} Self-hosted, self-evolving AI agent with its own dashboard.${RESET}\n"
34
+ printf "${DIM} ─────────────────────────────${RESET}\n\n"
30
35
 
31
36
  # ─── Detect platform ────────────────────────────────────────────────────────
32
37
 
@@ -65,7 +70,7 @@ check_system_node() {
65
70
  MAJOR=$(echo "$SYS_NODE_VERSION" | sed 's/^v//' | cut -d. -f1)
66
71
  if [ "$MAJOR" -ge "$MIN_NODE_MAJOR" ] 2>/dev/null; then
67
72
  USE_SYSTEM_NODE=true
68
- printf " ${GREEN}✔${RESET} Node.js ${SYS_NODE_VERSION} (system)\n"
73
+ printf " ${BLUE}✔${RESET} Node.js ${SYS_NODE_VERSION} (system)\n"
69
74
  return 0
70
75
  fi
71
76
  fi
@@ -80,12 +85,12 @@ install_node() {
80
85
  if [ -x "$NODE_DIR/bin/node" ]; then
81
86
  EXISTING=$("$NODE_DIR/bin/node" -v 2>/dev/null || echo "")
82
87
  if [ -n "$EXISTING" ]; then
83
- printf " ${GREEN}✔${RESET} Node.js ${EXISTING} (bundled)\n"
88
+ printf " ${BLUE}✔${RESET} Node.js ${EXISTING} (bundled)\n"
84
89
  return 0
85
90
  fi
86
91
  fi
87
92
 
88
- printf " ${CYAN}↓${RESET} Downloading Node.js v${NODE_VERSION}...\n"
93
+ printf " ${BLUE}↓${RESET} Downloading Node.js v${NODE_VERSION}...\n"
89
94
 
90
95
  NODE_URL="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-${PLATFORM}-${NODEARCH}.tar.xz"
91
96
  TMPFILE=$(mktemp /tmp/node-XXXXXX.tar.xz)
@@ -114,7 +119,7 @@ install_node() {
114
119
  exit 1
115
120
  fi
116
121
 
117
- printf " ${GREEN}✔${RESET} Node.js v${NODE_VERSION} installed\n"
122
+ printf " ${BLUE}✔${RESET} Node.js v${NODE_VERSION} installed\n"
118
123
  }
119
124
 
120
125
  # ─── Install Fluxy ────────────────────────────────────────────────────────
@@ -128,9 +133,14 @@ install_fluxy() {
128
133
  NODE="$NODE_DIR/bin/node"
129
134
  fi
130
135
 
131
- printf " ${CYAN}↓${RESET} Installing fluxy...\n"
136
+ # Fetch version + tarball URL from npm registry
137
+ NPM_VERSION=$("$NPM" view fluxy-bot version 2>/dev/null || echo "")
138
+ if [ -n "$NPM_VERSION" ]; then
139
+ printf " ${DIM}Latest npm version: fluxy-bot@${NPM_VERSION}${RESET}\n"
140
+ fi
141
+
142
+ printf " ${BLUE}↓${RESET} Installing fluxy...\n"
132
143
 
133
- # Get tarball URL from npm registry
134
144
  TARBALL_URL=$("$NPM" view fluxy-bot dist.tarball 2>/dev/null)
135
145
  if [ -z "$TARBALL_URL" ]; then
136
146
  printf " ${RED}✗${RESET} Failed to fetch package info from npm\n"
@@ -154,26 +164,42 @@ install_fluxy() {
154
164
  exit 1
155
165
  fi
156
166
 
157
- # Copy source files to ~/.fluxy/ (preserves existing config.json, memory.db, etc.)
158
- cp -r "$EXTRACTED"/* "$FLUXY_HOME"/
159
- cp -r "$EXTRACTED"/.[!.]* "$FLUXY_HOME"/ 2>/dev/null || true
160
- rm -rf "$TMPDIR"
167
+ # Copy code directories (always safe to overwrite)
168
+ for dir in bin supervisor worker shared scripts; do
169
+ [ -d "$EXTRACTED/$dir" ] && cp -r "$EXTRACTED/$dir" "$FLUXY_HOME/"
170
+ done
161
171
 
162
- # Install dependencies inside ~/.fluxy/
163
- (cd "$FLUXY_HOME" && "$NPM" install --omit=dev 2>/dev/null)
172
+ # Copy workspace template only on first install (preserves user files)
173
+ if [ ! -d "$FLUXY_HOME/workspace" ]; then
174
+ [ -d "$EXTRACTED/workspace" ] && cp -r "$EXTRACTED/workspace" "$FLUXY_HOME/"
175
+ fi
164
176
 
165
- # Build chat interface if not pre-built in the tarball
166
- if [ -d "$FLUXY_HOME/dist-fluxy" ] && [ -f "$FLUXY_HOME/dist-fluxy/onboard.html" ]; then
167
- printf " ${GREEN}✔${RESET} Chat interface ready\n"
168
- else
169
- printf " ${CYAN}↓${RESET} Building chat interface...\n"
177
+ # Copy code files (never touches config.json, memory.db, etc.)
178
+ for f in package.json vite.config.ts vite.fluxy.config.ts tsconfig.json postcss.config.js components.json; do
179
+ [ -f "$EXTRACTED/$f" ] && cp "$EXTRACTED/$f" "$FLUXY_HOME/"
180
+ done
181
+
182
+ # Copy pre-built UI from tarball, or build from source
183
+ if [ -d "$EXTRACTED/dist-fluxy" ]; then
184
+ rm -rf "$FLUXY_HOME/dist-fluxy"
185
+ cp -r "$EXTRACTED/dist-fluxy" "$FLUXY_HOME/"
186
+ printf " ${BLUE}✔${RESET} Chat interface ready\n"
187
+ elif [ ! -f "$FLUXY_HOME/dist-fluxy/onboard.html" ]; then
188
+ printf " ${BLUE}↓${RESET} Building chat interface...\n"
170
189
  if (cd "$FLUXY_HOME" && "$NPM" run build:fluxy 2>/dev/null); then
171
- printf " ${GREEN}✔${RESET} Chat interface built\n"
190
+ printf " ${BLUE}✔${RESET} Chat interface built\n"
172
191
  else
173
192
  printf " ${YELLOW}!${RESET} Chat build skipped — will build on first start\n"
174
193
  fi
194
+ else
195
+ printf " ${BLUE}✔${RESET} Chat interface ready\n"
175
196
  fi
176
197
 
198
+ rm -rf "$TMPDIR"
199
+
200
+ # Install dependencies inside ~/.fluxy/
201
+ (cd "$FLUXY_HOME" && "$NPM" install --omit=dev 2>/dev/null)
202
+
177
203
  # Verify
178
204
  if [ ! -f "$FLUXY_HOME/bin/cli.js" ]; then
179
205
  printf " ${RED}✗${RESET} Installation failed\n"
@@ -182,7 +208,7 @@ install_fluxy() {
182
208
 
183
209
  VERSION=$("$NODE" -e "const p=JSON.parse(require('fs').readFileSync('$FLUXY_HOME/package.json','utf8'));console.log(p.version)" 2>/dev/null || echo "unknown")
184
210
 
185
- printf " ${GREEN}✔${RESET} Fluxy v${VERSION} installed\n"
211
+ printf " ${BLUE}✔${RESET} Fluxy v${VERSION} installed\n"
186
212
  }
187
213
 
188
214
  # ─── Create wrapper script ──────────────────────────────────────────────────
@@ -210,7 +236,7 @@ WRAPPER
210
236
  fi
211
237
 
212
238
  chmod +x "$BIN_DIR/fluxy"
213
- printf " ${GREEN}✔${RESET} Created ${DIM}~/.fluxy/bin/fluxy${RESET}\n"
239
+ printf " ${BLUE}✔${RESET} Created ${DIM}~/.fluxy/bin/fluxy${RESET}\n"
214
240
  }
215
241
 
216
242
  # ─── Add to PATH ────────────────────────────────────────────────────────────
@@ -244,7 +270,7 @@ setup_path() {
244
270
  if ! grep -q "fluxy/bin" "$HOME/.config/fish/config.fish" 2>/dev/null; then
245
271
  echo 'set -gx PATH "$HOME/.fluxy/bin" $PATH' >> "$HOME/.config/fish/config.fish"
246
272
  fi
247
- printf " ${GREEN}✔${RESET} Added to PATH ${DIM}(~/.config/fish/config.fish)${RESET}\n"
273
+ printf " ${BLUE}✔${RESET} Added to PATH ${DIM}(~/.config/fish/config.fish)${RESET}\n"
248
274
  return 0
249
275
  ;;
250
276
  *) PROFILE="$HOME/.profile" ;;
@@ -254,7 +280,7 @@ setup_path() {
254
280
  if ! grep -q "fluxy/bin" "$PROFILE" 2>/dev/null; then
255
281
  printf "\n# Fluxy\n%s\n" "$EXPORT_LINE" >> "$PROFILE"
256
282
  fi
257
- printf " ${GREEN}✔${RESET} Added to PATH ${DIM}(${PROFILE})${RESET}\n"
283
+ printf " ${BLUE}✔${RESET} Added to PATH ${DIM}(${PROFILE})${RESET}\n"
258
284
  fi
259
285
 
260
286
  export PATH="$BIN_DIR:$PATH"
@@ -270,10 +296,18 @@ install_fluxy
270
296
  create_wrapper
271
297
  setup_path
272
298
 
273
- printf "\n ${GREEN}${BOLD}✔ Fluxy is ready!${RESET}\n\n"
274
- printf " Get started:\n\n"
275
- printf " ${CYAN}fluxy init${RESET} Set up your bot\n"
276
- printf " ${CYAN}fluxy start${RESET} Start your bot\n"
277
- printf " ${CYAN}fluxy status${RESET} Check if it's running\n\n"
278
- printf " ${DIM}Run ${RESET}${CYAN}fluxy init${RESET}${DIM} to begin.${RESET}\n"
279
- printf " ${DIM}(Open a new terminal if 'fluxy' isn't found yet)${RESET}\n\n"
299
+ printf "\n"
300
+ printf " ${PINK}${BOLD}✔ Fluxy is ready!${RESET}\n"
301
+ printf "\n"
302
+ printf " ${DIM}─────────────────────────────${RESET}\n"
303
+ printf " ${BOLD}Get started:${RESET}\n"
304
+ printf "\n"
305
+ printf " ${BLUE}fluxy init${RESET} Set up your bot\n"
306
+ printf " ${BLUE}fluxy start${RESET} Start your bot\n"
307
+ printf " ${BLUE}fluxy status${RESET} Check if it's running\n"
308
+ printf "\n"
309
+ printf " ${PINK}>${RESET} Run ${BLUE}fluxy init${RESET} to begin.\n"
310
+ printf " ${DIM}(Open a new terminal if 'fluxy' isn't found yet)${RESET}\n"
311
+ printf "\n"
312
+ printf " ${DIM}\033]8;;https://fluxy.bot\033\\https://fluxy.bot\033]8;;\033\\${RESET}\n"
313
+ printf "\n"
@@ -28,23 +28,30 @@ if (fs.existsSync(path.join(PKG_ROOT, '.git'))) {
28
28
 
29
29
  fs.mkdirSync(FLUXY_HOME, { recursive: true });
30
30
 
31
- const DIRS_TO_COPY = [
32
- 'bin', 'supervisor', 'worker', 'shared', 'workspace', 'scripts',
33
- ];
31
+ // Code directories — always overwrite (these are application code)
32
+ const CODE_DIRS = ['bin', 'supervisor', 'worker', 'shared', 'scripts'];
34
33
 
35
- const FILES_TO_COPY = [
34
+ // Code files — always overwrite
35
+ const CODE_FILES = [
36
36
  'package.json', 'vite.config.ts', 'vite.fluxy.config.ts',
37
37
  'tsconfig.json', 'postcss.config.js', 'components.json',
38
38
  ];
39
39
 
40
- for (const dir of DIRS_TO_COPY) {
40
+ for (const dir of CODE_DIRS) {
41
41
  const src = path.join(PKG_ROOT, dir);
42
42
  if (!fs.existsSync(src)) continue;
43
43
  const dst = path.join(FLUXY_HOME, dir);
44
44
  fs.cpSync(src, dst, { recursive: true, force: true });
45
45
  }
46
46
 
47
- for (const file of FILES_TO_COPY) {
47
+ // Workspace template — only on first install (preserves user files, uploads, etc.)
48
+ const wsSrc = path.join(PKG_ROOT, 'workspace');
49
+ const wsDst = path.join(FLUXY_HOME, 'workspace');
50
+ if (fs.existsSync(wsSrc) && !fs.existsSync(wsDst)) {
51
+ fs.cpSync(wsSrc, wsDst, { recursive: true });
52
+ }
53
+
54
+ for (const file of CODE_FILES) {
48
55
  const src = path.join(PKG_ROOT, file);
49
56
  if (!fs.existsSync(src)) continue;
50
57
  fs.copyFileSync(src, path.join(FLUXY_HOME, file));
@@ -61,10 +68,15 @@ try {
61
68
  // Non-fatal: deps may already exist from a previous install
62
69
  }
63
70
 
64
- // ── Build fluxy chat + onboard (served as static files) ──
71
+ // ── Copy pre-built UI if available, otherwise build ──
65
72
 
66
- const distFluxy = path.join(FLUXY_HOME, 'dist-fluxy', 'onboard.html');
67
- if (!fs.existsSync(distFluxy)) {
73
+ const distSrc = path.join(PKG_ROOT, 'dist-fluxy');
74
+ const distDst = path.join(FLUXY_HOME, 'dist-fluxy');
75
+ if (fs.existsSync(distSrc)) {
76
+ // Always use the pre-built UI from the package (handles updates)
77
+ if (fs.existsSync(distDst)) fs.rmSync(distDst, { recursive: true });
78
+ fs.cpSync(distSrc, distDst, { recursive: true });
79
+ } else if (!fs.existsSync(path.join(distDst, 'onboard.html'))) {
68
80
  try {
69
81
  execSync('npm run build:fluxy', {
70
82
  cwd: FLUXY_HOME,