fluxy-bot 0.8.9 → 0.9.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/README.md +53 -84
- package/bin/cli.js +1717 -6
- package/components.json +18 -18
- package/package.json +98 -109
- package/postcss.config.js +3 -3
- package/scripts/install.ps1 +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/postinstall.js +47 -58
- package/shared/ai.ts +118 -202
- package/shared/config.ts +37 -39
- package/shared/logger.ts +6 -14
- package/shared/paths.ts +10 -16
- package/shared/relay.ts +89 -98
- package/supervisor/backend.ts +77 -89
- package/supervisor/chat/ARCHITECTURE.md +13 -14
- package/supervisor/chat/OnboardWizard.tsx +2194 -3419
- package/supervisor/chat/fluxy-main.tsx +495 -633
- package/supervisor/chat/fluxy.html +24 -36
- package/supervisor/chat/onboard-main.tsx +8 -8
- package/supervisor/chat/onboard.html +10 -13
- package/supervisor/chat/src/components/Chat/AudioBubble.tsx +94 -108
- package/supervisor/chat/src/components/Chat/ChatView.tsx +14 -36
- package/supervisor/chat/src/components/Chat/ImageLightbox.tsx +71 -89
- package/supervisor/chat/src/components/Chat/InputBar.tsx +498 -728
- package/supervisor/chat/src/components/Chat/MessageBubble.tsx +154 -246
- package/supervisor/chat/src/components/Chat/MessageList.tsx +114 -138
- package/supervisor/chat/src/components/Chat/TypingIndicator.tsx +38 -50
- package/supervisor/chat/src/components/LoginScreen.tsx +245 -296
- package/supervisor/chat/src/hooks/useChat.ts +210 -259
- package/supervisor/chat/src/hooks/useFluxyChat.ts +232 -322
- package/supervisor/chat/src/hooks/useSpeechRecognition.ts +126 -148
- package/supervisor/chat/src/lib/auth.ts +20 -23
- package/supervisor/chat/src/lib/ws-client.ts +113 -125
- package/supervisor/chat/src/styles/globals.css +76 -85
- package/supervisor/file-saver.ts +39 -52
- package/supervisor/fluxy-agent.ts +213 -292
- package/supervisor/index.ts +895 -1263
- package/supervisor/scheduler.ts +237 -299
- package/supervisor/tunnel.ts +113 -167
- package/supervisor/vite-dev.ts +51 -61
- package/supervisor/widget.js +135 -147
- package/supervisor/worker.ts +45 -49
- package/tsconfig.json +18 -24
- package/vite.config.ts +48 -55
- package/vite.fluxy.config.ts +34 -34
- package/worker/claude-auth.ts +202 -265
- package/worker/codex-auth.ts +144 -181
- package/worker/db.ts +63 -191
- package/worker/index.ts +601 -842
- package/workspace/MYSELF.md +1 -1
- package/workspace/PULSE.json +6 -6
- package/workspace/backend/index.ts +16 -19
- package/workspace/client/index.html +47 -60
- package/workspace/client/public/manifest.json +21 -21
- package/workspace/client/public/sw.js +29 -31
- package/workspace/client/src/App.tsx +91 -136
- package/workspace/client/src/components/Dashboard/DashboardPage.tsx +68 -72
- package/workspace/client/src/components/ErrorBoundary.tsx +11 -11
- package/workspace/client/src/components/Layout/DashboardLayout.tsx +35 -35
- package/workspace/client/src/components/Layout/Footer.tsx +12 -12
- package/workspace/client/src/components/Layout/MobileNav.tsx +23 -23
- package/workspace/client/src/components/Layout/Sidebar.tsx +81 -81
- package/workspace/client/src/components/ui/avatar.tsx +85 -85
- package/workspace/client/src/components/ui/badge.tsx +40 -41
- package/workspace/client/src/components/ui/button.tsx +56 -57
- package/workspace/client/src/components/ui/card.tsx +75 -75
- package/workspace/client/src/components/ui/dialog.tsx +114 -117
- package/workspace/client/src/components/ui/dropdown-menu.tsx +191 -199
- package/workspace/client/src/components/ui/input.tsx +17 -17
- package/workspace/client/src/components/ui/scroll-area.tsx +47 -47
- package/workspace/client/src/components/ui/select.tsx +145 -151
- package/workspace/client/src/components/ui/separator.tsx +21 -21
- package/workspace/client/src/components/ui/sheet.tsx +102 -102
- package/workspace/client/src/components/ui/skeleton.tsx +10 -10
- package/workspace/client/src/components/ui/switch.tsx +26 -26
- package/workspace/client/src/components/ui/tabs.tsx +69 -69
- package/workspace/client/src/components/ui/textarea.tsx +14 -14
- package/workspace/client/src/components/ui/tooltip.tsx +37 -37
- package/workspace/client/src/lib/utils.ts +1 -1
- package/workspace/client/src/main.tsx +3 -3
- package/workspace/client/src/styles/globals.css +76 -85
- package/workspace/skills/code-reviewer/.claude-plugin/plugin.json +3 -3
- package/workspace/skills/code-reviewer/skills/code-reviewer/SKILL.md +0 -6
- package/workspace/skills/daily-standup/.claude-plugin/plugin.json +3 -3
- package/workspace/skills/daily-standup/skills/daily-standup/SKILL.md +0 -7
- package/workspace/skills/workspace-helper/.claude-plugin/plugin.json +3 -3
- package/workspace/skills/workspace-helper/skills/workspace-helper/SKILL.md +0 -2
- package/bin/cli.backup.js +0 -2138
- package/cli/commands/daemon.ts +0 -42
- package/cli/commands/init.ts +0 -32
- package/cli/commands/start.ts +0 -113
- package/cli/commands/tunnel.temp.ts +0 -206
- package/cli/commands/tunnel.ts +0 -227
- package/cli/commands/update.ts +0 -163
- package/cli/core/base-adapter.ts +0 -156
- package/cli/core/cloudflared.ts +0 -113
- package/cli/core/config.ts +0 -73
- package/cli/core/os-detector.ts +0 -43
- package/cli/core/server.ts +0 -109
- package/cli/core/types.ts +0 -15
- package/cli/index.ts +0 -72
- package/cli/platforms/darwin.ts +0 -146
- package/cli/platforms/index.ts +0 -21
- package/cli/platforms/linux.ts +0 -156
- package/cli/platforms/win32.ts +0 -34
- package/cli/utils/ui.ts +0 -37
package/supervisor/tunnel.ts
CHANGED
|
@@ -10,201 +10,147 @@ let proc: ChildProcess | null = null;
|
|
|
10
10
|
const MIN_CF_SIZE = 10 * 1024 * 1024; // 10 MB — valid cloudflared is ~30-50 MB
|
|
11
11
|
|
|
12
12
|
function findBinary(): string | null {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const size = fs.statSync(paths.cloudflared).size;
|
|
26
|
-
if (size < MIN_CF_SIZE) {
|
|
27
|
-
fs.unlinkSync(paths.cloudflared);
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
return paths.cloudflared;
|
|
13
|
+
// Check system-wide install
|
|
14
|
+
const which = process.platform === 'win32' ? 'where cloudflared' : 'which cloudflared';
|
|
15
|
+
try { execSync(which, { stdio: 'ignore' }); return 'cloudflared'; } catch {}
|
|
16
|
+
|
|
17
|
+
// Check local install (validate by file size, never execute — avoids Windows popup)
|
|
18
|
+
if (!fs.existsSync(paths.cloudflared)) return null;
|
|
19
|
+
const size = fs.statSync(paths.cloudflared).size;
|
|
20
|
+
if (size < MIN_CF_SIZE) {
|
|
21
|
+
fs.unlinkSync(paths.cloudflared);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return paths.cloudflared;
|
|
31
25
|
}
|
|
32
26
|
|
|
33
27
|
export async function installCloudflared(): Promise<string> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
execSync(`curl -fsSL "${url}" | tar xz -C "${dir}"`, {
|
|
65
|
-
stdio: 'ignore'
|
|
66
|
-
});
|
|
67
|
-
} else {
|
|
68
|
-
execSync(`curl -fsSL -o "${paths.cloudflared}" "${url}"`, {
|
|
69
|
-
stdio: 'ignore'
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
if (p !== 'win32') fs.chmodSync(paths.cloudflared, 0o755);
|
|
73
|
-
log.ok('cloudflared installed');
|
|
74
|
-
return paths.cloudflared;
|
|
28
|
+
const existing = findBinary();
|
|
29
|
+
if (existing) return existing;
|
|
30
|
+
|
|
31
|
+
const dir = path.dirname(paths.cloudflared);
|
|
32
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
33
|
+
|
|
34
|
+
const p = os.platform();
|
|
35
|
+
const a = p === 'win32'
|
|
36
|
+
? (process.env.PROCESSOR_ARCHITECTURE || os.arch()).toLowerCase()
|
|
37
|
+
: os.arch();
|
|
38
|
+
const base = 'https://github.com/cloudflare/cloudflared/releases/latest/download';
|
|
39
|
+
|
|
40
|
+
let url: string;
|
|
41
|
+
if (p === 'win32') url = `${base}/cloudflared-windows-${a.includes('arm') ? 'arm64' : 'amd64'}.exe`;
|
|
42
|
+
else if (p === 'darwin') url = `${base}/cloudflared-darwin-${a === 'arm64' ? 'arm64' : 'amd64'}.tgz`;
|
|
43
|
+
else if (a === 'arm64' || a === 'aarch64') url = `${base}/cloudflared-linux-arm64`;
|
|
44
|
+
else if (a.startsWith('arm')) url = `${base}/cloudflared-linux-arm`;
|
|
45
|
+
else url = `${base}/cloudflared-linux-amd64`;
|
|
46
|
+
|
|
47
|
+
log.info('Installing cloudflared...');
|
|
48
|
+
if (p === 'win32') {
|
|
49
|
+
execSync(`curl.exe -fsSL -o "${paths.cloudflared}" "${url}"`, { stdio: 'ignore' });
|
|
50
|
+
} else if (url.endsWith('.tgz')) {
|
|
51
|
+
execSync(`curl -fsSL "${url}" | tar xz -C "${dir}"`, { stdio: 'ignore' });
|
|
52
|
+
} else {
|
|
53
|
+
execSync(`curl -fsSL -o "${paths.cloudflared}" "${url}"`, { stdio: 'ignore' });
|
|
54
|
+
}
|
|
55
|
+
if (p !== 'win32') fs.chmodSync(paths.cloudflared, 0o755);
|
|
56
|
+
log.ok('cloudflared installed');
|
|
57
|
+
return paths.cloudflared;
|
|
75
58
|
}
|
|
76
59
|
|
|
77
60
|
export function startTunnel(port: number): Promise<string> {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
proc = spawn(
|
|
86
|
-
bin,
|
|
87
|
-
['tunnel', '--url', `http://localhost:${port}`, '--no-autoupdate'],
|
|
88
|
-
{
|
|
89
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
90
|
-
windowsHide: true
|
|
91
|
-
}
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
let buf = '';
|
|
95
|
-
const onData = (d: Buffer) => {
|
|
96
|
-
buf += d.toString();
|
|
97
|
-
const m = buf.match(/https:\/\/[^\s]+\.trycloudflare\.com/);
|
|
98
|
-
if (m) {
|
|
99
|
-
clearTimeout(timeout);
|
|
100
|
-
resolve(m[0]);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
proc.stdout?.on('data', onData);
|
|
105
|
-
proc.stderr?.on('data', onData);
|
|
106
|
-
proc.on('error', e => {
|
|
107
|
-
clearTimeout(timeout);
|
|
108
|
-
reject(e);
|
|
109
|
-
});
|
|
110
|
-
proc.on('exit', code => {
|
|
111
|
-
if (code !== 0) {
|
|
112
|
-
clearTimeout(timeout);
|
|
113
|
-
reject(new Error(`cloudflared exited with code ${code}`));
|
|
114
|
-
}
|
|
115
|
-
});
|
|
61
|
+
return new Promise(async (resolve, reject) => {
|
|
62
|
+
const bin = await installCloudflared();
|
|
63
|
+
const timeout = setTimeout(() => reject(new Error('Tunnel timeout')), 30_000);
|
|
64
|
+
|
|
65
|
+
proc = spawn(bin, ['tunnel', '--url', `http://localhost:${port}`, '--no-autoupdate'], {
|
|
66
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
67
|
+
windowsHide: true,
|
|
116
68
|
});
|
|
69
|
+
|
|
70
|
+
let buf = '';
|
|
71
|
+
const onData = (d: Buffer) => {
|
|
72
|
+
buf += d.toString();
|
|
73
|
+
const m = buf.match(/https:\/\/[^\s]+\.trycloudflare\.com/);
|
|
74
|
+
if (m) { clearTimeout(timeout); resolve(m[0]); }
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
proc.stdout?.on('data', onData);
|
|
78
|
+
proc.stderr?.on('data', onData);
|
|
79
|
+
proc.on('error', (e) => { clearTimeout(timeout); reject(e); });
|
|
80
|
+
proc.on('exit', (code) => { if (code !== 0) { clearTimeout(timeout); reject(new Error(`cloudflared exited with code ${code}`)); } });
|
|
81
|
+
});
|
|
117
82
|
}
|
|
118
83
|
|
|
119
|
-
export async function startNamedTunnel(
|
|
120
|
-
|
|
121
|
-
name: string
|
|
122
|
-
): Promise<void> {
|
|
123
|
-
const bin = await installCloudflared();
|
|
84
|
+
export async function startNamedTunnel(configPath: string, name: string): Promise<void> {
|
|
85
|
+
const bin = await installCloudflared();
|
|
124
86
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
87
|
+
proc = spawn(bin, ['tunnel', '--config', configPath, 'run', name], {
|
|
88
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
89
|
+
windowsHide: true,
|
|
90
|
+
});
|
|
129
91
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
log.info(`[named-tunnel] ${d.toString().trim()}`)
|
|
135
|
-
);
|
|
136
|
-
proc.on('error', e => log.error(`Named tunnel error: ${e.message}`));
|
|
137
|
-
proc.on('exit', code => {
|
|
138
|
-
if (code !== 0) log.warn(`Named tunnel exited with code ${code}`);
|
|
139
|
-
});
|
|
92
|
+
proc.stdout?.on('data', (d: Buffer) => log.info(`[named-tunnel] ${d.toString().trim()}`));
|
|
93
|
+
proc.stderr?.on('data', (d: Buffer) => log.info(`[named-tunnel] ${d.toString().trim()}`));
|
|
94
|
+
proc.on('error', (e) => log.error(`Named tunnel error: ${e.message}`));
|
|
95
|
+
proc.on('exit', (code) => { if (code !== 0) log.warn(`Named tunnel exited with code ${code}`); });
|
|
140
96
|
}
|
|
141
97
|
|
|
142
98
|
export async function startTunnelForMode(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
99
|
+
mode: 'off' | 'quick' | 'named',
|
|
100
|
+
port: number,
|
|
101
|
+
configPath?: string,
|
|
102
|
+
name?: string,
|
|
147
103
|
): Promise<string | null> {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return startTunnel(port);
|
|
104
|
+
if (mode === 'off') return null;
|
|
105
|
+
if (mode === 'named') {
|
|
106
|
+
if (!configPath || !name) throw new Error('Named tunnel requires configPath and name');
|
|
107
|
+
await startNamedTunnel(configPath, name);
|
|
108
|
+
return null; // URL is known from config.tunnel.domain
|
|
109
|
+
}
|
|
110
|
+
// mode === 'quick'
|
|
111
|
+
return startTunnel(port);
|
|
157
112
|
}
|
|
158
113
|
|
|
159
114
|
export function stopTunnel(): void {
|
|
160
|
-
|
|
161
|
-
|
|
115
|
+
proc?.kill();
|
|
116
|
+
proc = null;
|
|
162
117
|
}
|
|
163
118
|
|
|
164
119
|
/** Check if the cloudflared child process is still running */
|
|
165
120
|
export function isTunnelProcessAlive(): boolean {
|
|
166
|
-
|
|
121
|
+
return proc !== null && proc.exitCode === null && proc.signalCode === null;
|
|
167
122
|
}
|
|
168
123
|
|
|
169
|
-
export async function isTunnelAlive(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
);
|
|
188
|
-
if (res.ok) return true;
|
|
189
|
-
} catch {}
|
|
190
|
-
// Local server not responding — but cloudflared is alive, so tunnel is still up.
|
|
191
|
-
// The worker may be restarting. Don't kill the tunnel for a local issue.
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// No local port provided — process alive is enough
|
|
124
|
+
export async function isTunnelAlive(url: string, localPort?: number): Promise<boolean> {
|
|
125
|
+
// 1. Process check — if cloudflared exited, tunnel is definitely dead
|
|
126
|
+
if (!isTunnelProcessAlive()) return false;
|
|
127
|
+
|
|
128
|
+
// 2. Local reachability check — confirm the local server is still responding.
|
|
129
|
+
// We probe localhost directly instead of the tunnel URL because on macOS
|
|
130
|
+
// the server can't reach itself through the Cloudflare tunnel (DNS/firewall).
|
|
131
|
+
// If localhost responds and cloudflared is running, the tunnel is alive.
|
|
132
|
+
if (localPort) {
|
|
133
|
+
try {
|
|
134
|
+
const res = await fetch(`http://127.0.0.1:${localPort}/api/health?_cb=${Date.now()}`, {
|
|
135
|
+
signal: AbortSignal.timeout(3000),
|
|
136
|
+
});
|
|
137
|
+
if (res.ok) return true;
|
|
138
|
+
} catch {}
|
|
139
|
+
// Local server not responding — but cloudflared is alive, so tunnel is still up.
|
|
140
|
+
// The worker may be restarting. Don't kill the tunnel for a local issue.
|
|
196
141
|
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// No local port provided — process alive is enough
|
|
145
|
+
return true;
|
|
197
146
|
}
|
|
198
147
|
|
|
199
148
|
export async function restartTunnel(port: number): Promise<string> {
|
|
200
|
-
|
|
201
|
-
|
|
149
|
+
stopTunnel();
|
|
150
|
+
return startTunnel(port);
|
|
202
151
|
}
|
|
203
152
|
|
|
204
|
-
export async function restartNamedTunnel(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
): Promise<void> {
|
|
208
|
-
stopTunnel();
|
|
209
|
-
await startNamedTunnel(configPath, name);
|
|
153
|
+
export async function restartNamedTunnel(configPath: string, name: string): Promise<void> {
|
|
154
|
+
stopTunnel();
|
|
155
|
+
await startNamedTunnel(configPath, name);
|
|
210
156
|
}
|
package/supervisor/vite-dev.ts
CHANGED
|
@@ -6,77 +6,67 @@ import { log } from '../shared/logger.js';
|
|
|
6
6
|
|
|
7
7
|
let dashboardVite: ViteDevServer | null = null;
|
|
8
8
|
|
|
9
|
-
export async function startViteDevServers(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const ports = {
|
|
14
|
-
dashboard: supervisorPort + 2
|
|
15
|
-
};
|
|
9
|
+
export async function startViteDevServers(supervisorPort: number, hmrServer: http.Server): Promise<{ dashboard: number }> {
|
|
10
|
+
const ports = {
|
|
11
|
+
dashboard: supervisorPort + 2,
|
|
12
|
+
};
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
console.log(`[vite-dev] HMR attached to supervisor server`);
|
|
21
|
-
console.log(`[vite-dev] PKG_DIR = ${PKG_DIR}`);
|
|
14
|
+
console.log(`[vite-dev] Starting dashboard Vite dev server on :${ports.dashboard}`);
|
|
15
|
+
console.log(`[vite-dev] HMR attached to supervisor server`);
|
|
16
|
+
console.log(`[vite-dev] PKG_DIR = ${PKG_DIR}`);
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
18
|
+
try {
|
|
19
|
+
dashboardVite = await createViteServer({
|
|
20
|
+
configFile: path.join(PKG_DIR, 'vite.config.ts'),
|
|
21
|
+
server: {
|
|
22
|
+
port: ports.dashboard,
|
|
23
|
+
host: '127.0.0.1',
|
|
24
|
+
strictPort: true,
|
|
25
|
+
allowedHosts: true,
|
|
26
|
+
// HMR WebSocket is attached directly to the supervisor's HTTP server.
|
|
27
|
+
// No clientPort — the browser connects on the same origin the page is served from,
|
|
28
|
+
// so it works both locally (localhost:3000) and through the relay (riven.fluxy.bot:443).
|
|
29
|
+
hmr: { server: hmrServer },
|
|
30
|
+
},
|
|
31
|
+
logLevel: 'info',
|
|
32
|
+
});
|
|
33
|
+
await dashboardVite.listen();
|
|
34
|
+
console.log(`[vite-dev] ✓ Dashboard Vite ready on :${ports.dashboard}`);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error('[vite-dev] ✗ Dashboard Vite failed:', err);
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
log.ok(`Vite HMR active — dashboard :${ports.dashboard}`);
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
console.log('__VITE_WARM__');
|
|
60
|
-
})
|
|
61
|
-
.catch(() => {
|
|
62
|
-
console.log('__VITE_WARM__');
|
|
63
|
-
});
|
|
42
|
+
// Warm up: fetch the dashboard entry to pre-transform modules
|
|
43
|
+
fetch(`http://127.0.0.1:${ports.dashboard}/`).then(r => r.text()).then(async (html) => {
|
|
44
|
+
const scriptRe = /src="([^"]+\.tsx)"/g;
|
|
45
|
+
let m;
|
|
46
|
+
while ((m = scriptRe.exec(html)) !== null) {
|
|
47
|
+
const url = `http://127.0.0.1:${ports.dashboard}${m[1].startsWith('/') ? '' : '/'}${m[1]}`;
|
|
48
|
+
await fetch(url).then(r => r.text()).catch(() => {});
|
|
49
|
+
}
|
|
50
|
+
console.log('__VITE_WARM__');
|
|
51
|
+
}).catch(() => {
|
|
52
|
+
console.log('__VITE_WARM__');
|
|
53
|
+
});
|
|
64
54
|
|
|
65
|
-
|
|
55
|
+
return ports;
|
|
66
56
|
}
|
|
67
57
|
|
|
68
58
|
/** Tell connected browsers to full-reload via Vite's HMR WebSocket */
|
|
69
59
|
export function reloadDashboard(): void {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
if (!dashboardVite) return;
|
|
61
|
+
console.log('[vite-dev] Sending full-reload to dashboard clients');
|
|
62
|
+
dashboardVite.hot.send({ type: 'full-reload', path: '*' });
|
|
73
63
|
}
|
|
74
64
|
|
|
75
65
|
export async function stopViteDevServers(): Promise<void> {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
66
|
+
console.log('[vite-dev] Stopping Vite dev servers...');
|
|
67
|
+
if (dashboardVite) {
|
|
68
|
+
await dashboardVite.close();
|
|
69
|
+
dashboardVite = null;
|
|
70
|
+
console.log('[vite-dev] Dashboard Vite stopped');
|
|
71
|
+
}
|
|
82
72
|
}
|