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.
Files changed (106) hide show
  1. package/README.md +53 -84
  2. package/bin/cli.js +1717 -6
  3. package/components.json +18 -18
  4. package/package.json +98 -109
  5. package/postcss.config.js +3 -3
  6. package/scripts/install.ps1 +1 -1
  7. package/scripts/install.sh +1 -1
  8. package/scripts/postinstall.js +47 -58
  9. package/shared/ai.ts +118 -202
  10. package/shared/config.ts +37 -39
  11. package/shared/logger.ts +6 -14
  12. package/shared/paths.ts +10 -16
  13. package/shared/relay.ts +89 -98
  14. package/supervisor/backend.ts +77 -89
  15. package/supervisor/chat/ARCHITECTURE.md +13 -14
  16. package/supervisor/chat/OnboardWizard.tsx +2194 -3419
  17. package/supervisor/chat/fluxy-main.tsx +495 -633
  18. package/supervisor/chat/fluxy.html +24 -36
  19. package/supervisor/chat/onboard-main.tsx +8 -8
  20. package/supervisor/chat/onboard.html +10 -13
  21. package/supervisor/chat/src/components/Chat/AudioBubble.tsx +94 -108
  22. package/supervisor/chat/src/components/Chat/ChatView.tsx +14 -36
  23. package/supervisor/chat/src/components/Chat/ImageLightbox.tsx +71 -89
  24. package/supervisor/chat/src/components/Chat/InputBar.tsx +498 -728
  25. package/supervisor/chat/src/components/Chat/MessageBubble.tsx +154 -246
  26. package/supervisor/chat/src/components/Chat/MessageList.tsx +114 -138
  27. package/supervisor/chat/src/components/Chat/TypingIndicator.tsx +38 -50
  28. package/supervisor/chat/src/components/LoginScreen.tsx +245 -296
  29. package/supervisor/chat/src/hooks/useChat.ts +210 -259
  30. package/supervisor/chat/src/hooks/useFluxyChat.ts +232 -322
  31. package/supervisor/chat/src/hooks/useSpeechRecognition.ts +126 -148
  32. package/supervisor/chat/src/lib/auth.ts +20 -23
  33. package/supervisor/chat/src/lib/ws-client.ts +113 -125
  34. package/supervisor/chat/src/styles/globals.css +76 -85
  35. package/supervisor/file-saver.ts +39 -52
  36. package/supervisor/fluxy-agent.ts +213 -292
  37. package/supervisor/index.ts +895 -1263
  38. package/supervisor/scheduler.ts +237 -299
  39. package/supervisor/tunnel.ts +113 -167
  40. package/supervisor/vite-dev.ts +51 -61
  41. package/supervisor/widget.js +135 -147
  42. package/supervisor/worker.ts +45 -49
  43. package/tsconfig.json +18 -24
  44. package/vite.config.ts +48 -55
  45. package/vite.fluxy.config.ts +34 -34
  46. package/worker/claude-auth.ts +202 -265
  47. package/worker/codex-auth.ts +144 -181
  48. package/worker/db.ts +63 -191
  49. package/worker/index.ts +601 -842
  50. package/workspace/MYSELF.md +1 -1
  51. package/workspace/PULSE.json +6 -6
  52. package/workspace/backend/index.ts +16 -19
  53. package/workspace/client/index.html +47 -60
  54. package/workspace/client/public/manifest.json +21 -21
  55. package/workspace/client/public/sw.js +29 -31
  56. package/workspace/client/src/App.tsx +91 -136
  57. package/workspace/client/src/components/Dashboard/DashboardPage.tsx +68 -72
  58. package/workspace/client/src/components/ErrorBoundary.tsx +11 -11
  59. package/workspace/client/src/components/Layout/DashboardLayout.tsx +35 -35
  60. package/workspace/client/src/components/Layout/Footer.tsx +12 -12
  61. package/workspace/client/src/components/Layout/MobileNav.tsx +23 -23
  62. package/workspace/client/src/components/Layout/Sidebar.tsx +81 -81
  63. package/workspace/client/src/components/ui/avatar.tsx +85 -85
  64. package/workspace/client/src/components/ui/badge.tsx +40 -41
  65. package/workspace/client/src/components/ui/button.tsx +56 -57
  66. package/workspace/client/src/components/ui/card.tsx +75 -75
  67. package/workspace/client/src/components/ui/dialog.tsx +114 -117
  68. package/workspace/client/src/components/ui/dropdown-menu.tsx +191 -199
  69. package/workspace/client/src/components/ui/input.tsx +17 -17
  70. package/workspace/client/src/components/ui/scroll-area.tsx +47 -47
  71. package/workspace/client/src/components/ui/select.tsx +145 -151
  72. package/workspace/client/src/components/ui/separator.tsx +21 -21
  73. package/workspace/client/src/components/ui/sheet.tsx +102 -102
  74. package/workspace/client/src/components/ui/skeleton.tsx +10 -10
  75. package/workspace/client/src/components/ui/switch.tsx +26 -26
  76. package/workspace/client/src/components/ui/tabs.tsx +69 -69
  77. package/workspace/client/src/components/ui/textarea.tsx +14 -14
  78. package/workspace/client/src/components/ui/tooltip.tsx +37 -37
  79. package/workspace/client/src/lib/utils.ts +1 -1
  80. package/workspace/client/src/main.tsx +3 -3
  81. package/workspace/client/src/styles/globals.css +76 -85
  82. package/workspace/skills/code-reviewer/.claude-plugin/plugin.json +3 -3
  83. package/workspace/skills/code-reviewer/skills/code-reviewer/SKILL.md +0 -6
  84. package/workspace/skills/daily-standup/.claude-plugin/plugin.json +3 -3
  85. package/workspace/skills/daily-standup/skills/daily-standup/SKILL.md +0 -7
  86. package/workspace/skills/workspace-helper/.claude-plugin/plugin.json +3 -3
  87. package/workspace/skills/workspace-helper/skills/workspace-helper/SKILL.md +0 -2
  88. package/bin/cli.backup.js +0 -2138
  89. package/cli/commands/daemon.ts +0 -42
  90. package/cli/commands/init.ts +0 -32
  91. package/cli/commands/start.ts +0 -113
  92. package/cli/commands/tunnel.temp.ts +0 -206
  93. package/cli/commands/tunnel.ts +0 -227
  94. package/cli/commands/update.ts +0 -163
  95. package/cli/core/base-adapter.ts +0 -156
  96. package/cli/core/cloudflared.ts +0 -113
  97. package/cli/core/config.ts +0 -73
  98. package/cli/core/os-detector.ts +0 -43
  99. package/cli/core/server.ts +0 -109
  100. package/cli/core/types.ts +0 -15
  101. package/cli/index.ts +0 -72
  102. package/cli/platforms/darwin.ts +0 -146
  103. package/cli/platforms/index.ts +0 -21
  104. package/cli/platforms/linux.ts +0 -156
  105. package/cli/platforms/win32.ts +0 -34
  106. package/cli/utils/ui.ts +0 -37
@@ -1,42 +0,0 @@
1
- // oxlint-disable no-console
2
- import { Command } from 'commander';
3
- import os from 'node:os';
4
-
5
- import { DATA_DIR } from '../core/config.js';
6
- import type { DaemonAction, DaemonConfig } from '../core/types.js';
7
- import { getAdapter } from '../platforms/index.js';
8
-
9
- export function registerDaemonCommand(program: Command) {
10
- const adapter = getAdapter();
11
-
12
- program
13
- .command('daemon <action>')
14
- .description(
15
- 'Manage the Fluxy background daemon (install, start, stop, restart, status, logs, uninstall)'
16
- )
17
- .action(async (action: string) => {
18
- const validActions: DaemonAction[] = [
19
- 'install',
20
- 'start',
21
- 'stop',
22
- 'restart',
23
- 'status',
24
- 'logs',
25
- 'uninstall'
26
- ];
27
-
28
- if (!validActions.includes(action as DaemonAction)) {
29
- console.error(`Invalid daemon action: ${action}`);
30
- process.exit(1);
31
- }
32
-
33
- const config: DaemonConfig = {
34
- user: process.env.SUDO_USER || os.userInfo().username,
35
- home: process.env.FLUXY_REAL_HOME || os.homedir(),
36
- nodePath: process.env.FLUXY_NODE_PATH || process.execPath,
37
- dataDir: DATA_DIR
38
- };
39
-
40
- await adapter.handleDaemonAction(action as DaemonAction, config);
41
- });
42
- }
@@ -1,32 +0,0 @@
1
- import { Command } from 'commander';
2
- import { safeLoadConfig, pkg, DATA_DIR, createConfig } from '../core/config.js';
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import pc from 'picocolors';
6
- import { runTunnelSetup } from './tunnel.js';
7
-
8
- export function registerInitCommand(program: Command) {
9
- program
10
- .command('init')
11
- .description('Initialize Fluxy configuration')
12
- .action(async () => {
13
- createConfig(); // Creates it if missing
14
- fs.mkdirSync(DATA_DIR, { recursive: true });
15
- fs.writeFileSync(
16
- path.join(DATA_DIR, '.version'),
17
- pkg.version || '1.0.0'
18
- );
19
-
20
- console.log(pc.green('✓ Initialized base config.\n'));
21
-
22
- await runTunnelSetup();
23
-
24
- console.log(
25
- pc.dim(
26
- 'Run ' +
27
- pc.magenta('fluxy start') +
28
- ' to launch the server.'
29
- )
30
- );
31
- });
32
- }
@@ -1,113 +0,0 @@
1
- import { Command } from 'commander';
2
- import { spinner } from '@clack/prompts';
3
- import pc from 'picocolors';
4
- import fs from 'node:fs';
5
-
6
- import { loadConfig, createConfig, CONFIG_PATH } from '../core/config.ts';
7
- import { getAdapter } from '../platforms/index.ts';
8
- import { banner, commandExample } from '../utils/ui.ts';
9
- import { bootServer } from '../core/server.ts';
10
- import { CloudflaredManager } from '../core/cloudflared.ts';
11
-
12
- export function registerStartCommand(program: Command) {
13
- const adapter = getAdapter();
14
-
15
- program
16
- .command('start', { isDefault: true })
17
- .description('Start the Fluxy server and tunnel')
18
- .action(async () => {
19
- banner();
20
-
21
- if (!fs.existsSync(CONFIG_PATH)) {
22
- console.log(
23
- pc.yellow(
24
- ' ● Running first-time setup (creating config)...\n'
25
- )
26
- );
27
- createConfig();
28
- }
29
-
30
- const config = loadConfig();
31
-
32
- if (adapter.isInstalled && adapter.isActive) {
33
- console.log(
34
- pc.blue(' ● Fluxy is already running as a daemon.\n')
35
- );
36
-
37
- if (config.relay?.url) {
38
- console.log(commandExample('URL: ', config.relay.url));
39
- }
40
-
41
- console.log(commandExample(`Status: `, `fluxy daemon status`));
42
- console.log(commandExample(`Logs: `, `fluxy daemon logs`));
43
- console.log(commandExample(`Restart:`, `fluxy daemon restart`));
44
- console.log(commandExample(`Stop: `, `fluxy daemon stop`));
45
- return;
46
- }
47
-
48
- const tunnelMode =
49
- config.tunnel?.mode ??
50
- (config.tunnel?.enabled === false ? 'off' : 'quick');
51
- const hasTunnel = tunnelMode !== 'off';
52
-
53
- if (hasTunnel && tunnelMode !== 'named') {
54
- const s1 = spinner();
55
- s1.start('Ensuring Cloudflared is installed...');
56
- CloudflaredManager.install();
57
- s1.stop(pc.green('Cloudflared ready'));
58
- }
59
-
60
- const s = spinner();
61
- s.start('Starting server');
62
-
63
- try {
64
- const result = await bootServer({
65
- onTunnelUp: url => {
66
- if (!hasTunnel) return;
67
- s.message(
68
- `Connecting tunnel... up at ${pc.blue(url || '')}`
69
- );
70
- },
71
- onReady: () => {
72
- s.message('Preparing dashboard...');
73
- }
74
- });
75
-
76
- s.stop(pc.green('Server running'));
77
-
78
- console.log(`\n${pc.bold('Fluxy is ready!')}`);
79
- console.log(
80
- ` ${pc.dim('Local:')} ${pc.blue(`http://localhost:${config.port || 3000}`)}`
81
- );
82
-
83
- if (result.tunnelUrl && hasTunnel) {
84
- console.log(
85
- ` ${pc.dim('Tunnel:')} ${pc.blue(result.tunnelUrl)}`
86
- );
87
- }
88
- if (result.relayUrl) {
89
- console.log(
90
- ` ${pc.dim('Relay:')} ${pc.blue(result.relayUrl)}`
91
- );
92
- }
93
-
94
- // If not daemonized, we keep the process alive
95
- console.log(`\n ${pc.dim('Press Ctrl+C to stop')}\n`);
96
-
97
- // wait for Vite warm
98
- Promise.race([
99
- result.viteWarm,
100
- new Promise(r => setTimeout(r, 60_000))
101
- ]).then(() => {
102
- // done warming
103
- });
104
-
105
- // Block forever
106
- await new Promise(() => {});
107
- } catch (err: any) {
108
- s.stop(pc.red('Failed to start server'));
109
- console.error(err.message);
110
- process.exit(1);
111
- }
112
- });
113
- }
@@ -1,206 +0,0 @@
1
- import { Command } from 'commander';
2
- import { cancel, intro, isCancel, select, text, spinner } from '@clack/prompts';
3
- import pc from 'picocolors';
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import os from 'node:os';
7
-
8
- import {
9
- loadConfig,
10
- safeLoadConfig,
11
- CONFIG_PATH,
12
- DATA_DIR
13
- } from '../core/config.ts';
14
- import { getAdapter } from '../platforms/index.ts';
15
- import { CloudflaredManager } from '../core/cloudflared.ts';
16
-
17
- async function runNamedTunnelSetup() {
18
- CloudflaredManager.install();
19
-
20
- console.log(pc.bold(pc.white('\n Step 1: Log in to Cloudflare\n')));
21
- console.log(
22
- pc.dim(
23
- ' This will open a browser window. Authorize the domain you want to use.\n'
24
- )
25
- );
26
-
27
- try {
28
- CloudflaredManager.exec('tunnel login', { stdio: 'inherit' });
29
- } catch (_e) {
30
- console.error(pc.red('cloudflared login failed.'));
31
- process.exit(1);
32
- }
33
-
34
- const tunnelName = await text({
35
- message: 'Tunnel name (default: fluxy):',
36
- defaultValue: 'fluxy'
37
- });
38
-
39
- if (isCancel(tunnelName)) {
40
- cancel('Setup cancelled.');
41
- process.exit(0);
42
- }
43
-
44
- const s = spinner();
45
- s.start(`Creating tunnel "${tunnelName}"...`);
46
-
47
- let createOutput;
48
- try {
49
- createOutput = CloudflaredManager.exec(`tunnel create ${tunnelName}`, {
50
- encoding: 'utf-8'
51
- });
52
- } catch (e: any) {
53
- s.stop(pc.red('Failed to create tunnel. It may already exist.'));
54
- console.log(
55
- pc.dim('Try: fluxy tunnel list (or cloudflared tunnel list)')
56
- );
57
- process.exit(1);
58
- }
59
-
60
- const uuidMatch = createOutput?.match(
61
- /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i
62
- );
63
- if (!uuidMatch) {
64
- s.stop(pc.red('Could not parse tunnel UUID from output.'));
65
- console.log(pc.dim(createOutput || ''));
66
- process.exit(1);
67
- }
68
-
69
- const tunnelUuid = uuidMatch[1];
70
- s.stop(pc.green(`Tunnel created: ${pc.dim(tunnelUuid)}`));
71
-
72
- const domain = await text({
73
- message: 'Your domain (e.g. bot.mydomain.com):'
74
- });
75
-
76
- if (isCancel(domain) || !domain) {
77
- cancel('Domain is required.');
78
- process.exit(1);
79
- }
80
-
81
- const { port = 3000 } = safeLoadConfig();
82
- const cfHome = path.join(os.homedir(), '.cloudflared');
83
- const cfConfigPath = path.join(DATA_DIR, 'cloudflared-config.yml');
84
-
85
- const yamlContent = `tunnel: ${tunnelUuid}\ncredentials-file: ${path.join(cfHome, `${tunnelUuid}.json`)}\ningress:\n - service: http://localhost:${port}\n`;
86
- fs.mkdirSync(DATA_DIR, { recursive: true });
87
- fs.writeFileSync(cfConfigPath, yamlContent);
88
-
89
- console.log(`\n${pc.dim('─────────────────────────────────')}\n`);
90
- console.log(` ${pc.bold(pc.white('Tunnel created!'))}`);
91
- console.log(` ${pc.white('Add a CNAME record pointing to:')}\n`);
92
- console.log(` ${pc.bold(pc.magenta(`${tunnelUuid}.cfargotunnel.com`))}\n`);
93
- console.log(
94
- ` ${pc.dim(`Or run: cloudflared tunnel route dns ${tunnelName} ${domain}`)}\n`
95
- );
96
-
97
- return { tunnelName, domain, cfConfigPath };
98
- }
99
-
100
- export function registerTunnelCommand(program: Command) {
101
- const adapter = getAdapter();
102
-
103
- program
104
- .command('tunnel [subcommand]')
105
- .description('Manage the Cloudflared tunnel (setup, login, reset)')
106
- .action(async (subcommand?: string) => {
107
- if (subcommand === 'login') {
108
- CloudflaredManager.install();
109
- console.log(pc.cyan('Logging into cloudflared...'));
110
- CloudflaredManager.exec('tunnel login', { stdio: 'inherit' });
111
- return;
112
- }
113
-
114
- if (subcommand === 'reset') {
115
- if (!fs.existsSync(CONFIG_PATH)) {
116
- console.log(
117
- pc.yellow('No config found. Run fluxy init first.')
118
- );
119
- return;
120
- }
121
- const config = loadConfig();
122
- config.tunnel = { mode: 'quick' };
123
- delete config.tunnelUrl;
124
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
125
- console.log(pc.green('Tunnel mode reset to quick.'));
126
- if (adapter.isInstalled && adapter.isActive) {
127
- console.log(
128
- pc.dim(
129
- 'Restart the daemon to apply: ' +
130
- pc.magenta('fluxy daemon restart')
131
- )
132
- );
133
- }
134
- return;
135
- }
136
-
137
- intro(pc.inverse(' Fluxy Tunnel Setup '));
138
-
139
- const mode = await select({
140
- message: 'Select tunnel mode:',
141
- options: [
142
- { value: 'quick', label: 'Quick (Random Cloudflare URL)' },
143
- {
144
- value: 'named',
145
- label: 'Named (Persistent Custom Domain)'
146
- },
147
- { value: 'off', label: 'Off (Local only)' }
148
- ]
149
- });
150
-
151
- if (isCancel(mode)) {
152
- cancel('Operation cancelled.');
153
- return;
154
- }
155
-
156
- let setupData: any = {};
157
-
158
- if (mode === 'named') {
159
- setupData = await runNamedTunnelSetup();
160
- }
161
-
162
- const config = safeLoadConfig();
163
- if (mode === 'named') {
164
- config.tunnel = {
165
- mode: 'named',
166
- name: setupData.tunnelName,
167
- domain: setupData.domain,
168
- configPath: setupData.cfConfigPath
169
- };
170
- } else {
171
- config.tunnel = { mode: mode as any };
172
- delete config.tunnelUrl;
173
- }
174
-
175
- fs.mkdirSync(DATA_DIR, { recursive: true });
176
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
177
-
178
- console.log(pc.green('\n✓ Fluxy configuration updated.\n'));
179
-
180
- if (adapter.isInstalled && adapter.isActive) {
181
- const restart = await select({
182
- message: 'Restart daemon now to apply changes?',
183
- options: [
184
- { value: true, label: 'Yes' },
185
- { value: false, label: 'No' }
186
- ]
187
- });
188
- if (restart) {
189
- adapter.handleDaemonAction('restart', {
190
- user: process.env.SUDO_USER || os.userInfo().username,
191
- home: process.env.FLUXY_REAL_HOME || os.homedir(),
192
- nodePath:
193
- process.env.FLUXY_NODE_PATH || process.execPath,
194
- dataDir: DATA_DIR
195
- });
196
- }
197
- } else {
198
- console.log(
199
- pc.dim(
200
- 'Start the server to apply changes: ' +
201
- pc.magenta('fluxy start')
202
- )
203
- );
204
- }
205
- });
206
- }
@@ -1,227 +0,0 @@
1
- import { Command } from 'commander';
2
- import { cancel, intro, isCancel, select, text, spinner } from '@clack/prompts';
3
- import pc from 'picocolors';
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import os from 'node:os';
7
-
8
- import {
9
- loadConfig,
10
- safeLoadConfig,
11
- CONFIG_PATH,
12
- DATA_DIR
13
- } from '../core/config.ts';
14
- import { getAdapter } from '../platforms/index.ts';
15
- import { CloudflaredManager } from '../core/cloudflared.ts';
16
-
17
- async function runNamedTunnelSetup() {
18
- CloudflaredManager.install();
19
-
20
- console.log(pc.bold(pc.white('\n Step 1: Log in to Cloudflare\n')));
21
- console.log(
22
- pc.dim(
23
- ' This will open a browser window. Authorize the domain you want to use.\n'
24
- )
25
- );
26
-
27
- try {
28
- CloudflaredManager.exec('tunnel login', { stdio: 'inherit' });
29
- } catch (_e) {
30
- console.error(pc.red('cloudflared login failed.'));
31
- process.exit(1);
32
- }
33
-
34
- const tunnelName = await text({
35
- message: 'Tunnel name (default: fluxy):',
36
- defaultValue: 'fluxy'
37
- });
38
-
39
- if (isCancel(tunnelName)) {
40
- cancel('Setup cancelled.');
41
- process.exit(0);
42
- }
43
-
44
- const s = spinner();
45
- s.start(`Creating tunnel "${tunnelName}"...`);
46
-
47
- let createOutput;
48
- try {
49
- createOutput = CloudflaredManager.exec(`tunnel create ${tunnelName}`, {
50
- encoding: 'utf-8'
51
- });
52
- } catch (e: any) {
53
- s.stop(pc.red('Failed to create tunnel. It may already exist.'));
54
- console.log(
55
- pc.dim('Try: fluxy tunnel list (or cloudflared tunnel list)')
56
- );
57
- process.exit(1);
58
- }
59
-
60
- const uuidMatch = createOutput?.match(
61
- /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i
62
- );
63
- if (!uuidMatch) {
64
- s.stop(pc.red('Could not parse tunnel UUID from output.'));
65
- console.log(pc.dim(createOutput?.toString() || ''));
66
- process.exit(1);
67
- }
68
-
69
- const tunnelUuid = uuidMatch[1];
70
- s.stop(pc.green(`Tunnel created: ${pc.dim(tunnelUuid)}`));
71
-
72
- const domain = await text({
73
- message: 'Your domain (e.g. bot.mydomain.com):'
74
- });
75
-
76
- if (isCancel(domain) || !domain) {
77
- cancel('Domain is required.');
78
- process.exit(1);
79
- }
80
-
81
- const { port = 3000 } = safeLoadConfig();
82
- const cfHome = path.join(os.homedir(), '.cloudflared');
83
- const cfConfigPath = path.join(DATA_DIR, 'cloudflared-config.yml');
84
-
85
- const yamlContent = `tunnel: ${tunnelUuid}\ncredentials-file: ${path.join(cfHome, `${tunnelUuid}.json`)}\ningress:\n - service: http://localhost:${port}\n`;
86
- fs.mkdirSync(DATA_DIR, { recursive: true });
87
- fs.writeFileSync(cfConfigPath, yamlContent);
88
-
89
- console.log(`\n${pc.dim('─────────────────────────────────')}\n`);
90
- console.log(` ${pc.bold(pc.white('Tunnel created!'))}`);
91
- console.log(` ${pc.white('Add a CNAME record pointing to:')}\n`);
92
- console.log(` ${pc.bold(pc.magenta(`${tunnelUuid}.cfargotunnel.com`))}\n`);
93
- console.log(
94
- ` ${pc.dim(`Or run: cloudflared tunnel route dns ${tunnelName} ${domain}`)}\n`
95
- );
96
-
97
- return { tunnelName, domain, cfConfigPath };
98
- }
99
-
100
- export async function runTunnelSetup() {
101
- intro(pc.inverse(' Fluxy Tunnel Setup '));
102
-
103
- const mode = await select({
104
- message: 'How do you want to connect your bot?',
105
- options: [
106
- {
107
- value: 'quick',
108
- label: `Quick Tunnel ${pc.green('[Easy and Fast]')}`,
109
- hint: pc.dim(
110
- 'Random CloudFlare tunnel URL on every start/update\n Optional: Use Fluxy Relay Server and access your bot at my.fluxy.bot/YOURBOT'
111
- )
112
- },
113
- {
114
- value: 'named',
115
- label: `Named Tunnel ${pc.yellow('[Advanced]')}`,
116
- hint: pc.dim(
117
- 'Persistent URL with your own domain\n Requires a CloudFlare account + domain'
118
- )
119
- },
120
- {
121
- value: 'off',
122
- label: `Private Network ${pc.cyan('[Secure]')}`,
123
- hint: pc.dim(
124
- 'No public URL — access via local network or VPN only\n Your bot stays invisible to the internet'
125
- )
126
- }
127
- ]
128
- });
129
-
130
- if (isCancel(mode)) {
131
- cancel('Operation cancelled.');
132
- process.exit(1);
133
- }
134
-
135
- let setupData: any = {};
136
-
137
- if (mode === 'named') {
138
- setupData = await runNamedTunnelSetup();
139
- }
140
-
141
- const config = safeLoadConfig();
142
- if (mode === 'named') {
143
- config.tunnel = {
144
- mode: 'named',
145
- name: setupData.tunnelName,
146
- domain: setupData.domain,
147
- configPath: setupData.cfConfigPath
148
- };
149
- } else {
150
- config.tunnel = { mode: mode as any };
151
- delete config.tunnelUrl;
152
- }
153
-
154
- fs.mkdirSync(DATA_DIR, { recursive: true });
155
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
156
-
157
- console.log(pc.green('\n✓ Fluxy tunnel configuration updated.\n'));
158
-
159
- return mode;
160
- }
161
-
162
- export function registerTunnelCommand(program: Command) {
163
- const adapter = getAdapter();
164
-
165
- program
166
- .command('tunnel [subcommand]')
167
- .description('Manage the Cloudflared tunnel (setup, login, reset)')
168
- .action(async (subcommand?: string) => {
169
- if (subcommand === 'login') {
170
- CloudflaredManager.install();
171
- console.log(pc.cyan('Logging into cloudflared...'));
172
- CloudflaredManager.exec('tunnel login', { stdio: 'inherit' });
173
- return;
174
- }
175
-
176
- if (subcommand === 'reset') {
177
- if (!fs.existsSync(CONFIG_PATH)) {
178
- console.log(
179
- pc.yellow('No config found. Run fluxy init first.')
180
- );
181
- return;
182
- }
183
- const config = loadConfig();
184
- config.tunnel = { mode: 'quick' };
185
- delete config.tunnelUrl;
186
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
187
- console.log(pc.green('Tunnel mode reset to quick.'));
188
- if (adapter.isInstalled && adapter.isActive) {
189
- console.log(
190
- pc.dim(
191
- 'Restart the daemon to apply: ' +
192
- pc.magenta('fluxy daemon restart')
193
- )
194
- );
195
- }
196
- return;
197
- }
198
-
199
- await runTunnelSetup();
200
-
201
- if (adapter.isInstalled && adapter.isActive) {
202
- const restart = await select({
203
- message: 'Restart daemon now to apply changes?',
204
- options: [
205
- { value: true, label: 'Yes' },
206
- { value: false, label: 'No' }
207
- ]
208
- });
209
- if (restart === true) {
210
- adapter.handleDaemonAction('restart', {
211
- user: process.env.SUDO_USER || os.userInfo().username,
212
- home: process.env.FLUXY_REAL_HOME || os.homedir(),
213
- nodePath:
214
- process.env.FLUXY_NODE_PATH || process.execPath,
215
- dataDir: DATA_DIR
216
- });
217
- }
218
- } else {
219
- console.log(
220
- pc.dim(
221
- 'Start the server to apply changes: ' +
222
- pc.magenta('fluxy start')
223
- )
224
- );
225
- }
226
- });
227
- }