fluxy-bot 0.2.39 → 0.2.41

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": "fluxy-bot",
3
- "version": "0.2.39",
3
+ "version": "0.2.41",
4
4
  "description": "Self-hosted AI bot — run your own AI assistant from anywhere",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -106,7 +106,7 @@ function FluxyApp() {
106
106
  const cfgRes = await fetch('/api/auth/configured');
107
107
  const cfg = await cfgRes.json();
108
108
  if (!cfg.configured) {
109
- setAuthState('first-run'); // No credentials yet — allow chat for onboarding
109
+ setAuthState('first-run');
110
110
  } else {
111
111
  setAuthState('unauthenticated');
112
112
  }
@@ -1,10 +1,9 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
2
+ import { Routes, Route, Navigate } from 'react-router-dom';
3
3
  import ErrorBoundary from './components/ErrorBoundary';
4
4
  import DashboardLayout from './components/Layout/DashboardLayout';
5
5
  import DashboardPage from './components/Dashboard/DashboardPage';
6
6
  import LandingPage from './components/Landing/LandingPage';
7
- import LoginPage from './components/Auth/LoginPage';
8
7
 
9
8
  function DashboardError() {
10
9
  return (
@@ -31,7 +30,6 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
31
30
  useEffect(() => {
32
31
  (async () => {
33
32
  try {
34
- // Check if credentials are configured at all
35
33
  const cfgRes = await fetch('/api/auth/configured');
36
34
  const cfg = await cfgRes.json();
37
35
  if (!cfg.configured) {
@@ -40,12 +38,10 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
40
38
  return;
41
39
  }
42
40
 
43
- // Credentials exist — verify session
44
41
  const meRes = await fetch('/api/auth/me');
45
42
  const me = await meRes.json();
46
43
  setStatus(me.authenticated ? 'ok' : 'denied');
47
44
  } catch {
48
- // Worker not ready — allow (onboarding scenario)
49
45
  setStatus('ok');
50
46
  }
51
47
  })();
@@ -59,7 +55,8 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
59
55
  );
60
56
  }
61
57
 
62
- if (status === 'denied') return <Navigate to="/login" replace />;
58
+ // Not authenticated send to landing page (login via chat bubble)
59
+ if (status === 'denied') return <Navigate to="/" replace />;
63
60
 
64
61
  return <>{children}</>;
65
62
  }
@@ -151,7 +148,6 @@ export default function App() {
151
148
  return (
152
149
  <Routes>
153
150
  <Route path="/" element={<LandingPage />} />
154
- <Route path="/login" element={<LoginPage />} />
155
151
  <Route
156
152
  path="/dashboard"
157
153
  element={
@@ -5,19 +5,38 @@ export default function LandingPage() {
5
5
  const navigate = useNavigate();
6
6
  const [botName, setBotName] = useState('Fluxy');
7
7
  const [isAuthed, setIsAuthed] = useState(false);
8
- const [checking, setChecking] = useState(true);
8
+ const [showOnboard, setShowOnboard] = useState(false);
9
9
 
10
10
  useEffect(() => {
11
11
  Promise.all([
12
12
  fetch('/api/settings').then((r) => r.json()).catch(() => null),
13
13
  fetch('/api/auth/me').then((r) => r.json()).catch(() => null),
14
- ]).then(([settings, me]) => {
14
+ fetch('/api/auth/configured').then((r) => r.json()).catch(() => null),
15
+ ]).then(([settings, me, cfg]) => {
15
16
  if (settings?.agent_name) setBotName(settings.agent_name);
16
17
  if (me?.authenticated) setIsAuthed(true);
17
- setChecking(false);
18
+ // First run — show onboard overlay
19
+ if (cfg && !cfg.configured && !cfg.onboardComplete) setShowOnboard(true);
18
20
  });
19
21
  }, []);
20
22
 
23
+ // Hide/show bubble during onboarding
24
+ useEffect(() => {
25
+ const bubble = document.getElementById('fluxy-widget-bubble');
26
+ if (bubble) bubble.style.display = showOnboard ? 'none' : '';
27
+ }, [showOnboard]);
28
+
29
+ // Listen for onboard complete from fluxy iframe
30
+ useEffect(() => {
31
+ const handler = (e: MessageEvent) => {
32
+ if (e.data?.type === 'fluxy:onboard-complete') {
33
+ setShowOnboard(false);
34
+ }
35
+ };
36
+ window.addEventListener('message', handler);
37
+ return () => window.removeEventListener('message', handler);
38
+ }, []);
39
+
21
40
  return (
22
41
  <div className="min-h-dvh flex flex-col bg-background">
23
42
  {/* Header */}
@@ -26,12 +45,12 @@ export default function LandingPage() {
26
45
  <img src="/fluxy.png" alt={botName} className="h-6 w-auto" />
27
46
  <span className="text-sm font-semibold">{botName}</span>
28
47
  </div>
29
- {!checking && (
48
+ {isAuthed && (
30
49
  <button
31
- onClick={() => navigate(isAuthed ? '/dashboard' : '/login')}
50
+ onClick={() => navigate('/dashboard')}
32
51
  className="text-sm font-medium px-4 py-2 rounded-full border border-white/[0.08] hover:bg-white/[0.06] transition-colors"
33
52
  >
34
- {isAuthed ? 'Dashboard' : 'Sign In'}
53
+ Dashboard
35
54
  </button>
36
55
  )}
37
56
  </header>
@@ -58,6 +77,14 @@ export default function LandingPage() {
58
77
  <footer className="py-4 text-center">
59
78
  <p className="text-xs text-muted-foreground/60">Powered by Fluxy</p>
60
79
  </footer>
80
+
81
+ {/* Onboard overlay (first run) — served from dist-fluxy (supervisor territory) */}
82
+ {showOnboard && (
83
+ <iframe
84
+ src="/fluxy/onboard.html"
85
+ style={{ position: 'fixed', inset: 0, width: '100vw', height: '100dvh', border: 'none', zIndex: 200 }}
86
+ />
87
+ )}
61
88
  </div>
62
89
  );
63
90
  }
@@ -1,83 +0,0 @@
1
- import { useState } from 'react';
2
- import { useNavigate, Link } from 'react-router-dom';
3
-
4
- export default function LoginPage() {
5
- const navigate = useNavigate();
6
- const [username, setUsername] = useState('');
7
- const [password, setPassword] = useState('');
8
- const [error, setError] = useState('');
9
- const [loading, setLoading] = useState(false);
10
-
11
- const handleSubmit = async (e: React.FormEvent) => {
12
- e.preventDefault();
13
- setError('');
14
- setLoading(true);
15
- try {
16
- const res = await fetch('/api/auth/login', {
17
- method: 'POST',
18
- headers: { 'Content-Type': 'application/json' },
19
- body: JSON.stringify({ username, password }),
20
- });
21
- const data = await res.json();
22
- if (data.ok) {
23
- navigate('/dashboard');
24
- } else {
25
- setError(data.error || 'Login failed');
26
- }
27
- } catch {
28
- setError('Connection error');
29
- } finally {
30
- setLoading(false);
31
- }
32
- };
33
-
34
- return (
35
- <div className="min-h-dvh flex flex-col items-center justify-center bg-background px-6">
36
- <div className="w-full max-w-sm">
37
- <div className="flex flex-col items-center mb-8">
38
- <img src="/fluxy.png" alt="Fluxy" className="h-12 w-auto mb-4" />
39
- <h1 className="text-xl font-semibold">Welcome Back</h1>
40
- <p className="text-sm text-muted-foreground mt-1">Sign in to access the dashboard</p>
41
- </div>
42
-
43
- <form onSubmit={handleSubmit} className="flex flex-col gap-3">
44
- <input
45
- type="text"
46
- placeholder="Username"
47
- value={username}
48
- onChange={(e) => setUsername(e.target.value)}
49
- autoComplete="username"
50
- autoFocus
51
- className="w-full bg-white/[0.05] border border-white/[0.08] rounded-xl px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground outline-none input-glow"
52
- />
53
- <input
54
- type="password"
55
- placeholder="Password"
56
- value={password}
57
- onChange={(e) => setPassword(e.target.value)}
58
- autoComplete="current-password"
59
- className="w-full bg-white/[0.05] border border-white/[0.08] rounded-xl px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground outline-none input-glow"
60
- />
61
- {error && (
62
- <div className="bg-red-500/8 border border-red-500/15 rounded-lg px-3 py-2 text-xs text-red-400">
63
- {error}
64
- </div>
65
- )}
66
- <button
67
- type="submit"
68
- disabled={loading || !username || !password}
69
- className="w-full bg-gradient-brand text-white font-medium rounded-full py-3 text-sm hover:opacity-90 transition-opacity disabled:opacity-50"
70
- >
71
- {loading ? 'Signing in...' : 'Sign In'}
72
- </button>
73
- </form>
74
-
75
- <div className="mt-6 text-center">
76
- <Link to="/" className="text-sm text-muted-foreground hover:text-foreground transition-colors">
77
- Back to home
78
- </Link>
79
- </div>
80
- </div>
81
- </div>
82
- );
83
- }