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
package/components.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
4
- "rsc": false,
5
- "tsx": true,
6
- "tailwind": {
7
- "config": "",
8
- "css": "workspace/client/src/styles/globals.css",
9
- "baseColor": "neutral",
10
- "cssVariables": true
11
- },
12
- "aliases": {
13
- "components": "@/components",
14
- "utils": "@/lib/utils",
15
- "ui": "@/components/ui",
16
- "lib": "@/lib",
17
- "hooks": "@/hooks"
18
- },
19
- "iconLibrary": "lucide"
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "workspace/client/src/styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true
11
+ },
12
+ "aliases": {
13
+ "components": "@/components",
14
+ "utils": "@/lib/utils",
15
+ "ui": "@/components/ui",
16
+ "lib": "@/lib",
17
+ "hooks": "@/hooks"
18
+ },
19
+ "iconLibrary": "lucide"
20
20
  }
package/package.json CHANGED
@@ -1,111 +1,100 @@
1
1
  {
2
- "name": "fluxy-bot",
3
- "description": "Self-hosted, self-evolving AI agent with its own dashboard.",
4
- "version": "0.8.9",
5
- "type": "module",
6
- "license": "MIT",
7
- "keywords": [
8
- "ai",
9
- "assistant",
10
- "bot",
11
- "chatbot",
12
- "fluxy",
13
- "raspberry-pi",
14
- "self-hosted"
15
- ],
16
- "bin": {
17
- "fluxy": "./bin/cli.ts"
18
- },
19
- "scripts": {
20
- "dev": "concurrently \"tsx watch supervisor/index.ts\" \"vite\"",
21
- "dev:docs": "cd ./docs && npx fumapress",
22
- "build": "vite build && vite build --config vite.fluxy.config.ts",
23
- "build:fluxy": "vite build --config vite.fluxy.config.ts",
24
- "start": "node --import tsx/esm supervisor/index.ts",
25
- "postinstall": "node scripts/postinstall.js",
26
- "format": "oxfmt .",
27
- "lint": "oxlint .",
28
- "lint:fix": "oxlint --fix ."
29
- },
30
- "dependencies": {
31
- "@anthropic-ai/claude-agent-sdk": "^0.2.50",
32
- "@clack/prompts": "^1.1.0",
33
- "@google/genai": "^1.46.0",
34
- "@inquirer/prompts": "^8.3.2",
35
- "@react-three/drei": "^10.7.7",
36
- "@react-three/fiber": "^9.5.0",
37
- "@rocicorp/resolver": "^1.0.2",
38
- "@tailwindcss/postcss": "^4.2.0",
39
- "@tailwindcss/vite": "^4.2.0",
40
- "@vitejs/plugin-react": "^5.1.4",
41
- "@xyflow/react": "^12.10.1",
42
- "better-sqlite3": "^12.6.2",
43
- "class-variance-authority": "^0.7.1",
44
- "clsx": "^2.1.1",
45
- "commander": "^14.0.3",
46
- "cron-parser": "^5.5.0",
47
- "date-fns": "^4.1.0",
48
- "express": "^5.2.1",
49
- "framer-motion": "^12.34.3",
50
- "lucide-react": "^0.575.0",
51
- "openai": "^6.32.0",
52
- "otpauth": "^9.3.6",
53
- "picocolors": "^1.1.1",
54
- "postcss": "^8.5.6",
55
- "qrcode": "^1.5.4",
56
- "radix-ui": "^1.4.3",
57
- "react": "^19.2.4",
58
- "react-dom": "^19.2.4",
59
- "react-markdown": "^10.1.0",
60
- "react-router": "^7.13.1",
61
- "react-syntax-highlighter": "^16.1.0",
62
- "recharts": "^3.7.0",
63
- "remark-gfm": "^4.0.1",
64
- "sonner": "^2.0.7",
65
- "tailwind-merge": "^3.5.0",
66
- "tailwindcss": "^4.2.0",
67
- "three": "^0.183.1",
68
- "tsx": "^4.21.0",
69
- "vite": "^7.3.1",
70
- "vite-plugin-pwa": "^1.2.0",
71
- "web-push": "^3.6.7",
72
- "ws": "^8.19.0",
73
- "zustand": "^5.0.11"
74
- },
75
- "devDependencies": {
76
- "@types/better-sqlite3": "^7.6.13",
77
- "@types/express": "^5.0.6",
78
- "@types/node": "^24",
79
- "@types/qrcode": "^1.5.5",
80
- "@types/react": "^19.2.14",
81
- "@types/react-dom": "^19.2.3",
82
- "@types/react-syntax-highlighter": "^15.5.13",
83
- "@types/ws": "^8.18.1",
84
- "concurrently": "^9.2.1",
85
- "fumapress": "^0.1.1",
86
- "oxc": "^1.0.1",
87
- "oxfmt": "^0.41.0",
88
- "oxlint": "^1.56.0",
89
- "typescript": "^5.9.3"
90
- },
91
- "files": [
92
- "bin/",
93
- "cli/",
94
- "dist-fluxy/",
95
- "supervisor/",
96
- "worker/",
97
- "shared/",
98
- "workspace/",
99
- "scripts/",
100
- "vite.config.ts",
101
- "vite.fluxy.config.ts",
102
- "tsconfig.json",
103
- "postcss.config.js",
104
- "components.json",
105
- "install.ps1"
106
- ],
107
- "engines": {
108
- "node": ">=22"
109
- },
110
- "releaseNotes": []
2
+ "name": "fluxy-bot",
3
+ "version": "0.9.0",
4
+ "releaseNotes": [
5
+ "Fixed some bugs to iOs ",
6
+ "2. ",
7
+ "3. ",
8
+ "4. "
9
+ ],
10
+ "description": "Self-hosted, self-evolving AI agent with its own dashboard.",
11
+ "type": "module",
12
+ "license": "MIT",
13
+ "bin": {
14
+ "fluxy": "./bin/cli.js"
15
+ },
16
+ "files": [
17
+ "bin/",
18
+ "dist-fluxy/",
19
+ "supervisor/",
20
+ "worker/",
21
+ "shared/",
22
+ "workspace/",
23
+ "scripts/",
24
+ "vite.config.ts",
25
+ "vite.fluxy.config.ts",
26
+ "tsconfig.json",
27
+ "postcss.config.js",
28
+ "components.json",
29
+ "install.ps1"
30
+ ],
31
+ "keywords": [
32
+ "ai",
33
+ "bot",
34
+ "self-hosted",
35
+ "assistant",
36
+ "chatbot",
37
+ "raspberry-pi",
38
+ "fluxy"
39
+ ],
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "scripts": {
44
+ "dev": "concurrently \"tsx watch supervisor/index.ts\" \"vite\"",
45
+ "build": "vite build && vite build --config vite.fluxy.config.ts",
46
+ "build:fluxy": "vite build --config vite.fluxy.config.ts",
47
+ "start": "node --import tsx/esm supervisor/index.ts",
48
+ "postinstall": "node scripts/postinstall.js",
49
+ "dev:docs": "cd ./docs && npx fumapress"
50
+ },
51
+ "dependencies": {
52
+ "@anthropic-ai/claude-agent-sdk": "^0.2.50",
53
+ "@react-three/drei": "^10.7.7",
54
+ "@react-three/fiber": "^9.5.0",
55
+ "@tailwindcss/postcss": "^4.2.0",
56
+ "@tailwindcss/vite": "^4.2.0",
57
+ "@vitejs/plugin-react": "^5.1.4",
58
+ "better-sqlite3": "^12.6.2",
59
+ "class-variance-authority": "^0.7.1",
60
+ "clsx": "^2.1.1",
61
+ "cron-parser": "^5.5.0",
62
+ "date-fns": "^4.1.0",
63
+ "express": "^5.2.1",
64
+ "framer-motion": "^12.34.3",
65
+ "lucide-react": "^0.575.0",
66
+ "postcss": "^8.5.6",
67
+ "radix-ui": "^1.4.3",
68
+ "react": "^19.2.4",
69
+ "react-dom": "^19.2.4",
70
+ "react-markdown": "^10.1.0",
71
+ "react-syntax-highlighter": "^16.1.0",
72
+ "recharts": "^3.7.0",
73
+ "remark-gfm": "^4.0.1",
74
+ "sonner": "^2.0.7",
75
+ "tailwind-merge": "^3.5.0",
76
+ "tailwindcss": "^4.2.0",
77
+ "three": "^0.183.1",
78
+ "tsx": "^4.21.0",
79
+ "vite": "^7.3.1",
80
+ "vite-plugin-pwa": "^1.2.0",
81
+ "otpauth": "^9.3.6",
82
+ "qrcode": "^1.5.4",
83
+ "web-push": "^3.6.7",
84
+ "ws": "^8.19.0",
85
+ "zustand": "^5.0.11"
86
+ },
87
+ "devDependencies": {
88
+ "@types/better-sqlite3": "^7.6.13",
89
+ "@types/express": "^5.0.6",
90
+ "@types/node": "^25.3.0",
91
+ "@types/qrcode": "^1.5.5",
92
+ "@types/react": "^19.2.14",
93
+ "@types/react-dom": "^19.2.3",
94
+ "@types/react-syntax-highlighter": "^15.5.13",
95
+ "@types/ws": "^8.18.1",
96
+ "concurrently": "^9.2.1",
97
+ "fumapress": "^0.1.1",
98
+ "typescript": "^5.9.3"
99
+ }
111
100
  }
package/postcss.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export default {
2
- plugins: {
3
- // Tailwind is handled by @tailwindcss/vite plugin — no PostCSS plugin needed
4
- }
2
+ plugins: {
3
+ // Tailwind is handled by @tailwindcss/vite plugin — no PostCSS plugin needed
4
+ },
5
5
  };
@@ -189,7 +189,7 @@ function Install-Fluxy {
189
189
  New-Item -ItemType Directory -Path $FLUXY_HOME -Force | Out-Null
190
190
 
191
191
  # Copy code directories (always safe to overwrite)
192
- foreach ($dir in @("bin", "cli", "supervisor", "worker", "shared", "scripts")) {
192
+ foreach ($dir in @("bin", "supervisor", "worker", "shared", "scripts")) {
193
193
  $src = Join-Path $extracted $dir
194
194
  if (Test-Path $src) {
195
195
  Copy-Item -Path $src -Destination $FLUXY_HOME -Recurse -Force
@@ -170,7 +170,7 @@ install_fluxy() {
170
170
  fi
171
171
 
172
172
  # Copy code directories (always safe to overwrite)
173
- for dir in bin cli supervisor worker shared scripts; do
173
+ for dir in bin supervisor worker shared scripts; do
174
174
  [ -d "$EXTRACTED/$dir" ] && cp -r "$EXTRACTED/$dir" "$FLUXY_HOME/"
175
175
  done
176
176
 
@@ -16,12 +16,12 @@ const FLUXY_HOME = path.join(os.homedir(), '.fluxy');
16
16
  // Loop guard: if we're already running inside ~/.fluxy/, exit
17
17
  // (prevents infinite loop when npm install triggers postinstall again)
18
18
  if (path.resolve(PKG_ROOT) === path.resolve(FLUXY_HOME)) {
19
- process.exit(0);
19
+ process.exit(0);
20
20
  }
21
21
 
22
22
  // Dev guard: don't interfere with development
23
23
  if (fs.existsSync(path.join(PKG_ROOT, '.git'))) {
24
- process.exit(0);
24
+ process.exit(0);
25
25
  }
26
26
 
27
27
  // ── Copy source files to ~/.fluxy/ ──
@@ -29,47 +29,43 @@ if (fs.existsSync(path.join(PKG_ROOT, '.git'))) {
29
29
  fs.mkdirSync(FLUXY_HOME, { recursive: true });
30
30
 
31
31
  // Code directories — always overwrite (these are application code)
32
- const CODE_DIRS = ['bin', 'cli', 'supervisor', 'worker', 'shared', 'scripts'];
32
+ const CODE_DIRS = ['bin', 'supervisor', 'worker', 'shared', 'scripts'];
33
33
 
34
34
  // Code files — always overwrite
35
35
  const CODE_FILES = [
36
- 'package.json',
37
- 'vite.config.ts',
38
- 'vite.fluxy.config.ts',
39
- 'tsconfig.json',
40
- 'postcss.config.js',
41
- 'components.json'
36
+ 'package.json', 'vite.config.ts', 'vite.fluxy.config.ts',
37
+ 'tsconfig.json', 'postcss.config.js', 'components.json',
42
38
  ];
43
39
 
44
40
  for (const dir of CODE_DIRS) {
45
- const src = path.join(PKG_ROOT, dir);
46
- if (!fs.existsSync(src)) continue;
47
- const dst = path.join(FLUXY_HOME, dir);
48
- fs.cpSync(src, dst, { recursive: true, force: true });
41
+ const src = path.join(PKG_ROOT, dir);
42
+ if (!fs.existsSync(src)) continue;
43
+ const dst = path.join(FLUXY_HOME, dir);
44
+ fs.cpSync(src, dst, { recursive: true, force: true });
49
45
  }
50
46
 
51
47
  // Workspace template — only on first install (preserves user files, uploads, etc.)
52
48
  const wsSrc = path.join(PKG_ROOT, 'workspace');
53
49
  const wsDst = path.join(FLUXY_HOME, 'workspace');
54
50
  if (fs.existsSync(wsSrc) && !fs.existsSync(wsDst)) {
55
- fs.cpSync(wsSrc, wsDst, { recursive: true });
51
+ fs.cpSync(wsSrc, wsDst, { recursive: true });
56
52
  }
57
53
 
58
54
  for (const file of CODE_FILES) {
59
- const src = path.join(PKG_ROOT, file);
60
- if (!fs.existsSync(src)) continue;
61
- fs.copyFileSync(src, path.join(FLUXY_HOME, file));
55
+ const src = path.join(PKG_ROOT, file);
56
+ if (!fs.existsSync(src)) continue;
57
+ fs.copyFileSync(src, path.join(FLUXY_HOME, file));
62
58
  }
63
59
 
64
60
  // ── Install dependencies in ~/.fluxy/ ──
65
61
 
66
62
  try {
67
- execSync('npm install --omit=dev', {
68
- cwd: FLUXY_HOME,
69
- stdio: 'ignore'
70
- });
63
+ execSync('npm install --omit=dev', {
64
+ cwd: FLUXY_HOME,
65
+ stdio: 'ignore',
66
+ });
71
67
  } catch {
72
- // Non-fatal: deps may already exist from a previous install
68
+ // Non-fatal: deps may already exist from a previous install
73
69
  }
74
70
 
75
71
  // ── Copy pre-built UI if available, otherwise build ──
@@ -77,18 +73,18 @@ try {
77
73
  const distSrc = path.join(PKG_ROOT, 'dist-fluxy');
78
74
  const distDst = path.join(FLUXY_HOME, 'dist-fluxy');
79
75
  if (fs.existsSync(distSrc)) {
80
- // Always use the pre-built UI from the package (handles updates)
81
- if (fs.existsSync(distDst)) fs.rmSync(distDst, { recursive: true });
82
- fs.cpSync(distSrc, distDst, { recursive: true });
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 });
83
79
  } else if (!fs.existsSync(path.join(distDst, 'onboard.html'))) {
84
- try {
85
- execSync('npm run build:fluxy', {
86
- cwd: FLUXY_HOME,
87
- stdio: 'ignore'
88
- });
89
- } catch {
90
- // Non-fatal: supervisor will build on first start
91
- }
80
+ try {
81
+ execSync('npm run build:fluxy', {
82
+ cwd: FLUXY_HOME,
83
+ stdio: 'ignore',
84
+ });
85
+ } catch {
86
+ // Non-fatal: supervisor will build on first start
87
+ }
92
88
  }
93
89
 
94
90
  // ── Create fluxy symlink ──
@@ -97,32 +93,25 @@ const cliPath = path.join(FLUXY_HOME, 'bin', 'cli.js');
97
93
  fs.chmodSync(cliPath, 0o755);
98
94
 
99
95
  if (process.platform === 'win32') {
100
- // On Windows, npm handles the bin linking via package.json "bin" field
101
- // Just ensure the files are in place
96
+ // On Windows, npm handles the bin linking via package.json "bin" field
97
+ // Just ensure the files are in place
102
98
  } else {
103
- const targets = [
104
- '/usr/local/bin/fluxy',
105
- path.join(os.homedir(), '.local', 'bin', 'fluxy')
106
- ];
107
- let linked = false;
108
-
109
- for (const target of targets) {
110
- try {
111
- fs.mkdirSync(path.dirname(target), { recursive: true });
112
- try {
113
- fs.unlinkSync(target);
114
- } catch {}
115
- fs.symlinkSync(cliPath, target);
116
- linked = true;
117
- break;
118
- } catch {
119
- // No permission, try next
120
- }
121
- }
99
+ const targets = ['/usr/local/bin/fluxy', path.join(os.homedir(), '.local', 'bin', 'fluxy')];
100
+ let linked = false;
122
101
 
123
- if (!linked) {
124
- console.log(
125
- `Note: Could not create fluxy symlink. You can run it directly: node ${cliPath}`
126
- );
102
+ for (const target of targets) {
103
+ try {
104
+ fs.mkdirSync(path.dirname(target), { recursive: true });
105
+ try { fs.unlinkSync(target); } catch {}
106
+ fs.symlinkSync(cliPath, target);
107
+ linked = true;
108
+ break;
109
+ } catch {
110
+ // No permission, try next
127
111
  }
112
+ }
113
+
114
+ if (!linked) {
115
+ console.log(`Note: Could not create fluxy symlink. You can run it directly: node ${cliPath}`);
116
+ }
128
117
  }