fluxy-bot 0.9.1 → 0.9.3

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
@@ -1232,8 +1232,8 @@ async function update() {
1232
1232
  const daemonWasRunning = isDaemonInstalled() && isDaemonActive();
1233
1233
 
1234
1234
  const steps = [
1235
- ...(daemonWasRunning ? ['Stopping daemon'] : []),
1236
1235
  'Downloading update',
1236
+ ...(daemonWasRunning ? ['Stopping daemon'] : []),
1237
1237
  'Updating files',
1238
1238
  'Installing dependencies',
1239
1239
  'Building interface',
@@ -1243,20 +1243,7 @@ async function update() {
1243
1243
  const stepper = new Stepper(steps);
1244
1244
  stepper.start();
1245
1245
 
1246
- // Stop daemon before updating files
1247
- if (daemonWasRunning) {
1248
- try {
1249
- if (PLATFORM === 'darwin') {
1250
- execSync(`launchctl unload "${LAUNCHD_PLIST_PATH}"`, { stdio: 'ignore' });
1251
- } else {
1252
- const cmd = needsSudo() ? `sudo systemctl stop ${SERVICE_NAME}` : `systemctl stop ${SERVICE_NAME}`;
1253
- execSync(cmd, { stdio: 'ignore' });
1254
- }
1255
- } catch {}
1256
- stepper.advance();
1257
- }
1258
-
1259
- // Download tarball
1246
+ // Download tarball FIRST — before stopping daemon, so failure doesn't leave bot offline
1260
1247
  const tarballUrl = latest.dist.tarball;
1261
1248
  const tmpDir = path.join(os.tmpdir(), `fluxy-update-${Date.now()}`);
1262
1249
  fs.mkdirSync(tmpDir, { recursive: true });
@@ -1264,65 +1251,90 @@ async function update() {
1264
1251
 
1265
1252
  try {
1266
1253
  const res = await fetch(tarballUrl);
1267
- if (!res.ok) throw new Error();
1254
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1268
1255
  const buf = Buffer.from(await res.arrayBuffer());
1269
1256
  fs.writeFileSync(tarball, buf);
1270
1257
  execSync(`tar xzf "${tarball}" -C "${tmpDir}"`, { stdio: 'ignore' });
1271
- } catch {
1258
+ } catch (e) {
1272
1259
  stepper.finish();
1273
- console.log(`\n ${c.red}✗${c.reset} Download failed\n`);
1260
+ console.log(`\n ${c.red}✗${c.reset} Download failed: ${e.message}\n`);
1274
1261
  fs.rmSync(tmpDir, { recursive: true, force: true });
1275
1262
  process.exit(1);
1276
1263
  }
1277
1264
  stepper.advance();
1278
1265
 
1266
+ // Stop daemon AFTER download succeeds — minimizes downtime
1267
+ if (daemonWasRunning) {
1268
+ try {
1269
+ if (PLATFORM === 'darwin') {
1270
+ execSync(`launchctl unload "${LAUNCHD_PLIST_PATH}"`, { stdio: 'ignore' });
1271
+ } else {
1272
+ const cmd = needsSudo() ? `sudo systemctl stop ${SERVICE_NAME}` : `systemctl stop ${SERVICE_NAME}`;
1273
+ execSync(cmd, { stdio: 'ignore' });
1274
+ }
1275
+ } catch (e) {
1276
+ console.log(` ${c.yellow}⚠${c.reset} Could not stop daemon: ${e.message}`);
1277
+ }
1278
+ stepper.advance();
1279
+ }
1280
+
1279
1281
  const extracted = path.join(tmpDir, 'package');
1280
1282
 
1281
1283
  // Update code directories (preserve workspace/ user data)
1282
- for (const dir of ['bin', 'supervisor', 'worker', 'shared', 'scripts']) {
1283
- const src = path.join(extracted, dir);
1284
- if (fs.existsSync(src)) {
1285
- fs.cpSync(src, path.join(DATA_DIR, dir), { recursive: true, force: true });
1284
+ try {
1285
+ for (const dir of ['bin', 'supervisor', 'worker', 'shared', 'scripts']) {
1286
+ const src = path.join(extracted, dir);
1287
+ if (fs.existsSync(src)) {
1288
+ fs.cpSync(src, path.join(DATA_DIR, dir), { recursive: true, force: true });
1289
+ }
1286
1290
  }
1287
- }
1288
1291
 
1289
- // Copy workspace template only if it doesn't exist yet
1290
- if (!fs.existsSync(path.join(DATA_DIR, 'workspace'))) {
1291
- const wsSrc = path.join(extracted, 'workspace');
1292
- if (fs.existsSync(wsSrc)) {
1293
- fs.cpSync(wsSrc, path.join(DATA_DIR, 'workspace'), { recursive: true });
1292
+ // Copy workspace template only if it doesn't exist yet
1293
+ if (!fs.existsSync(path.join(DATA_DIR, 'workspace'))) {
1294
+ const wsSrc = path.join(extracted, 'workspace');
1295
+ if (fs.existsSync(wsSrc)) {
1296
+ fs.cpSync(wsSrc, path.join(DATA_DIR, 'workspace'), { recursive: true });
1297
+ }
1294
1298
  }
1295
- }
1296
1299
 
1297
- // Update code files (never touches config.json, memory.db, etc.)
1298
- for (const file of ['package.json', 'vite.config.ts', 'vite.fluxy.config.ts', 'tsconfig.json', 'postcss.config.js', 'components.json']) {
1299
- const src = path.join(extracted, file);
1300
- if (fs.existsSync(src)) {
1301
- fs.copyFileSync(src, path.join(DATA_DIR, file));
1300
+ // Update code files (never touches config.json, memory.db, etc.)
1301
+ for (const file of ['package.json', 'vite.config.ts', 'vite.fluxy.config.ts', 'tsconfig.json', 'postcss.config.js', 'components.json']) {
1302
+ const src = path.join(extracted, file);
1303
+ if (fs.existsSync(src)) {
1304
+ fs.copyFileSync(src, path.join(DATA_DIR, file));
1305
+ }
1302
1306
  }
1303
- }
1304
1307
 
1305
- // Update pre-built UI from tarball
1306
- const distSrc = path.join(extracted, 'dist-fluxy');
1307
- const distDst = path.join(DATA_DIR, 'dist-fluxy');
1308
- if (fs.existsSync(distSrc)) {
1309
- if (fs.existsSync(distDst)) fs.rmSync(distDst, { recursive: true });
1310
- fs.cpSync(distSrc, distDst, { recursive: true });
1308
+ // Update pre-built UI from tarball
1309
+ const distSrc = path.join(extracted, 'dist-fluxy');
1310
+ if (fs.existsSync(distSrc)) {
1311
+ const dst = path.join(DATA_DIR, 'dist-fluxy');
1312
+ if (fs.existsSync(dst)) fs.rmSync(dst, { recursive: true });
1313
+ fs.cpSync(distSrc, dst, { recursive: true });
1314
+ }
1315
+ } catch (e) {
1316
+ console.log(` ${c.red}✗${c.reset} File copy failed: ${e.message}`);
1317
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1318
+ process.exit(1);
1311
1319
  }
1312
1320
 
1313
1321
  stepper.advance();
1314
1322
 
1315
- // Install dependencies
1323
+ const distDst = path.join(DATA_DIR, 'dist-fluxy');
1324
+
1325
+ // Install dependencies (5 min timeout to prevent hanging forever)
1316
1326
  try {
1317
- execSync('npm install --omit=dev', { cwd: DATA_DIR, stdio: 'ignore' });
1318
- } catch {}
1327
+ execSync('npm install --omit=dev', { cwd: DATA_DIR, stdio: 'ignore', timeout: 300_000 });
1328
+ } catch (e) {
1329
+ console.log(` ${c.yellow}⚠${c.reset} npm install issue: ${e.message}`);
1330
+ }
1319
1331
  stepper.advance();
1320
1332
 
1321
1333
  // Rebuild UI if not in tarball
1322
1334
  if (!fs.existsSync(path.join(distDst, 'onboard.html'))) {
1323
1335
  try {
1324
1336
  if (fs.existsSync(distDst)) fs.rmSync(distDst, { recursive: true });
1325
- execSync('npm run build:fluxy', { cwd: DATA_DIR, stdio: 'ignore' });
1337
+ execSync('npm run build:fluxy', { cwd: DATA_DIR, stdio: 'ignore', timeout: 300_000 });
1326
1338
  } catch {}
1327
1339
  }
1328
1340
  stepper.advance();
@@ -42,18 +42,7 @@ export function registerUpdateCommand(program: Command) {
42
42
  const adapter = getAdapter();
43
43
  const daemonWasRunning = fs.existsSync(DATA_DIR) && adapter.isInstalled;
44
44
 
45
- if (daemonWasRunning && adapter.isActive) {
46
- s.message('Stopping daemon...');
47
- try {
48
- adapter.handleDaemonAction('stop', {
49
- user: os.userInfo().username,
50
- home: os.homedir(),
51
- nodePath: process.execPath,
52
- dataDir: DATA_DIR,
53
- });
54
- } catch {}
55
- }
56
-
45
+ // Download tarball FIRST — before stopping daemon, so failure doesn't leave bot offline
57
46
  const tmpDir = path.join(os.tmpdir(), `fluxy-update-${Date.now()}`);
58
47
  fs.mkdirSync(tmpDir, { recursive: true });
59
48
  const tarballFilePath = path.join(tmpDir, 'fluxy.tgz');
@@ -70,48 +59,68 @@ export function registerUpdateCommand(program: Command) {
70
59
  process.exit(1);
71
60
  }
72
61
 
62
+ // Stop daemon AFTER download succeeds — minimizes downtime
63
+ if (daemonWasRunning && adapter.isActive) {
64
+ s.message('Stopping daemon...');
65
+ try {
66
+ adapter.handleDaemonAction('stop', {
67
+ user: os.userInfo().username,
68
+ home: os.homedir(),
69
+ nodePath: process.execPath,
70
+ dataDir: DATA_DIR,
71
+ });
72
+ } catch {}
73
+ }
74
+
73
75
  s.message('Updating files...');
74
76
  const extracted = path.join(tmpDir, 'package');
75
77
 
76
- for (const dir of ['bin', 'supervisor', 'worker', 'shared', 'scripts']) {
77
- const src = path.join(extracted, dir);
78
- if (fs.existsSync(src)) {
79
- fs.cpSync(src, path.join(DATA_DIR, dir), { recursive: true, force: true });
78
+ try {
79
+ for (const dir of ['bin', 'supervisor', 'worker', 'shared', 'scripts']) {
80
+ const src = path.join(extracted, dir);
81
+ if (fs.existsSync(src)) {
82
+ fs.cpSync(src, path.join(DATA_DIR, dir), { recursive: true, force: true });
83
+ }
80
84
  }
81
- }
82
85
 
83
- const wsSrc = path.join(extracted, 'workspace');
84
- if (!fs.existsSync(path.join(DATA_DIR, 'workspace')) && fs.existsSync(wsSrc)) {
85
- fs.cpSync(wsSrc, path.join(DATA_DIR, 'workspace'), { recursive: true });
86
- }
86
+ const wsSrc = path.join(extracted, 'workspace');
87
+ if (!fs.existsSync(path.join(DATA_DIR, 'workspace')) && fs.existsSync(wsSrc)) {
88
+ fs.cpSync(wsSrc, path.join(DATA_DIR, 'workspace'), { recursive: true });
89
+ }
87
90
 
88
- for (const file of ['package.json', 'vite.config.ts', 'vite.fluxy.config.ts', 'tsconfig.json', 'postcss.config.js', 'components.json']) {
89
- const src = path.join(extracted, file);
90
- if (fs.existsSync(src)) {
91
- fs.cpSync(src, path.join(DATA_DIR, file), { force: true });
91
+ for (const file of ['package.json', 'vite.config.ts', 'vite.fluxy.config.ts', 'tsconfig.json', 'postcss.config.js', 'components.json']) {
92
+ const src = path.join(extracted, file);
93
+ if (fs.existsSync(src)) {
94
+ fs.cpSync(src, path.join(DATA_DIR, file), { force: true });
95
+ }
92
96
  }
93
- }
94
97
 
95
- const distSrc = path.join(extracted, 'dist-fluxy');
96
- const distDst = path.join(DATA_DIR, 'dist-fluxy');
97
- if (fs.existsSync(distSrc)) {
98
- fs.rmSync(distDst, { recursive: true, force: true });
99
- fs.cpSync(distSrc, distDst, { recursive: true });
98
+ const distSrc = path.join(extracted, 'dist-fluxy');
99
+ const distDst = path.join(DATA_DIR, 'dist-fluxy');
100
+ if (fs.existsSync(distSrc)) {
101
+ fs.rmSync(distDst, { recursive: true, force: true });
102
+ fs.cpSync(distSrc, distDst, { recursive: true });
103
+ }
104
+ } catch (e: any) {
105
+ s.stop(pc.red('File copy failed: ' + e.message));
106
+ fs.rmSync(tmpDir, { recursive: true, force: true });
107
+ process.exit(1);
100
108
  }
101
109
 
102
110
  s.message('Installing dependencies...');
103
111
  try {
104
- execSync('npm install --omit=dev', { cwd: DATA_DIR, stdio: 'ignore' });
112
+ execSync('npm install --omit=dev', { cwd: DATA_DIR, stdio: 'ignore', timeout: 300_000 });
105
113
  } catch {}
106
114
 
115
+ const distDst = path.join(DATA_DIR, 'dist-fluxy');
107
116
  if (!fs.existsSync(path.join(distDst, 'onboard.html'))) {
108
117
  s.message('Building interface...');
109
118
  try {
110
- execSync('npm run build:fluxy', { cwd: DATA_DIR, stdio: 'ignore' });
119
+ execSync('npm run build:fluxy', { cwd: DATA_DIR, stdio: 'ignore', timeout: 300_000 });
111
120
  } catch {}
112
121
  }
113
122
 
114
- fs.writeFileSync(path.join(DATA_DIR, '.version'), latest.version);
123
+ fs.writeFileSync(path.join(DATA_DIR, 'VERSION'), latest.version);
115
124
  fs.rmSync(tmpDir, { recursive: true, force: true });
116
125
 
117
126
  if (daemonWasRunning) {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "fluxy-bot",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "releaseNotes": [
5
- "Fixed some bugs to iOs ",
5
+ "Fixing auto update",
6
6
  "2. ",
7
7
  "3. ",
8
8
  "4. "
@@ -6,7 +6,7 @@ import { WebSocketServer, WebSocket } from 'ws';
6
6
  import { loadConfig, saveConfig } from '../shared/config.js';
7
7
  import { createProvider, type AiProvider, type ChatMessage } from '../shared/ai.js';
8
8
  import { paths } from '../shared/paths.js';
9
- import { PKG_DIR, WORKSPACE_DIR } from '../shared/paths.js';
9
+ import { PKG_DIR, DATA_DIR, WORKSPACE_DIR } from '../shared/paths.js';
10
10
  import { log } from '../shared/logger.js';
11
11
  import { startTunnel, stopTunnel, isTunnelAlive, restartTunnel, startNamedTunnel, restartNamedTunnel } from './tunnel.js';
12
12
  import { spawnWorker, stopWorker, getWorkerPort, isWorkerAlive } from './worker.js';
@@ -782,13 +782,23 @@ export async function startSupervisor() {
782
782
  // Run fluxy update in a detached process so it survives daemon stop/restart
783
783
  function runDeferredUpdate() {
784
784
  const cliPath = path.join(PKG_DIR, 'bin', 'cli.js');
785
+ const updateLog = path.join(DATA_DIR, 'update.log');
785
786
  log.info('Deferred update triggered — running fluxy update...');
786
787
  try {
788
+ const logFd = fs.openSync(updateLog, 'w');
787
789
  const child = cpSpawn(process.execPath, [cliPath, 'update'], {
788
790
  detached: true,
789
- stdio: 'ignore',
791
+ stdio: ['ignore', logFd, logFd],
790
792
  env: { ...process.env },
791
793
  });
794
+ child.on('exit', (code) => {
795
+ try { fs.closeSync(logFd); } catch {}
796
+ if (code === 0) {
797
+ log.ok('Update process completed successfully');
798
+ } else {
799
+ log.error(`Update process exited with code ${code} — see ${updateLog}`);
800
+ }
801
+ });
792
802
  child.unref();
793
803
  } catch (err) {
794
804
  log.error(`Deferred update failed: ${err instanceof Error ? err.message : err}`);