neoagent 2.4.1-beta.8 → 2.4.1

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 (248) hide show
  1. package/.env.example +33 -3
  2. package/LICENSE +111 -56
  3. package/README.md +8 -3
  4. package/docs/configuration.md +8 -0
  5. package/docs/getting-started.md +9 -3
  6. package/docs/index.md +4 -0
  7. package/extensions/chrome-browser/background.mjs +45 -7
  8. package/extensions/chrome-browser/icons/icon128.png +0 -0
  9. package/extensions/chrome-browser/icons/icon16.png +0 -0
  10. package/extensions/chrome-browser/icons/icon48.png +0 -0
  11. package/extensions/chrome-browser/icons/logo.svg +39 -8
  12. package/extensions/chrome-browser/manifest.json +3 -2
  13. package/extensions/chrome-browser/popup.html +5 -1
  14. package/extensions/chrome-browser/popup.js +15 -2
  15. package/flutter_app/android/app/src/main/AndroidManifest.xml +2 -1
  16. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  17. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  18. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  19. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  20. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  21. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  22. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  23. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  24. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  25. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  26. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  27. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  28. package/flutter_app/assets/branding/app_icon_light_1024.png +0 -0
  29. package/flutter_app/assets/branding/app_icon_light_128.png +0 -0
  30. package/flutter_app/assets/branding/app_icon_light_192.png +0 -0
  31. package/flutter_app/assets/branding/app_icon_light_256.png +0 -0
  32. package/flutter_app/assets/branding/app_icon_light_32.png +0 -0
  33. package/flutter_app/assets/branding/app_icon_light_512.png +0 -0
  34. package/flutter_app/assets/branding/app_icon_light_64.png +0 -0
  35. package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
  36. package/flutter_app/assets/branding/tray_icon_light_template.png +0 -0
  37. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  38. package/flutter_app/lib/features/location/location_service.dart +3 -0
  39. package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +391 -382
  40. package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +743 -0
  41. package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +18 -16
  42. package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +19 -18
  43. package/flutter_app/lib/features/onboarding/onboarding_shell.dart +8 -1
  44. package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +16 -13
  45. package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +17 -13
  46. package/flutter_app/lib/main.dart +3 -0
  47. package/flutter_app/lib/main_account_settings.dart +10 -34
  48. package/flutter_app/lib/main_admin.dart +14 -1
  49. package/flutter_app/lib/main_app_shell.dart +377 -340
  50. package/flutter_app/lib/main_chat.dart +707 -227
  51. package/flutter_app/lib/main_controller.dart +338 -46
  52. package/flutter_app/lib/main_devices.dart +851 -122
  53. package/flutter_app/lib/main_integrations.dart +255 -6
  54. package/flutter_app/lib/main_launcher.dart +1 -1
  55. package/flutter_app/lib/main_model_picker.dart +685 -0
  56. package/flutter_app/lib/main_models.dart +212 -0
  57. package/flutter_app/lib/main_navigation.dart +1 -9
  58. package/flutter_app/lib/main_operations.dart +1417 -614
  59. package/flutter_app/lib/main_settings.dart +764 -887
  60. package/flutter_app/lib/main_shared.dart +971 -443
  61. package/flutter_app/lib/main_spacing.dart +4 -4
  62. package/flutter_app/lib/main_theme.dart +22 -14
  63. package/flutter_app/lib/main_unified.dart +5 -72
  64. package/flutter_app/lib/src/android_apk_drop_zone.dart +24 -0
  65. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +13 -0
  66. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +219 -0
  67. package/flutter_app/lib/src/backend_client.dart +79 -0
  68. package/flutter_app/lib/src/desktop_companion_actions.dart +56 -7
  69. package/flutter_app/lib/src/desktop_companion_io.dart +77 -1
  70. package/flutter_app/lib/src/desktop_native_bridge.dart +13 -0
  71. package/flutter_app/lib/src/security/password_strength.dart +84 -0
  72. package/flutter_app/lib/src/stream_renderer.dart +205 -35
  73. package/flutter_app/lib/src/theme/palette.dart +76 -34
  74. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  75. package/flutter_app/macos/Runner/AppDelegate.swift +44 -0
  76. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  77. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  78. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  79. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  80. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  81. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  82. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  83. package/flutter_app/pubspec.lock +2 -2
  84. package/flutter_app/pubspec.yaml +7 -1
  85. package/flutter_app/tool/branding_source/neoagent-icon-1024.png +0 -0
  86. package/flutter_app/tool/branding_source/neoagent-icon-128.png +0 -0
  87. package/flutter_app/tool/branding_source/neoagent-icon-16.png +0 -0
  88. package/flutter_app/tool/branding_source/neoagent-icon-180.png +0 -0
  89. package/flutter_app/tool/branding_source/neoagent-icon-192.png +0 -0
  90. package/flutter_app/tool/branding_source/neoagent-icon-256.png +0 -0
  91. package/flutter_app/tool/branding_source/neoagent-icon-32.png +0 -0
  92. package/flutter_app/tool/branding_source/neoagent-icon-48.png +0 -0
  93. package/flutter_app/tool/branding_source/neoagent-icon-512.png +0 -0
  94. package/flutter_app/tool/branding_source/neoagent-icon-64.png +0 -0
  95. package/flutter_app/tool/branding_source/neoagent-icon.svg +43 -0
  96. package/flutter_app/tool/generate_desktop_branding.py +154 -152
  97. package/flutter_app/web/favicon.png +0 -0
  98. package/flutter_app/web/favicon.svg +40 -9
  99. package/flutter_app/web/favicon_light.svg +43 -0
  100. package/flutter_app/web/icons/Icon-192-light.png +0 -0
  101. package/flutter_app/web/icons/Icon-192.png +0 -0
  102. package/flutter_app/web/icons/Icon-512-light.png +0 -0
  103. package/flutter_app/web/icons/Icon-512.png +0 -0
  104. package/flutter_app/web/icons/Icon-maskable-192-light.png +0 -0
  105. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  106. package/flutter_app/web/icons/Icon-maskable-512-light.png +0 -0
  107. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  108. package/flutter_app/windows/runner/main.cpp +7 -1
  109. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  110. package/lib/manager.js +445 -83
  111. package/package.json +17 -3
  112. package/runtime/paths.js +3 -1
  113. package/server/admin/access.js +198 -0
  114. package/server/admin/admin.css +268 -0
  115. package/server/admin/admin.js +348 -0
  116. package/server/admin/analytics.js +128 -0
  117. package/server/admin/index.html +1015 -0
  118. package/server/admin/login.html +290 -0
  119. package/server/admin/logo.svg +43 -0
  120. package/server/admin/sql.js +134 -0
  121. package/server/admin/users.js +147 -0
  122. package/server/config/origins.js +3 -1
  123. package/server/db/database.js +92 -0
  124. package/server/db/ftsQuery.js +27 -0
  125. package/server/http/routes.js +1 -0
  126. package/server/http/static.js +23 -6
  127. package/server/index.js +1 -1
  128. package/server/middleware/adminAuth.js +48 -0
  129. package/server/middleware/auth.js +1 -40
  130. package/server/public/.last_build_id +1 -1
  131. package/server/public/app_icon.png +0 -0
  132. package/server/public/assets/AssetManifest.bin +1 -1
  133. package/server/public/assets/AssetManifest.bin.json +1 -1
  134. package/server/public/assets/assets/branding/app_icon_1024.png +0 -0
  135. package/server/public/assets/assets/branding/app_icon_256.png +0 -0
  136. package/server/public/assets/assets/branding/app_icon_512.png +0 -0
  137. package/server/public/assets/assets/branding/app_icon_light_1024.png +0 -0
  138. package/server/public/assets/assets/branding/app_icon_light_256.png +0 -0
  139. package/server/public/assets/assets/branding/app_icon_light_512.png +0 -0
  140. package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
  141. package/server/public/assets/assets/branding/tray_icon_light_template.png +0 -0
  142. package/server/public/assets/assets/branding/tray_icon_template.png +0 -0
  143. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  144. package/server/public/assets/web/icons/Icon-192.png +0 -0
  145. package/server/public/favicon.png +0 -0
  146. package/server/public/favicon.svg +40 -9
  147. package/server/public/favicon_light.svg +43 -0
  148. package/server/public/flutter_bootstrap.js +2 -2
  149. package/server/public/icons/Icon-192-light.png +0 -0
  150. package/server/public/icons/Icon-192.png +0 -0
  151. package/server/public/icons/Icon-512-light.png +0 -0
  152. package/server/public/icons/Icon-512.png +0 -0
  153. package/server/public/icons/Icon-maskable-192-light.png +0 -0
  154. package/server/public/icons/Icon-maskable-192.png +0 -0
  155. package/server/public/icons/Icon-maskable-512-light.png +0 -0
  156. package/server/public/icons/Icon-maskable-512.png +0 -0
  157. package/server/public/main.dart.js +88944 -85720
  158. package/server/routes/admin.js +632 -0
  159. package/server/routes/agent_profiles.js +3 -0
  160. package/server/routes/agents.js +1 -1
  161. package/server/routes/auth.js +3 -1
  162. package/server/routes/browser.js +14 -0
  163. package/server/routes/browser_extension.js +21 -4
  164. package/server/routes/desktop.js +10 -0
  165. package/server/routes/mcp.js +29 -13
  166. package/server/routes/memory.js +23 -1
  167. package/server/routes/messaging.js +2 -0
  168. package/server/routes/screenHistory.js +14 -8
  169. package/server/routes/settings.js +14 -2
  170. package/server/routes/stream.js +12 -3
  171. package/server/routes/triggers.js +4 -4
  172. package/server/routes/voice_assistant.js +36 -1
  173. package/server/routes/workspace.js +86 -0
  174. package/server/services/account/admin_two_factor.js +132 -0
  175. package/server/services/account/password_policy.js +6 -1
  176. package/server/services/ai/completion.js +44 -0
  177. package/server/services/ai/engine.js +321 -500
  178. package/server/services/ai/imageAnalysis.js +9 -5
  179. package/server/services/ai/logFormat.js +46 -0
  180. package/server/services/ai/loopPolicy.js +11 -0
  181. package/server/services/ai/messagingFallback.js +228 -0
  182. package/server/services/ai/models.js +194 -55
  183. package/server/services/ai/providerRetry.js +169 -0
  184. package/server/services/ai/providers/anthropic.js +15 -4
  185. package/server/services/ai/providers/google.js +38 -11
  186. package/server/services/ai/providers/grok.js +19 -68
  187. package/server/services/ai/providers/grokOauth.js +141 -0
  188. package/server/services/ai/providers/nvidia.js +154 -0
  189. package/server/services/ai/providers/ollama.js +76 -39
  190. package/server/services/ai/providers/openai.js +20 -31
  191. package/server/services/ai/providers/openaiCodex.js +6 -2
  192. package/server/services/ai/providers/openaiCompatible.js +70 -0
  193. package/server/services/ai/providers/openrouter.js +162 -0
  194. package/server/services/ai/settings.js +30 -0
  195. package/server/services/ai/systemPrompt.js +51 -29
  196. package/server/services/ai/taskAnalysis.js +60 -1
  197. package/server/services/ai/toolEvidence.js +207 -0
  198. package/server/services/ai/toolSelector.js +8 -1
  199. package/server/services/ai/tools.js +80 -15
  200. package/server/services/android/controller.js +36 -1
  201. package/server/services/browser/controller.js +18 -0
  202. package/server/services/browser/extension/manifest.js +33 -0
  203. package/server/services/browser/extension/provider.js +12 -6
  204. package/server/services/browser/extension/registry.js +229 -18
  205. package/server/services/desktop/protocol.js +1 -0
  206. package/server/services/desktop/provider.js +4 -0
  207. package/server/services/desktop/registry.js +104 -1
  208. package/server/services/desktop/screenRecorder.js +208 -98
  209. package/server/services/desktop/screen_recorder_support.js +46 -0
  210. package/server/services/integrations/google/provider.js +13 -0
  211. package/server/services/integrations/home_assistant/constants.js +88 -0
  212. package/server/services/integrations/home_assistant/network.js +207 -0
  213. package/server/services/integrations/home_assistant/provider.js +249 -0
  214. package/server/services/integrations/home_assistant/snapshot.js +98 -0
  215. package/server/services/integrations/home_assistant/tools.js +101 -0
  216. package/server/services/integrations/manager.js +5 -0
  217. package/server/services/integrations/registry.js +2 -0
  218. package/server/services/integrations/trello/provider.js +8 -2
  219. package/server/services/manager.js +51 -47
  220. package/server/services/mcp/client.js +208 -268
  221. package/server/services/mcp/client_support.js +172 -0
  222. package/server/services/mcp/recovery.js +116 -0
  223. package/server/services/mcp/tool_operations.js +123 -0
  224. package/server/services/memory/ingestion.js +185 -370
  225. package/server/services/memory/ingestion_coverage.js +129 -0
  226. package/server/services/memory/ingestion_documents.js +123 -0
  227. package/server/services/memory/ingestion_support.js +171 -0
  228. package/server/services/memory/intelligence.js +181 -0
  229. package/server/services/memory/manager.js +476 -32
  230. package/server/services/messaging/automation.js +71 -134
  231. package/server/services/messaging/formatting_guides.js +26 -1
  232. package/server/services/messaging/inbound_queue.js +111 -0
  233. package/server/services/messaging/manager.js +25 -8
  234. package/server/services/messaging/typing_keepalive.js +110 -0
  235. package/server/services/runtime/manager.js +7 -3
  236. package/server/services/runtime/settings.js +17 -0
  237. package/server/services/streaming/android-stream.js +296 -32
  238. package/server/services/streaming/stream-hub.js +1 -1
  239. package/server/services/tasks/integration_runtime.js +4 -1
  240. package/server/services/tasks/runtime.js +415 -56
  241. package/server/services/tasks/task_repository.js +48 -6
  242. package/server/services/websocket.js +8 -2
  243. package/server/services/widgets/service.js +17 -1
  244. package/server/services/workspace/manager.js +116 -0
  245. package/server/utils/logger.js +44 -6
  246. package/server/utils/security.js +3 -0
  247. package/server/utils/version.js +29 -19
  248. package/server/services/memory/openhuman_uplift.test.js +0 -98
@@ -581,6 +581,9 @@ db.exec(`
581
581
  metadata_json TEXT DEFAULT '{}',
582
582
  content TEXT NOT NULL,
583
583
  importance INTEGER DEFAULT 5,
584
+ confidence REAL DEFAULT 0.7,
585
+ summary TEXT,
586
+ memory_hash TEXT,
584
587
  embedding TEXT,
585
588
  access_count INTEGER DEFAULT 0,
586
589
  archived INTEGER DEFAULT 0,
@@ -686,6 +689,62 @@ db.exec(`
686
689
  CREATE INDEX IF NOT EXISTS idx_memory_ingestion_documents_provider ON memory_ingestion_documents(user_id, agent_id, provider_key, connection_id, updated_at DESC);
687
690
  CREATE INDEX IF NOT EXISTS idx_materialized_knowledge_views_user ON materialized_knowledge_views(user_id, agent_id, view_type, updated_at DESC);
688
691
 
692
+ CREATE TABLE IF NOT EXISTS memory_facts (
693
+ id TEXT PRIMARY KEY,
694
+ memory_id TEXT NOT NULL,
695
+ user_id INTEGER NOT NULL,
696
+ agent_id TEXT,
697
+ subject TEXT NOT NULL,
698
+ predicate TEXT NOT NULL,
699
+ object TEXT NOT NULL,
700
+ category TEXT DEFAULT 'episodic',
701
+ confidence REAL DEFAULT 0.7,
702
+ event_time TEXT,
703
+ metadata_json TEXT DEFAULT '{}',
704
+ created_at TEXT DEFAULT (datetime('now')),
705
+ updated_at TEXT DEFAULT (datetime('now')),
706
+ FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE,
707
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
708
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
709
+ );
710
+
711
+ CREATE TABLE IF NOT EXISTS memory_entities (
712
+ id TEXT PRIMARY KEY,
713
+ user_id INTEGER NOT NULL,
714
+ agent_id TEXT,
715
+ entity_key TEXT NOT NULL,
716
+ name TEXT NOT NULL,
717
+ kind TEXT DEFAULT 'concept',
718
+ aliases_json TEXT DEFAULT '[]',
719
+ summary TEXT DEFAULT '',
720
+ mention_count INTEGER DEFAULT 0,
721
+ first_seen_at TEXT DEFAULT (datetime('now')),
722
+ last_seen_at TEXT DEFAULT (datetime('now')),
723
+ metadata_json TEXT DEFAULT '{}',
724
+ UNIQUE(user_id, agent_id, entity_key),
725
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
726
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
727
+ );
728
+
729
+ CREATE TABLE IF NOT EXISTS memory_entity_mentions (
730
+ entity_id TEXT NOT NULL,
731
+ memory_id TEXT NOT NULL,
732
+ user_id INTEGER NOT NULL,
733
+ agent_id TEXT,
734
+ created_at TEXT DEFAULT (datetime('now')),
735
+ PRIMARY KEY (entity_id, memory_id),
736
+ FOREIGN KEY (entity_id) REFERENCES memory_entities(id) ON DELETE CASCADE,
737
+ FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE,
738
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
739
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
740
+ );
741
+
742
+ CREATE INDEX IF NOT EXISTS idx_memory_facts_memory ON memory_facts(memory_id);
743
+ CREATE INDEX IF NOT EXISTS idx_memory_facts_user ON memory_facts(user_id, agent_id, category, updated_at DESC);
744
+ CREATE INDEX IF NOT EXISTS idx_memory_entities_user ON memory_entities(user_id, agent_id, mention_count DESC, last_seen_at DESC);
745
+ CREATE INDEX IF NOT EXISTS idx_memory_entity_mentions_memory ON memory_entity_mentions(memory_id);
746
+ CREATE INDEX IF NOT EXISTS idx_memory_entity_mentions_entity ON memory_entity_mentions(entity_id);
747
+
689
748
  CREATE TABLE IF NOT EXISTS agent_run_events (
690
749
  id INTEGER PRIMARY KEY AUTOINCREMENT,
691
750
  run_id TEXT NOT NULL,
@@ -984,6 +1043,16 @@ try {
984
1043
  INSERT INTO messages_fts(rowid, content, role, user_id, agent_id, run_id, platform, platform_chat_id)
985
1044
  VALUES (new.id, COALESCE(new.content, ''), new.role, new.user_id, COALESCE(new.agent_id, ''), COALESCE(new.run_id, ''), COALESCE(new.platform, ''), COALESCE(new.platform_chat_id, ''));
986
1045
  END;
1046
+
1047
+ CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
1048
+ memory_id UNINDEXED,
1049
+ user_id UNINDEXED,
1050
+ agent_id UNINDEXED,
1051
+ content,
1052
+ category,
1053
+ entities,
1054
+ tokenize = 'porter unicode61'
1055
+ );
987
1056
  `);
988
1057
  } catch {
989
1058
  // FTS5 is optional. The app still works with LIKE-based fallbacks if unavailable.
@@ -1005,6 +1074,25 @@ try {
1005
1074
  // Ignore cleanup failures for obsolete wearable tables.
1006
1075
  }
1007
1076
 
1077
+ // Admin 2FA tables (singleton — no foreign key, no user_id)
1078
+ db.exec(`
1079
+ CREATE TABLE IF NOT EXISTS admin_two_factor (
1080
+ id INTEGER PRIMARY KEY CHECK (id = 1),
1081
+ enabled INTEGER NOT NULL DEFAULT 0,
1082
+ secret TEXT,
1083
+ pending_secret TEXT,
1084
+ enabled_at TEXT,
1085
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1086
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1087
+ );
1088
+ CREATE TABLE IF NOT EXISTS admin_recovery_codes (
1089
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1090
+ code_hash TEXT NOT NULL,
1091
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1092
+ used_at TEXT
1093
+ );
1094
+ `);
1095
+
1008
1096
  // Migrations for existing databases
1009
1097
  for (const col of [
1010
1098
  "ALTER TABLE agent_runs ADD COLUMN agent_id TEXT",
@@ -1035,6 +1123,9 @@ for (const col of [
1035
1123
  "ALTER TABLE memories ADD COLUMN source_label TEXT",
1036
1124
  "ALTER TABLE memories ADD COLUMN stale_after_days INTEGER",
1037
1125
  "ALTER TABLE memories ADD COLUMN metadata_json TEXT DEFAULT '{}'",
1126
+ "ALTER TABLE memories ADD COLUMN confidence REAL DEFAULT 0.7",
1127
+ "ALTER TABLE memories ADD COLUMN summary TEXT",
1128
+ "ALTER TABLE memories ADD COLUMN memory_hash TEXT",
1038
1129
  "ALTER TABLE scheduled_tasks ADD COLUMN run_at TEXT",
1039
1130
  "ALTER TABLE scheduled_tasks ADD COLUMN one_time INTEGER DEFAULT 0",
1040
1131
  "ALTER TABLE scheduled_tasks ADD COLUMN execution_mode TEXT DEFAULT 'prompt'",
@@ -1094,6 +1185,7 @@ function createAgentScopedIndexes() {
1094
1185
  ['conversation_history', 'CREATE INDEX IF NOT EXISTS idx_conv_history_agent ON conversation_history(user_id, agent_id, created_at DESC)'],
1095
1186
  ['memories', 'CREATE INDEX IF NOT EXISTS idx_memories_agent ON memories(user_id, agent_id, archived, updated_at DESC)'],
1096
1187
  ['memories', 'CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(user_id, agent_id, scope_type, scope_id, archived, updated_at DESC)'],
1188
+ ['memories', 'CREATE INDEX IF NOT EXISTS idx_memories_hash ON memories(user_id, agent_id, memory_hash, archived)'],
1097
1189
  ['core_memory', 'CREATE INDEX IF NOT EXISTS idx_core_memory_agent ON core_memory(user_id, agent_id, key)'],
1098
1190
  ['ai_widgets', 'CREATE UNIQUE INDEX IF NOT EXISTS idx_ai_widgets_system_key ON ai_widgets(user_id, agent_id, system_key) WHERE system_key IS NOT NULL'],
1099
1191
  ];
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ // Shared builder for SQLite FTS5 MATCH expressions. Any code path that feeds a
4
+ // user-supplied string into a `... MATCH ?` query must route it through here:
5
+ // raw input regularly contains characters that are special in FTS5's query
6
+ // grammar and would otherwise throw at query time.
7
+ //
8
+ // Two pitfalls are handled:
9
+ // 1. Hyphens — inside a MATCH a `-` is the NOT/column operator, so a bareword
10
+ // like `covid-19*` parses as `covid AND NOT column "19"` and throws
11
+ // "no such column: 19". The token regex excludes `-`, splitting on it.
12
+ // 2. Bareword operators — an uppercase token such as AND/OR/NOT/NEAR is parsed
13
+ // as an operator ("syntax error near AND"). FTS5's tokenizer is
14
+ // case-insensitive, so lowercasing each token neutralizes the collision
15
+ // while preserving matches.
16
+ //
17
+ // Returns a prefix-matched AND query (e.g. `covid* AND 19*`), or null when the
18
+ // input yields no usable tokens — callers should treat null as "no FTS filter".
19
+
20
+ function buildFtsQuery(query) {
21
+ const tokens = String(query || '')
22
+ .match(/[\p{L}\p{N}_]{2,}/gu) || [];
23
+ if (!tokens.length) return null;
24
+ return tokens.map((token) => `${token.toLowerCase().replace(/"/g, '')}*`).join(' AND ');
25
+ }
26
+
27
+ module.exports = { buildFtsQuery };
@@ -18,6 +18,7 @@ const routeRegistry = [
18
18
  { basePath: '/api/skills', modulePath: '../routes/skills' },
19
19
  { basePath: '/api/store', modulePath: '../routes/store' },
20
20
  { basePath: '/api/artifacts', modulePath: '../routes/artifacts' },
21
+ { basePath: '/api/workspace', modulePath: '../routes/workspace' },
21
22
  { basePath: '/api/memory', modulePath: '../routes/memory' },
22
23
  { basePath: '/api/tasks', modulePath: '../routes/tasks' },
23
24
  { basePath: '/api/widgets', modulePath: '../routes/widgets' },
@@ -7,6 +7,7 @@ const { DATA_DIR } = require('../../runtime/paths');
7
7
  const { requireAuth } = require('../middleware/auth');
8
8
 
9
9
  const FLUTTER_WEB_DIR = path.join(__dirname, '..', 'public');
10
+ const LANDING_DIR = path.join(__dirname, '..', '..', 'landing');
10
11
  const BUILD_ID_PATH = path.join(FLUTTER_WEB_DIR, '.last_build_id');
11
12
 
12
13
  function setNoStoreHeaders(res) {
@@ -63,11 +64,23 @@ function registerStaticRoutes(app) {
63
64
  });
64
65
  });
65
66
 
66
- app.use(express.static(FLUTTER_WEB_DIR, {
67
- index: false,
68
- setHeaders: setFlutterStaticHeaders,
69
- }));
70
- app.get(/^\/(?!api|screenshots|telnyx-audio).*/, serveFlutterApp);
67
+ const adminRouter = require('../routes/admin');
68
+ app.use('/admin', adminRouter);
69
+
70
+ // Flutter app at /app
71
+ app.use(
72
+ '/app',
73
+ express.static(FLUTTER_WEB_DIR, {
74
+ index: false,
75
+ setHeaders: setFlutterStaticHeaders,
76
+ })
77
+ );
78
+ app.get(/^\/app(\/.*)?$/, serveFlutterApp);
79
+
80
+ // Landing page at /
81
+ app.use(express.static(LANDING_DIR));
82
+ app.get('/', (req, res) => res.sendFile(path.join(LANDING_DIR, 'index.html')));
83
+ app.get('/legal.html', (req, res) => res.sendFile(path.join(LANDING_DIR, 'legal.html')));
71
84
  }
72
85
 
73
86
  function serveFlutterApp(req, res) {
@@ -80,11 +93,15 @@ function serveFlutterApp(req, res) {
80
93
  );
81
94
  }
82
95
  setNoStoreHeaders(res);
83
- return res.sendFile(entry);
96
+ // Rewrite <base href="/"> to <base href="/app/"> so Flutter asset paths resolve correctly
97
+ const html = fs.readFileSync(entry, 'utf8').replace('<base href="/">', '<base href="/app/">');
98
+ res.set('Content-Type', 'text/html');
99
+ return res.send(html);
84
100
  }
85
101
 
86
102
  module.exports = {
87
103
  FLUTTER_WEB_DIR,
104
+ LANDING_DIR,
88
105
  registerStaticRoutes,
89
106
  serveFlutterApp
90
107
  };
package/server/index.js CHANGED
@@ -103,7 +103,7 @@ const sessionMiddleware = createSessionMiddleware({
103
103
  });
104
104
  const activeSockets = new Set();
105
105
 
106
- setupConsoleInterceptor(io);
106
+ app.locals.logHistory = setupConsoleInterceptor(io);
107
107
  applyHttpMiddleware(app, {
108
108
  secureCookies: SECURE_COOKIES,
109
109
  trustProxy: TRUST_PROXY,
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+
5
+ /**
6
+ * Constant-time string comparison — prevents timing attacks on API key checks.
7
+ */
8
+ function safeEqual(a, b) {
9
+ if (!a || !b) return false;
10
+ const bufA = Buffer.from(String(a));
11
+ const bufB = Buffer.from(String(b));
12
+ if (bufA.length !== bufB.length) {
13
+ // Lengths differ — do a dummy compare anyway to keep timing consistent
14
+ crypto.timingSafeEqual(bufA, Buffer.alloc(bufA.length));
15
+ return false;
16
+ }
17
+ return crypto.timingSafeEqual(bufA, bufB);
18
+ }
19
+
20
+ /**
21
+ * Allows admin access via either:
22
+ * 1. Browser session (`req.session.isAdmin === true`)
23
+ * 2. API key (`Authorization: Bearer <ADMIN_API_KEY>`)
24
+ *
25
+ * When ADMIN_API_KEY is absent or empty, Bearer auth is disabled entirely.
26
+ */
27
+ function requireAdminAuth(req, res, next) {
28
+ // 1. Session-based auth (admin web dashboard)
29
+ if (req.session?.isAdmin === true) return next();
30
+
31
+ // 2. API key auth (programmatic / scripts)
32
+ const configuredKey = process.env.ADMIN_API_KEY;
33
+ if (configuredKey) {
34
+ const authHeader = String(req.headers.authorization || '');
35
+ if (authHeader.startsWith('Bearer ')) {
36
+ const provided = authHeader.slice(7).trim();
37
+ if (safeEqual(provided, configuredKey)) return next();
38
+ }
39
+ }
40
+
41
+ // 3. Reject
42
+ if (req.path.startsWith('/api/')) {
43
+ return res.status(401).json({ error: 'Admin authentication required' });
44
+ }
45
+ return res.redirect('/admin/login');
46
+ }
47
+
48
+ module.exports = { requireAdminAuth };
@@ -18,43 +18,4 @@ function requireNoAuth(req, res, next) {
18
18
  next();
19
19
  }
20
20
 
21
- function attachUser(req, res, next) {
22
- if (req.session && req.session.userId) {
23
- const db = require('../db/database');
24
- const now = Date.now();
25
- const cacheMaxAgeMs = 5 * 60 * 1000;
26
- const cachedUser = req.session._cachedUser;
27
- const cachedAt = Number(req.session._cachedUserAt || 0);
28
- const cacheFresh = (
29
- cachedUser
30
- && Number(cachedUser.id) === Number(req.session.userId)
31
- && cachedAt > 0
32
- && (now - cachedAt) < cacheMaxAgeMs
33
- );
34
-
35
- if (cacheFresh) {
36
- req.user = { ...cachedUser };
37
- return next();
38
- }
39
-
40
- const user = db.prepare('SELECT id, username, email, created_at FROM users WHERE id = ?').get(req.session.userId);
41
- if (user) {
42
- req.user = { ...user };
43
- req.session._cachedUser = { ...user };
44
- req.session._cachedUserAt = now;
45
- console.log(`[Auth] Attached user ${user.id} (${user.username}) to ${req.method} ${req.originalUrl || req.url}`);
46
- return next();
47
- }
48
-
49
- console.warn(`[Auth] Session user ${req.session.userId} not found for ${req.method} ${req.originalUrl || req.url}; destroying session`);
50
- req.session.destroy(() => {});
51
- const requestPath = req.originalUrl || req.url || req.path || '';
52
- if (requestPath.startsWith('/api/')) {
53
- return res.status(401).json({ error: 'Session invalid' });
54
- }
55
- return res.redirect('/login');
56
- }
57
- next();
58
- }
59
-
60
- module.exports = { requireAuth, requireNoAuth, attachUser };
21
+ module.exports = { requireAuth, requireNoAuth };
@@ -1 +1 @@
1
- f09d02f54b922317d6897b53a29a5868
1
+ dcad55b6198b066ebc14a3b7bb789972
Binary file
@@ -1 +1 @@
1
-
1
+
@@ -1 +1 @@
1
- "DQgHIGFzc2V0cy9icmFuZGluZy9hcHBfaWNvbl8yNTYucG5nDAENAQcFYXNzZXQHIGFzc2V0cy9icmFuZGluZy9hcHBfaWNvbl8yNTYucG5nByRhc3NldHMvYnJhbmRpbmcvb25ib2FyZGluZ19pbnRyby5tcDQMAQ0BBwVhc3NldAckYXNzZXRzL2JyYW5kaW5nL29uYm9hcmRpbmdfaW50cm8ubXA0ByZhc3NldHMvYnJhbmRpbmcvdHJheV9pY29uX3RlbXBsYXRlLnBuZwwBDQEHBWFzc2V0ByZhc3NldHMvYnJhbmRpbmcvdHJheV9pY29uX3RlbXBsYXRlLnBuZwcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYMAQ0BBwVhc3NldAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYHLHBhY2thZ2VzL21peHBhbmVsX2ZsdXR0ZXIvYXNzZXRzL21peHBhbmVsLmpzDAENAQcFYXNzZXQHLHBhY2thZ2VzL21peHBhbmVsX2ZsdXR0ZXIvYXNzZXRzL21peHBhbmVsLmpzBzdwYWNrYWdlcy9yZWNvcmRfd2ViL2Fzc2V0cy9qcy9yZWNvcmQuZml4d2VibWR1cmF0aW9uLmpzDAENAQcFYXNzZXQHN3BhY2thZ2VzL3JlY29yZF93ZWIvYXNzZXRzL2pzL3JlY29yZC5maXh3ZWJtZHVyYXRpb24uanMHL3BhY2thZ2VzL3JlY29yZF93ZWIvYXNzZXRzL2pzL3JlY29yZC53b3JrbGV0LmpzDAENAQcFYXNzZXQHL3BhY2thZ2VzL3JlY29yZF93ZWIvYXNzZXRzL2pzL3JlY29yZC53b3JrbGV0LmpzBxZ3ZWIvaWNvbnMvSWNvbi0xOTIucG5nDAENAQcFYXNzZXQHFndlYi9pY29ucy9JY29uLTE5Mi5wbmc="
1
+ "DQ4HIWFzc2V0cy9icmFuZGluZy9hcHBfaWNvbl8xMDI0LnBuZwwBDQEHBWFzc2V0ByFhc3NldHMvYnJhbmRpbmcvYXBwX2ljb25fMTAyNC5wbmcHIGFzc2V0cy9icmFuZGluZy9hcHBfaWNvbl8yNTYucG5nDAENAQcFYXNzZXQHIGFzc2V0cy9icmFuZGluZy9hcHBfaWNvbl8yNTYucG5nByBhc3NldHMvYnJhbmRpbmcvYXBwX2ljb25fNTEyLnBuZwwBDQEHBWFzc2V0ByBhc3NldHMvYnJhbmRpbmcvYXBwX2ljb25fNTEyLnBuZwcnYXNzZXRzL2JyYW5kaW5nL2FwcF9pY29uX2xpZ2h0XzEwMjQucG5nDAENAQcFYXNzZXQHJ2Fzc2V0cy9icmFuZGluZy9hcHBfaWNvbl9saWdodF8xMDI0LnBuZwcmYXNzZXRzL2JyYW5kaW5nL2FwcF9pY29uX2xpZ2h0XzI1Ni5wbmcMAQ0BBwVhc3NldAcmYXNzZXRzL2JyYW5kaW5nL2FwcF9pY29uX2xpZ2h0XzI1Ni5wbmcHJmFzc2V0cy9icmFuZGluZy9hcHBfaWNvbl9saWdodF81MTIucG5nDAENAQcFYXNzZXQHJmFzc2V0cy9icmFuZGluZy9hcHBfaWNvbl9saWdodF81MTIucG5nByRhc3NldHMvYnJhbmRpbmcvb25ib2FyZGluZ19pbnRyby5tcDQMAQ0BBwVhc3NldAckYXNzZXRzL2JyYW5kaW5nL29uYm9hcmRpbmdfaW50cm8ubXA0Byxhc3NldHMvYnJhbmRpbmcvdHJheV9pY29uX2xpZ2h0X3RlbXBsYXRlLnBuZwwBDQEHBWFzc2V0Byxhc3NldHMvYnJhbmRpbmcvdHJheV9pY29uX2xpZ2h0X3RlbXBsYXRlLnBuZwcmYXNzZXRzL2JyYW5kaW5nL3RyYXlfaWNvbl90ZW1wbGF0ZS5wbmcMAQ0BBwVhc3NldAcmYXNzZXRzL2JyYW5kaW5nL3RyYXlfaWNvbl90ZW1wbGF0ZS5wbmcHMnBhY2thZ2VzL2N1cGVydGlub19pY29ucy9hc3NldHMvQ3VwZXJ0aW5vSWNvbnMudHRmDAENAQcFYXNzZXQHMnBhY2thZ2VzL2N1cGVydGlub19pY29ucy9hc3NldHMvQ3VwZXJ0aW5vSWNvbnMudHRmByxwYWNrYWdlcy9taXhwYW5lbF9mbHV0dGVyL2Fzc2V0cy9taXhwYW5lbC5qcwwBDQEHBWFzc2V0ByxwYWNrYWdlcy9taXhwYW5lbF9mbHV0dGVyL2Fzc2V0cy9taXhwYW5lbC5qcwc3cGFja2FnZXMvcmVjb3JkX3dlYi9hc3NldHMvanMvcmVjb3JkLmZpeHdlYm1kdXJhdGlvbi5qcwwBDQEHBWFzc2V0BzdwYWNrYWdlcy9yZWNvcmRfd2ViL2Fzc2V0cy9qcy9yZWNvcmQuZml4d2VibWR1cmF0aW9uLmpzBy9wYWNrYWdlcy9yZWNvcmRfd2ViL2Fzc2V0cy9qcy9yZWNvcmQud29ya2xldC5qcwwBDQEHBWFzc2V0By9wYWNrYWdlcy9yZWNvcmRfd2ViL2Fzc2V0cy9qcy9yZWNvcmQud29ya2xldC5qcwcWd2ViL2ljb25zL0ljb24tMTkyLnBuZwwBDQEHBWFzc2V0BxZ3ZWIvaWNvbnMvSWNvbi0xOTIucG5n"
Binary file
@@ -1,12 +1,43 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 100 100">
2
2
  <defs>
3
- <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
- <stop offset="0%" stop-color="#8f6d3e"/>
5
- <stop offset="100%" stop-color="#2f7d6e"/>
3
+ <linearGradient id="tile" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#705331"></stop>
5
+ <stop offset="100%" stop-color="#22564c"></stop>
6
6
  </linearGradient>
7
+ <radialGradient id="shine" cx="26%" cy="14%" r="72%">
8
+ <stop offset="0%" stop-color="#ffffff" stop-opacity="0.22"></stop>
9
+ <stop offset="100%" stop-color="#ffffff" stop-opacity="0"></stop>
10
+ </radialGradient>
11
+ <radialGradient id="core" cx="38%" cy="32%" r="75%">
12
+ <stop offset="0%" stop-color="#fffdf6"></stop>
13
+ <stop offset="48%" stop-color="#eee3cc"></stop>
14
+ <stop offset="100%" stop-color="#b5a888"></stop>
15
+ </radialGradient>
16
+ <radialGradient id="node" cx="36%" cy="30%" r="80%">
17
+ <stop offset="0%" stop-color="#f8dca8"></stop>
18
+ <stop offset="52%" stop-color="#d3a85f"></stop>
19
+ <stop offset="100%" stop-color="#9b6f2f"></stop>
20
+ </radialGradient>
21
+ <linearGradient id="tube" x1="0%" y1="0%" x2="0%" y2="100%">
22
+ <stop offset="0%" stop-color="#fffaf0"></stop>
23
+ <stop offset="52%" stop-color="#efe7d6"></stop>
24
+ <stop offset="100%" stop-color="#cabf9f"></stop>
25
+ </linearGradient>
26
+ <filter id="sh" x="-30%" y="-30%" width="160%" height="160%">
27
+ <feDropShadow dx="0" dy="1.1" stdDeviation="1.1" flood-color="#000000" flood-opacity="0.45"></feDropShadow>
28
+ </filter>
29
+ <clipPath id="tc"><rect x="0" y="0" width="100" height="100" rx="22.5"></rect></clipPath>
7
30
  </defs>
8
- <rect x="2.5" y="2.5" width="27" height="27" rx="9" ry="9" fill="url(#bg)" stroke="#ffffff" stroke-opacity="0.16" stroke-width="1"/>
9
- <polygon points="16,5.7 5.2,11.6 16,17.6 26.8,11.6" fill="white"/>
10
- <polyline points="5.2,17.6 16,23.5 26.8,17.6" fill="none" stroke="white" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
11
- <polyline points="5.2,22.7 16,28.1 26.8,22.7" fill="none" stroke="white" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
12
- </svg>
31
+ <g clip-path="url(#tc)">
32
+ <rect x="0" y="0" width="100" height="100" fill="url(#tile)"></rect>
33
+ <rect x="0" y="0" width="100" height="100" fill="url(#shine)"></rect>
34
+ <g fill="none" stroke="url(#tube)" stroke-linecap="round" filter="url(#sh)">
35
+ <circle cx="50" cy="50" r="17" stroke-width="5" stroke-dasharray="78 29" transform="rotate(-35 50 50)" stroke-opacity="0.92"></circle>
36
+ <circle cx="50" cy="50" r="30" stroke-width="5" stroke-dasharray="150 39" transform="rotate(70 50 50)" stroke-opacity="0.8"></circle>
37
+ </g>
38
+ <circle cx="50" cy="50" r="7" fill="url(#core)"></circle>
39
+ <circle cx="50" cy="20" r="5.6" fill="url(#node)"></circle>
40
+ <circle cx="48" cy="18.4" r="1.7" fill="#fff7e6" fill-opacity="0.8"></circle>
41
+ </g>
42
+ <rect x="0.6" y="0.6" width="98.8" height="98.8" rx="22" fill="none" stroke="#ffffff" stroke-opacity="0.16" stroke-width="1.2"></rect>
43
+ </svg>
@@ -0,0 +1,43 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 100 100">
2
+ <defs>
3
+ <linearGradient id="tile" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#705331"></stop>
5
+ <stop offset="100%" stop-color="#22564c"></stop>
6
+ </linearGradient>
7
+ <radialGradient id="shine" cx="26%" cy="14%" r="72%">
8
+ <stop offset="0%" stop-color="#ffffff" stop-opacity="0.22"></stop>
9
+ <stop offset="100%" stop-color="#ffffff" stop-opacity="0"></stop>
10
+ </radialGradient>
11
+ <radialGradient id="core" cx="38%" cy="32%" r="75%">
12
+ <stop offset="0%" stop-color="#fffdf6"></stop>
13
+ <stop offset="48%" stop-color="#eee3cc"></stop>
14
+ <stop offset="100%" stop-color="#b5a888"></stop>
15
+ </radialGradient>
16
+ <radialGradient id="node" cx="36%" cy="30%" r="80%">
17
+ <stop offset="0%" stop-color="#f8dca8"></stop>
18
+ <stop offset="52%" stop-color="#d3a85f"></stop>
19
+ <stop offset="100%" stop-color="#9b6f2f"></stop>
20
+ </radialGradient>
21
+ <linearGradient id="tube" x1="0%" y1="0%" x2="0%" y2="100%">
22
+ <stop offset="0%" stop-color="#fffaf0"></stop>
23
+ <stop offset="52%" stop-color="#efe7d6"></stop>
24
+ <stop offset="100%" stop-color="#cabf9f"></stop>
25
+ </linearGradient>
26
+ <filter id="sh" x="-30%" y="-30%" width="160%" height="160%">
27
+ <feDropShadow dx="0" dy="1.1" stdDeviation="1.1" flood-color="#000000" flood-opacity="0.45"></feDropShadow>
28
+ </filter>
29
+ <clipPath id="tc"><rect x="0" y="0" width="100" height="100" rx="22.5"></rect></clipPath>
30
+ </defs>
31
+ <g clip-path="url(#tc)">
32
+ <rect x="0" y="0" width="100" height="100" fill="url(#tile)"></rect>
33
+ <rect x="0" y="0" width="100" height="100" fill="url(#shine)"></rect>
34
+ <g fill="none" stroke="url(#tube)" stroke-linecap="round" filter="url(#sh)">
35
+ <circle cx="50" cy="50" r="17" stroke-width="5" stroke-dasharray="78 29" transform="rotate(-35 50 50)" stroke-opacity="0.92"></circle>
36
+ <circle cx="50" cy="50" r="30" stroke-width="5" stroke-dasharray="150 39" transform="rotate(70 50 50)" stroke-opacity="0.8"></circle>
37
+ </g>
38
+ <circle cx="50" cy="50" r="7" fill="url(#core)"></circle>
39
+ <circle cx="50" cy="20" r="5.6" fill="url(#node)"></circle>
40
+ <circle cx="48" cy="18.4" r="1.7" fill="#fff7e6" fill-opacity="0.8"></circle>
41
+ </g>
42
+ <rect x="0.6" y="0.6" width="98.8" height="98.8" rx="22" fill="none" stroke="#ffffff" stroke-opacity="0.16" stroke-width="1.2"></rect>
43
+ </svg>
@@ -33,10 +33,10 @@ addEventListener("message", eventListener);
33
33
  if (!window._flutter) {
34
34
  window._flutter = {};
35
35
  }
36
- _flutter.buildConfig = {"engineRevision":"4c525dac5ebe5971c5708ef73558ed8edcf4a362","builds":[{"compileTarget":"dart2js","renderer":"canvaskit","mainJsPath":"main.dart.js"},{}]};
36
+ _flutter.buildConfig = {"engineRevision":"c416acfeb8126e097f758c664aaa3da929e27da0","builds":[{"compileTarget":"dart2js","renderer":"canvaskit","mainJsPath":"main.dart.js"},{}]};
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "1854193544" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "141262336" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
Binary file
Binary file