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/worker/db.ts CHANGED
@@ -53,274 +53,146 @@ CREATE INDEX IF NOT EXISTS idx_td_token ON trusted_devices(token);
53
53
  let db: Database.Database;
54
54
 
55
55
  export function initDb(): void {
56
- fs.mkdirSync(DATA_DIR, { recursive: true });
57
- db = new Database(paths.db);
58
- db.pragma('journal_mode = WAL');
59
- db.pragma('foreign_keys = ON');
60
- db.exec(SCHEMA);
56
+ fs.mkdirSync(DATA_DIR, { recursive: true });
57
+ db = new Database(paths.db);
58
+ db.pragma('journal_mode = WAL');
59
+ db.pragma('foreign_keys = ON');
60
+ db.exec(SCHEMA);
61
61
 
62
- // Migration: add session_id column if missing (existing DBs)
63
- const cols = db.prepare('PRAGMA table_info(conversations)').all() as {
64
- name: string;
65
- }[];
66
- if (!cols.some(c => c.name === 'session_id')) {
67
- db.exec('ALTER TABLE conversations ADD COLUMN session_id TEXT');
68
- }
62
+ // Migration: add session_id column if missing (existing DBs)
63
+ const cols = db.prepare("PRAGMA table_info(conversations)").all() as { name: string }[];
64
+ if (!cols.some((c) => c.name === 'session_id')) {
65
+ db.exec('ALTER TABLE conversations ADD COLUMN session_id TEXT');
66
+ }
69
67
 
70
- // Migration: add audio_data column if missing (voice messages)
71
- const msgCols = db.prepare('PRAGMA table_info(messages)').all() as {
72
- name: string;
73
- }[];
74
- if (!msgCols.some(c => c.name === 'audio_data')) {
75
- db.exec('ALTER TABLE messages ADD COLUMN audio_data TEXT');
76
- }
68
+ // Migration: add audio_data column if missing (voice messages)
69
+ const msgCols = db.prepare("PRAGMA table_info(messages)").all() as { name: string }[];
70
+ if (!msgCols.some((c) => c.name === 'audio_data')) {
71
+ db.exec('ALTER TABLE messages ADD COLUMN audio_data TEXT');
72
+ }
77
73
 
78
- // Migration: add attachments column if missing (persistent file attachments)
79
- const msgCols2 = db.prepare('PRAGMA table_info(messages)').all() as {
80
- name: string;
81
- }[];
82
- if (!msgCols2.some(c => c.name === 'attachments')) {
83
- db.exec('ALTER TABLE messages ADD COLUMN attachments TEXT');
84
- }
74
+ // Migration: add attachments column if missing (persistent file attachments)
75
+ const msgCols2 = db.prepare("PRAGMA table_info(messages)").all() as { name: string }[];
76
+ if (!msgCols2.some((c) => c.name === 'attachments')) {
77
+ db.exec('ALTER TABLE messages ADD COLUMN attachments TEXT');
78
+ }
85
79
  }
86
80
 
87
- export function closeDb(): void {
88
- db?.close();
89
- }
81
+ export function closeDb(): void { db?.close(); }
90
82
 
91
83
  // Conversations
92
84
  export function createConversation(title?: string, model?: string) {
93
- return db
94
- .prepare(
95
- 'INSERT INTO conversations (title, model) VALUES (?, ?) RETURNING *'
96
- )
97
- .get(title ?? null, model ?? null) as any;
85
+ return db.prepare('INSERT INTO conversations (title, model) VALUES (?, ?) RETURNING *').get(title ?? null, model ?? null) as any;
98
86
  }
99
87
  export function listConversations(limit = 50) {
100
- return db
101
- .prepare('SELECT * FROM conversations ORDER BY updated_at DESC LIMIT ?')
102
- .all(limit);
88
+ return db.prepare('SELECT * FROM conversations ORDER BY updated_at DESC LIMIT ?').all(limit);
103
89
  }
104
90
  export function deleteConversation(id: string) {
105
- db.prepare('DELETE FROM conversations WHERE id = ?').run(id);
91
+ db.prepare('DELETE FROM conversations WHERE id = ?').run(id);
106
92
  }
107
93
 
108
94
  // Messages
109
- export function addMessage(
110
- convId: string,
111
- role: string,
112
- content: string,
113
- meta?: {
114
- tokens_in?: number;
115
- tokens_out?: number;
116
- model?: string;
117
- audio_data?: string;
118
- attachments?: string;
119
- }
120
- ) {
121
- const msg = db
122
- .prepare(
123
- 'INSERT INTO messages (conversation_id, role, content, tokens_in, tokens_out, model, audio_data, attachments) VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *'
124
- )
125
- .get(
126
- convId,
127
- role,
128
- content,
129
- meta?.tokens_in ?? null,
130
- meta?.tokens_out ?? null,
131
- meta?.model ?? null,
132
- meta?.audio_data ?? null,
133
- meta?.attachments ?? null
134
- );
135
- db.prepare(
136
- 'UPDATE conversations SET updated_at = CURRENT_TIMESTAMP WHERE id = ?'
137
- ).run(convId);
138
- return msg as any;
95
+ export function addMessage(convId: string, role: string, content: string, meta?: { tokens_in?: number; tokens_out?: number; model?: string; audio_data?: string; attachments?: string }) {
96
+ const msg = db.prepare('INSERT INTO messages (conversation_id, role, content, tokens_in, tokens_out, model, audio_data, attachments) VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *')
97
+ .get(convId, role, content, meta?.tokens_in ?? null, meta?.tokens_out ?? null, meta?.model ?? null, meta?.audio_data ?? null, meta?.attachments ?? null);
98
+ db.prepare('UPDATE conversations SET updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(convId);
99
+ return msg as any;
139
100
  }
140
101
  export function getMessages(convId: string) {
141
- return db
142
- .prepare(
143
- 'SELECT * FROM messages WHERE conversation_id = ? ORDER BY created_at ASC'
144
- )
145
- .all(convId);
102
+ return db.prepare('SELECT * FROM messages WHERE conversation_id = ? ORDER BY created_at ASC').all(convId);
146
103
  }
147
104
 
148
105
  // Settings
149
106
  export function getSetting(key: string): string | undefined {
150
- return (
151
- db.prepare('SELECT value FROM settings WHERE key = ?').get(key) as any
152
- )?.value;
107
+ return (db.prepare('SELECT value FROM settings WHERE key = ?').get(key) as any)?.value;
153
108
  }
154
109
  export function setSetting(key: string, value: string) {
155
- db.prepare(
156
- 'INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP'
157
- ).run(key, value);
110
+ db.prepare('INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP').run(key, value);
158
111
  }
159
112
  export function getAllSettings() {
160
- const rows = db.prepare('SELECT key, value FROM settings').all() as {
161
- key: string;
162
- value: string;
163
- }[];
164
- return Object.fromEntries(rows.map(r => [r.key, r.value]));
113
+ const rows = db.prepare('SELECT key, value FROM settings').all() as { key: string; value: string }[];
114
+ return Object.fromEntries(rows.map((r) => [r.key, r.value]));
165
115
  }
166
116
 
167
117
  // Auth sessions
168
118
  export function createSession(token: string, expiresAt: string) {
169
- db.prepare('INSERT INTO sessions (token, expires_at) VALUES (?, ?)').run(
170
- token,
171
- expiresAt
172
- );
119
+ db.prepare('INSERT INTO sessions (token, expires_at) VALUES (?, ?)').run(token, expiresAt);
173
120
  }
174
- export function getSession(
175
- token: string
176
- ): { token: string; created_at: string; expires_at: string } | undefined {
177
- return db
178
- .prepare(
179
- "SELECT * FROM sessions WHERE token = ? AND expires_at > datetime('now')"
180
- )
181
- .get(token) as any;
121
+ export function getSession(token: string): { token: string; created_at: string; expires_at: string } | undefined {
122
+ return db.prepare('SELECT * FROM sessions WHERE token = ? AND expires_at > datetime(\'now\')').get(token) as any;
182
123
  }
183
124
  export function deleteSession(token: string) {
184
- db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
125
+ db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
185
126
  }
186
127
  export function deleteExpiredSessions() {
187
- db.prepare(
188
- "DELETE FROM sessions WHERE expires_at <= datetime('now')"
189
- ).run();
128
+ db.prepare('DELETE FROM sessions WHERE expires_at <= datetime(\'now\')').run();
190
129
  }
191
130
 
192
131
  // Session ID (Agent SDK)
193
132
  export function getSessionId(convId: string): string | null {
194
- const row = db
195
- .prepare('SELECT session_id FROM conversations WHERE id = ?')
196
- .get(convId) as any;
197
- return row?.session_id ?? null;
133
+ const row = db.prepare('SELECT session_id FROM conversations WHERE id = ?').get(convId) as any;
134
+ return row?.session_id ?? null;
198
135
  }
199
136
  export function saveSessionId(convId: string, sessionId: string): void {
200
- db.prepare('UPDATE conversations SET session_id = ? WHERE id = ?').run(
201
- sessionId,
202
- convId
203
- );
137
+ db.prepare('UPDATE conversations SET session_id = ? WHERE id = ?').run(sessionId, convId);
204
138
  }
205
139
 
206
140
  // Push subscriptions
207
- export function addPushSubscription(
208
- endpoint: string,
209
- p256dh: string,
210
- auth: string
211
- ) {
212
- db.prepare(
213
- `INSERT INTO push_subscriptions (endpoint, keys_p256dh, keys_auth) VALUES (?, ?, ?)
141
+ export function addPushSubscription(endpoint: string, p256dh: string, auth: string) {
142
+ db.prepare(
143
+ `INSERT INTO push_subscriptions (endpoint, keys_p256dh, keys_auth) VALUES (?, ?, ?)
214
144
  ON CONFLICT(endpoint) DO UPDATE SET keys_p256dh = excluded.keys_p256dh, keys_auth = excluded.keys_auth`
215
- ).run(endpoint, p256dh, auth);
145
+ ).run(endpoint, p256dh, auth);
216
146
  }
217
147
  export function removePushSubscription(endpoint: string) {
218
- db.prepare('DELETE FROM push_subscriptions WHERE endpoint = ?').run(
219
- endpoint
220
- );
148
+ db.prepare('DELETE FROM push_subscriptions WHERE endpoint = ?').run(endpoint);
221
149
  }
222
150
  export function getAllPushSubscriptions() {
223
- return db.prepare('SELECT * FROM push_subscriptions').all() as {
224
- id: number;
225
- endpoint: string;
226
- keys_p256dh: string;
227
- keys_auth: string;
228
- }[];
151
+ return db.prepare('SELECT * FROM push_subscriptions').all() as { id: number; endpoint: string; keys_p256dh: string; keys_auth: string }[];
229
152
  }
230
153
  export function getPushSubscriptionByEndpoint(endpoint: string) {
231
- return db
232
- .prepare('SELECT * FROM push_subscriptions WHERE endpoint = ?')
233
- .get(endpoint) as
234
- | {
235
- id: number;
236
- endpoint: string;
237
- keys_p256dh: string;
238
- keys_auth: string;
239
- }
240
- | undefined;
154
+ return db.prepare('SELECT * FROM push_subscriptions WHERE endpoint = ?').get(endpoint) as { id: number; endpoint: string; keys_p256dh: string; keys_auth: string } | undefined;
241
155
  }
242
156
 
243
157
  // Trusted devices (2FA)
244
- export function createTrustedDevice(
245
- token: string,
246
- label: string,
247
- expiresAt: string
248
- ) {
249
- return db
250
- .prepare(
251
- 'INSERT INTO trusted_devices (token, label, expires_at) VALUES (?, ?, ?) RETURNING *'
252
- )
253
- .get(token, label, expiresAt) as any;
158
+ export function createTrustedDevice(token: string, label: string, expiresAt: string) {
159
+ return db.prepare('INSERT INTO trusted_devices (token, label, expires_at) VALUES (?, ?, ?) RETURNING *').get(token, label, expiresAt) as any;
254
160
  }
255
- export function getTrustedDevice(token: string):
256
- | {
257
- id: string;
258
- token: string;
259
- label: string;
260
- created_at: string;
261
- expires_at: string;
262
- last_seen: string;
263
- }
264
- | undefined {
265
- return db
266
- .prepare(
267
- "SELECT * FROM trusted_devices WHERE token = ? AND expires_at > datetime('now')"
268
- )
269
- .get(token) as any;
161
+ export function getTrustedDevice(token: string): { id: string; token: string; label: string; created_at: string; expires_at: string; last_seen: string } | undefined {
162
+ return db.prepare("SELECT * FROM trusted_devices WHERE token = ? AND expires_at > datetime('now')").get(token) as any;
270
163
  }
271
164
  export function updateDeviceLastSeen(token: string) {
272
- db.prepare(
273
- 'UPDATE trusted_devices SET last_seen = CURRENT_TIMESTAMP WHERE token = ?'
274
- ).run(token);
165
+ db.prepare("UPDATE trusted_devices SET last_seen = CURRENT_TIMESTAMP WHERE token = ?").run(token);
275
166
  }
276
167
  export function listTrustedDevices() {
277
- return db
278
- .prepare(
279
- "SELECT id, label, last_seen, created_at FROM trusted_devices WHERE expires_at > datetime('now') ORDER BY last_seen DESC"
280
- )
281
- .all() as {
282
- id: string;
283
- label: string;
284
- last_seen: string;
285
- created_at: string;
286
- }[];
168
+ return db.prepare("SELECT id, label, last_seen, created_at FROM trusted_devices WHERE expires_at > datetime('now') ORDER BY last_seen DESC").all() as { id: string; label: string; last_seen: string; created_at: string }[];
287
169
  }
288
170
  export function deleteTrustedDevice(id: string) {
289
- db.prepare('DELETE FROM trusted_devices WHERE id = ?').run(id);
171
+ db.prepare('DELETE FROM trusted_devices WHERE id = ?').run(id);
290
172
  }
291
173
  export function deleteExpiredDevices() {
292
- db.prepare(
293
- "DELETE FROM trusted_devices WHERE expires_at <= datetime('now')"
294
- ).run();
174
+ db.prepare("DELETE FROM trusted_devices WHERE expires_at <= datetime('now')").run();
295
175
  }
296
176
  export function deleteAllTrustedDevices() {
297
- db.prepare('DELETE FROM trusted_devices').run();
177
+ db.prepare('DELETE FROM trusted_devices').run();
298
178
  }
299
179
 
300
180
  // Recent messages (for context injection)
301
181
  export function getRecentMessages(convId: string, limit = 20) {
302
- return db
303
- .prepare(`
182
+ return db.prepare(`
304
183
  SELECT * FROM (
305
184
  SELECT * FROM messages WHERE conversation_id = ? ORDER BY created_at DESC LIMIT ?
306
185
  ) sub ORDER BY created_at ASC
307
- `)
308
- .all(convId, limit);
186
+ `).all(convId, limit);
309
187
  }
310
188
 
311
189
  // Cursor-based pagination: messages before a given ID
312
- export function getMessagesBefore(
313
- convId: string,
314
- beforeId: string,
315
- limit = 20
316
- ) {
317
- return db
318
- .prepare(`
190
+ export function getMessagesBefore(convId: string, beforeId: string, limit = 20) {
191
+ return db.prepare(`
319
192
  SELECT * FROM (
320
193
  SELECT * FROM messages
321
194
  WHERE conversation_id = ? AND id < ?
322
195
  ORDER BY id DESC LIMIT ?
323
196
  ) sub ORDER BY id ASC
324
- `)
325
- .all(convId, beforeId, limit);
197
+ `).all(convId, beforeId, limit);
326
198
  }