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.
- package/.env.example +33 -3
- package/LICENSE +111 -56
- package/README.md +8 -3
- package/docs/configuration.md +8 -0
- package/docs/getting-started.md +9 -3
- package/docs/index.md +4 -0
- package/extensions/chrome-browser/background.mjs +45 -7
- package/extensions/chrome-browser/icons/icon128.png +0 -0
- package/extensions/chrome-browser/icons/icon16.png +0 -0
- package/extensions/chrome-browser/icons/icon48.png +0 -0
- package/extensions/chrome-browser/icons/logo.svg +39 -8
- package/extensions/chrome-browser/manifest.json +3 -2
- package/extensions/chrome-browser/popup.html +5 -1
- package/extensions/chrome-browser/popup.js +15 -2
- package/flutter_app/android/app/src/main/AndroidManifest.xml +2 -1
- package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/flutter_app/assets/branding/app_icon_1024.png +0 -0
- package/flutter_app/assets/branding/app_icon_128.png +0 -0
- package/flutter_app/assets/branding/app_icon_192.png +0 -0
- package/flutter_app/assets/branding/app_icon_256.png +0 -0
- package/flutter_app/assets/branding/app_icon_32.png +0 -0
- package/flutter_app/assets/branding/app_icon_512.png +0 -0
- package/flutter_app/assets/branding/app_icon_64.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_1024.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_128.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_192.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_256.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_32.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_512.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_64.png +0 -0
- package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
- package/flutter_app/assets/branding/tray_icon_light_template.png +0 -0
- package/flutter_app/assets/branding/tray_icon_template.png +0 -0
- package/flutter_app/lib/features/location/location_service.dart +3 -0
- package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +391 -382
- package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +743 -0
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +18 -16
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +19 -18
- package/flutter_app/lib/features/onboarding/onboarding_shell.dart +8 -1
- package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +16 -13
- package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +17 -13
- package/flutter_app/lib/main.dart +3 -0
- package/flutter_app/lib/main_account_settings.dart +10 -34
- package/flutter_app/lib/main_admin.dart +14 -1
- package/flutter_app/lib/main_app_shell.dart +377 -340
- package/flutter_app/lib/main_chat.dart +707 -227
- package/flutter_app/lib/main_controller.dart +338 -46
- package/flutter_app/lib/main_devices.dart +851 -122
- package/flutter_app/lib/main_integrations.dart +255 -6
- package/flutter_app/lib/main_launcher.dart +1 -1
- package/flutter_app/lib/main_model_picker.dart +685 -0
- package/flutter_app/lib/main_models.dart +212 -0
- package/flutter_app/lib/main_navigation.dart +1 -9
- package/flutter_app/lib/main_operations.dart +1417 -614
- package/flutter_app/lib/main_settings.dart +764 -887
- package/flutter_app/lib/main_shared.dart +971 -443
- package/flutter_app/lib/main_spacing.dart +4 -4
- package/flutter_app/lib/main_theme.dart +22 -14
- package/flutter_app/lib/main_unified.dart +5 -72
- package/flutter_app/lib/src/android_apk_drop_zone.dart +24 -0
- package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +13 -0
- package/flutter_app/lib/src/android_apk_drop_zone_web.dart +219 -0
- package/flutter_app/lib/src/backend_client.dart +79 -0
- package/flutter_app/lib/src/desktop_companion_actions.dart +56 -7
- package/flutter_app/lib/src/desktop_companion_io.dart +77 -1
- package/flutter_app/lib/src/desktop_native_bridge.dart +13 -0
- package/flutter_app/lib/src/security/password_strength.dart +84 -0
- package/flutter_app/lib/src/stream_renderer.dart +205 -35
- package/flutter_app/lib/src/theme/palette.dart +76 -34
- package/flutter_app/linux/runner/resources/app_icon.png +0 -0
- package/flutter_app/macos/Runner/AppDelegate.swift +44 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
- package/flutter_app/pubspec.lock +2 -2
- package/flutter_app/pubspec.yaml +7 -1
- package/flutter_app/tool/branding_source/neoagent-icon-1024.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-128.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-16.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-180.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-192.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-256.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-32.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-48.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-512.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon-64.png +0 -0
- package/flutter_app/tool/branding_source/neoagent-icon.svg +43 -0
- package/flutter_app/tool/generate_desktop_branding.py +154 -152
- package/flutter_app/web/favicon.png +0 -0
- package/flutter_app/web/favicon.svg +40 -9
- package/flutter_app/web/favicon_light.svg +43 -0
- package/flutter_app/web/icons/Icon-192-light.png +0 -0
- package/flutter_app/web/icons/Icon-192.png +0 -0
- package/flutter_app/web/icons/Icon-512-light.png +0 -0
- package/flutter_app/web/icons/Icon-512.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-192-light.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-512-light.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
- package/flutter_app/windows/runner/main.cpp +7 -1
- package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
- package/lib/manager.js +445 -83
- package/package.json +17 -3
- package/runtime/paths.js +3 -1
- package/server/admin/access.js +198 -0
- package/server/admin/admin.css +268 -0
- package/server/admin/admin.js +348 -0
- package/server/admin/analytics.js +128 -0
- package/server/admin/index.html +1015 -0
- package/server/admin/login.html +290 -0
- package/server/admin/logo.svg +43 -0
- package/server/admin/sql.js +134 -0
- package/server/admin/users.js +147 -0
- package/server/config/origins.js +3 -1
- package/server/db/database.js +92 -0
- package/server/db/ftsQuery.js +27 -0
- package/server/http/routes.js +1 -0
- package/server/http/static.js +23 -6
- package/server/index.js +1 -1
- package/server/middleware/adminAuth.js +48 -0
- package/server/middleware/auth.js +1 -40
- package/server/public/.last_build_id +1 -1
- package/server/public/app_icon.png +0 -0
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/assets/branding/app_icon_1024.png +0 -0
- package/server/public/assets/assets/branding/app_icon_256.png +0 -0
- package/server/public/assets/assets/branding/app_icon_512.png +0 -0
- package/server/public/assets/assets/branding/app_icon_light_1024.png +0 -0
- package/server/public/assets/assets/branding/app_icon_light_256.png +0 -0
- package/server/public/assets/assets/branding/app_icon_light_512.png +0 -0
- package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
- package/server/public/assets/assets/branding/tray_icon_light_template.png +0 -0
- package/server/public/assets/assets/branding/tray_icon_template.png +0 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/assets/web/icons/Icon-192.png +0 -0
- package/server/public/favicon.png +0 -0
- package/server/public/favicon.svg +40 -9
- package/server/public/favicon_light.svg +43 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/icons/Icon-192-light.png +0 -0
- package/server/public/icons/Icon-192.png +0 -0
- package/server/public/icons/Icon-512-light.png +0 -0
- package/server/public/icons/Icon-512.png +0 -0
- package/server/public/icons/Icon-maskable-192-light.png +0 -0
- package/server/public/icons/Icon-maskable-192.png +0 -0
- package/server/public/icons/Icon-maskable-512-light.png +0 -0
- package/server/public/icons/Icon-maskable-512.png +0 -0
- package/server/public/main.dart.js +88944 -85720
- package/server/routes/admin.js +632 -0
- package/server/routes/agent_profiles.js +3 -0
- package/server/routes/agents.js +1 -1
- package/server/routes/auth.js +3 -1
- package/server/routes/browser.js +14 -0
- package/server/routes/browser_extension.js +21 -4
- package/server/routes/desktop.js +10 -0
- package/server/routes/mcp.js +29 -13
- package/server/routes/memory.js +23 -1
- package/server/routes/messaging.js +2 -0
- package/server/routes/screenHistory.js +14 -8
- package/server/routes/settings.js +14 -2
- package/server/routes/stream.js +12 -3
- package/server/routes/triggers.js +4 -4
- package/server/routes/voice_assistant.js +36 -1
- package/server/routes/workspace.js +86 -0
- package/server/services/account/admin_two_factor.js +132 -0
- package/server/services/account/password_policy.js +6 -1
- package/server/services/ai/completion.js +44 -0
- package/server/services/ai/engine.js +321 -500
- package/server/services/ai/imageAnalysis.js +9 -5
- package/server/services/ai/logFormat.js +46 -0
- package/server/services/ai/loopPolicy.js +11 -0
- package/server/services/ai/messagingFallback.js +228 -0
- package/server/services/ai/models.js +194 -55
- package/server/services/ai/providerRetry.js +169 -0
- package/server/services/ai/providers/anthropic.js +15 -4
- package/server/services/ai/providers/google.js +38 -11
- package/server/services/ai/providers/grok.js +19 -68
- package/server/services/ai/providers/grokOauth.js +141 -0
- package/server/services/ai/providers/nvidia.js +154 -0
- package/server/services/ai/providers/ollama.js +76 -39
- package/server/services/ai/providers/openai.js +20 -31
- package/server/services/ai/providers/openaiCodex.js +6 -2
- package/server/services/ai/providers/openaiCompatible.js +70 -0
- package/server/services/ai/providers/openrouter.js +162 -0
- package/server/services/ai/settings.js +30 -0
- package/server/services/ai/systemPrompt.js +51 -29
- package/server/services/ai/taskAnalysis.js +60 -1
- package/server/services/ai/toolEvidence.js +207 -0
- package/server/services/ai/toolSelector.js +8 -1
- package/server/services/ai/tools.js +80 -15
- package/server/services/android/controller.js +36 -1
- package/server/services/browser/controller.js +18 -0
- package/server/services/browser/extension/manifest.js +33 -0
- package/server/services/browser/extension/provider.js +12 -6
- package/server/services/browser/extension/registry.js +229 -18
- package/server/services/desktop/protocol.js +1 -0
- package/server/services/desktop/provider.js +4 -0
- package/server/services/desktop/registry.js +104 -1
- package/server/services/desktop/screenRecorder.js +208 -98
- package/server/services/desktop/screen_recorder_support.js +46 -0
- package/server/services/integrations/google/provider.js +13 -0
- package/server/services/integrations/home_assistant/constants.js +88 -0
- package/server/services/integrations/home_assistant/network.js +207 -0
- package/server/services/integrations/home_assistant/provider.js +249 -0
- package/server/services/integrations/home_assistant/snapshot.js +98 -0
- package/server/services/integrations/home_assistant/tools.js +101 -0
- package/server/services/integrations/manager.js +5 -0
- package/server/services/integrations/registry.js +2 -0
- package/server/services/integrations/trello/provider.js +8 -2
- package/server/services/manager.js +51 -47
- package/server/services/mcp/client.js +208 -268
- package/server/services/mcp/client_support.js +172 -0
- package/server/services/mcp/recovery.js +116 -0
- package/server/services/mcp/tool_operations.js +123 -0
- package/server/services/memory/ingestion.js +185 -370
- package/server/services/memory/ingestion_coverage.js +129 -0
- package/server/services/memory/ingestion_documents.js +123 -0
- package/server/services/memory/ingestion_support.js +171 -0
- package/server/services/memory/intelligence.js +181 -0
- package/server/services/memory/manager.js +476 -32
- package/server/services/messaging/automation.js +71 -134
- package/server/services/messaging/formatting_guides.js +26 -1
- package/server/services/messaging/inbound_queue.js +111 -0
- package/server/services/messaging/manager.js +25 -8
- package/server/services/messaging/typing_keepalive.js +110 -0
- package/server/services/runtime/manager.js +7 -3
- package/server/services/runtime/settings.js +17 -0
- package/server/services/streaming/android-stream.js +296 -32
- package/server/services/streaming/stream-hub.js +1 -1
- package/server/services/tasks/integration_runtime.js +4 -1
- package/server/services/tasks/runtime.js +415 -56
- package/server/services/tasks/task_repository.js +48 -6
- package/server/services/websocket.js +8 -2
- package/server/services/widgets/service.js +17 -1
- package/server/services/workspace/manager.js +116 -0
- package/server/utils/logger.js +44 -6
- package/server/utils/security.js +3 -0
- package/server/utils/version.js +29 -19
- package/server/services/memory/openhuman_uplift.test.js +0 -98
|
@@ -188,7 +188,9 @@ class OfficialIntegrationsTab extends StatelessWidget {
|
|
|
188
188
|
!item.env.configured
|
|
189
189
|
? item.env.summary
|
|
190
190
|
: item.hasExpiredAccounts
|
|
191
|
-
?
|
|
191
|
+
? item.id == 'google_workspace'
|
|
192
|
+
? 'One or more accounts expired. Reconnect to restore access. If this keeps happening, your Google Cloud OAuth app may be in Testing mode — publish it to Production in Google Cloud Console to get long-lived tokens.'
|
|
193
|
+
: 'One or more accounts expired. Reconnect the affected account to restore tool access.'
|
|
192
194
|
: !item.supportsMultipleAccounts && item.isConnected
|
|
193
195
|
? 'This integration currently supports one connected account per agent. Re-open setup to replace it.'
|
|
194
196
|
: item.isConnected
|
|
@@ -226,12 +228,255 @@ void _openOfficialIntegrationSetupDialog(
|
|
|
226
228
|
String providerId,
|
|
227
229
|
) {
|
|
228
230
|
switch (providerId) {
|
|
231
|
+
case 'home_assistant':
|
|
232
|
+
_showHomeAssistantSetupDialog(context, controller);
|
|
233
|
+
return;
|
|
229
234
|
case 'trello':
|
|
230
235
|
_showTrelloSetupDialog(context, controller);
|
|
231
236
|
return;
|
|
232
237
|
}
|
|
233
238
|
}
|
|
234
239
|
|
|
240
|
+
Future<void> _showHomeAssistantSetupDialog(
|
|
241
|
+
BuildContext context,
|
|
242
|
+
NeoAgentController controller,
|
|
243
|
+
) async {
|
|
244
|
+
Map<String, dynamic> existing;
|
|
245
|
+
try {
|
|
246
|
+
existing = await controller.getOfficialIntegrationConfig('home_assistant');
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (context.mounted) {
|
|
249
|
+
ScaffoldMessenger.of(context).showSnackBar(
|
|
250
|
+
SnackBar(content: Text(controller.errorMessage ?? error.toString())),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
final savedBaseUrl = existing['baseUrl']?.toString() ?? '';
|
|
257
|
+
final hasToken = existing['hasToken'] == true;
|
|
258
|
+
final accountCount = (existing['accountCount'] as num?)?.toInt() ?? 0;
|
|
259
|
+
final hasConnectedAccount =
|
|
260
|
+
existing['hasConnectedAccount'] == true || accountCount > 0;
|
|
261
|
+
var formError = '';
|
|
262
|
+
var saving = false;
|
|
263
|
+
|
|
264
|
+
final baseUrlController = TextEditingController(text: savedBaseUrl);
|
|
265
|
+
final tokenController = TextEditingController();
|
|
266
|
+
|
|
267
|
+
if (!context.mounted) return;
|
|
268
|
+
await showDialog<void>(
|
|
269
|
+
context: context,
|
|
270
|
+
barrierDismissible: false,
|
|
271
|
+
builder: (dialogContext) {
|
|
272
|
+
return StatefulBuilder(
|
|
273
|
+
builder: (dialogContext, setState) {
|
|
274
|
+
return AlertDialog(
|
|
275
|
+
title: const Text('Home Assistant Setup'),
|
|
276
|
+
content: SizedBox(
|
|
277
|
+
width: 520,
|
|
278
|
+
child: Column(
|
|
279
|
+
mainAxisSize: MainAxisSize.min,
|
|
280
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
281
|
+
children: <Widget>[
|
|
282
|
+
Text(
|
|
283
|
+
'Connect a public HTTPS Home Assistant endpoint with a Long-Lived Access Token. Local, loopback, and private network addresses are blocked by the server.',
|
|
284
|
+
style: TextStyle(color: _textSecondary),
|
|
285
|
+
),
|
|
286
|
+
const SizedBox(height: 16),
|
|
287
|
+
_IntegrationSetupStatusItem(
|
|
288
|
+
label: 'Endpoint',
|
|
289
|
+
status: savedBaseUrl.trim().isNotEmpty
|
|
290
|
+
? 'Configured'
|
|
291
|
+
: 'Not configured',
|
|
292
|
+
isConnected: savedBaseUrl.trim().isNotEmpty,
|
|
293
|
+
),
|
|
294
|
+
const SizedBox(height: 12),
|
|
295
|
+
_IntegrationSetupStatusItem(
|
|
296
|
+
label: 'Connected Instance',
|
|
297
|
+
status: hasConnectedAccount
|
|
298
|
+
? '$accountCount ${accountCount == 1 ? 'instance' : 'instances'} connected'
|
|
299
|
+
: 'Not connected',
|
|
300
|
+
isConnected: hasConnectedAccount,
|
|
301
|
+
),
|
|
302
|
+
const SizedBox(height: 12),
|
|
303
|
+
TextField(
|
|
304
|
+
controller: baseUrlController,
|
|
305
|
+
onChanged: (_) => setState(() {}),
|
|
306
|
+
keyboardType: TextInputType.url,
|
|
307
|
+
decoration: const InputDecoration(
|
|
308
|
+
labelText: 'Home Assistant URL',
|
|
309
|
+
hintText: 'https://your-instance.example.com',
|
|
310
|
+
border: OutlineInputBorder(),
|
|
311
|
+
),
|
|
312
|
+
),
|
|
313
|
+
const SizedBox(height: 12),
|
|
314
|
+
TextField(
|
|
315
|
+
controller: tokenController,
|
|
316
|
+
onChanged: (_) => setState(() {}),
|
|
317
|
+
obscureText: true,
|
|
318
|
+
decoration: InputDecoration(
|
|
319
|
+
labelText: hasToken
|
|
320
|
+
? 'Paste replacement Long-Lived Access Token'
|
|
321
|
+
: 'Long-Lived Access Token',
|
|
322
|
+
border: const OutlineInputBorder(),
|
|
323
|
+
),
|
|
324
|
+
),
|
|
325
|
+
if (hasToken) ...<Widget>[
|
|
326
|
+
const SizedBox(height: 8),
|
|
327
|
+
Text(
|
|
328
|
+
'Leave the token empty to keep the currently stored token.',
|
|
329
|
+
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
330
|
+
),
|
|
331
|
+
],
|
|
332
|
+
if (formError.isNotEmpty) ...<Widget>[
|
|
333
|
+
const SizedBox(height: 12),
|
|
334
|
+
Container(
|
|
335
|
+
padding: const EdgeInsets.all(8),
|
|
336
|
+
decoration: BoxDecoration(
|
|
337
|
+
color: _danger.withValues(alpha: 0.1),
|
|
338
|
+
borderRadius: BorderRadius.circular(4),
|
|
339
|
+
border: Border.all(
|
|
340
|
+
color: _danger.withValues(alpha: 0.3),
|
|
341
|
+
),
|
|
342
|
+
),
|
|
343
|
+
child: Text(
|
|
344
|
+
formError,
|
|
345
|
+
style: TextStyle(color: _danger, fontSize: 12),
|
|
346
|
+
),
|
|
347
|
+
),
|
|
348
|
+
],
|
|
349
|
+
],
|
|
350
|
+
),
|
|
351
|
+
),
|
|
352
|
+
actions: <Widget>[
|
|
353
|
+
if (savedBaseUrl.trim().isNotEmpty || hasToken)
|
|
354
|
+
TextButton(
|
|
355
|
+
onPressed: saving
|
|
356
|
+
? null
|
|
357
|
+
: () async {
|
|
358
|
+
final shouldClear =
|
|
359
|
+
await showDialog<bool>(
|
|
360
|
+
context: dialogContext,
|
|
361
|
+
builder: (context) {
|
|
362
|
+
return AlertDialog(
|
|
363
|
+
title: const Text(
|
|
364
|
+
'Disconnect Home Assistant?',
|
|
365
|
+
),
|
|
366
|
+
content: const Text(
|
|
367
|
+
'This removes the Home Assistant setup and connected instance for this agent.',
|
|
368
|
+
),
|
|
369
|
+
actions: [
|
|
370
|
+
TextButton(
|
|
371
|
+
onPressed: () =>
|
|
372
|
+
Navigator.of(context).pop(false),
|
|
373
|
+
child: const Text('Cancel'),
|
|
374
|
+
),
|
|
375
|
+
FilledButton(
|
|
376
|
+
onPressed: () =>
|
|
377
|
+
Navigator.of(context).pop(true),
|
|
378
|
+
child: const Text('Disconnect'),
|
|
379
|
+
),
|
|
380
|
+
],
|
|
381
|
+
);
|
|
382
|
+
},
|
|
383
|
+
) ??
|
|
384
|
+
false;
|
|
385
|
+
if (!shouldClear) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
setState(() {
|
|
389
|
+
formError = '';
|
|
390
|
+
saving = true;
|
|
391
|
+
});
|
|
392
|
+
try {
|
|
393
|
+
await controller.clearOfficialIntegrationConfig(
|
|
394
|
+
'home_assistant',
|
|
395
|
+
);
|
|
396
|
+
if (dialogContext.mounted) {
|
|
397
|
+
Navigator.of(dialogContext).pop();
|
|
398
|
+
}
|
|
399
|
+
} catch (_) {
|
|
400
|
+
setState(() {
|
|
401
|
+
formError =
|
|
402
|
+
controller.errorMessage ??
|
|
403
|
+
'Could not disconnect Home Assistant.';
|
|
404
|
+
saving = false;
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
child: const Text('Disconnect'),
|
|
409
|
+
),
|
|
410
|
+
TextButton(
|
|
411
|
+
onPressed: saving
|
|
412
|
+
? null
|
|
413
|
+
: () => Navigator.of(dialogContext).pop(),
|
|
414
|
+
child: const Text('Close'),
|
|
415
|
+
),
|
|
416
|
+
FilledButton(
|
|
417
|
+
onPressed: saving
|
|
418
|
+
? null
|
|
419
|
+
: () async {
|
|
420
|
+
setState(() {
|
|
421
|
+
formError = '';
|
|
422
|
+
saving = true;
|
|
423
|
+
});
|
|
424
|
+
try {
|
|
425
|
+
final baseUrl = baseUrlController.text.trim();
|
|
426
|
+
final token = tokenController.text.trim();
|
|
427
|
+
if (baseUrl.isEmpty) {
|
|
428
|
+
setState(() {
|
|
429
|
+
formError = 'Home Assistant URL is required.';
|
|
430
|
+
saving = false;
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (token.isEmpty && !hasToken) {
|
|
435
|
+
setState(() {
|
|
436
|
+
formError =
|
|
437
|
+
'Home Assistant Long-Lived Access Token is required.';
|
|
438
|
+
saving = false;
|
|
439
|
+
});
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
await controller.saveOfficialIntegrationConfig(
|
|
443
|
+
'home_assistant',
|
|
444
|
+
config: <String, dynamic>{
|
|
445
|
+
'baseUrl': baseUrl,
|
|
446
|
+
if (token.isNotEmpty) 'token': token,
|
|
447
|
+
},
|
|
448
|
+
);
|
|
449
|
+
if (dialogContext.mounted) {
|
|
450
|
+
Navigator.of(dialogContext).pop();
|
|
451
|
+
}
|
|
452
|
+
} catch (_) {
|
|
453
|
+
setState(() {
|
|
454
|
+
formError =
|
|
455
|
+
controller.errorMessage ??
|
|
456
|
+
'Could not save Home Assistant setup.';
|
|
457
|
+
saving = false;
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
child: Text(
|
|
462
|
+
saving
|
|
463
|
+
? 'Saving...'
|
|
464
|
+
: hasConnectedAccount
|
|
465
|
+
? 'Update Instance'
|
|
466
|
+
: 'Connect Instance',
|
|
467
|
+
),
|
|
468
|
+
),
|
|
469
|
+
],
|
|
470
|
+
);
|
|
471
|
+
},
|
|
472
|
+
);
|
|
473
|
+
},
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
baseUrlController.dispose();
|
|
477
|
+
tokenController.dispose();
|
|
478
|
+
}
|
|
479
|
+
|
|
235
480
|
Future<void> _showTrelloSetupDialog(
|
|
236
481
|
BuildContext context,
|
|
237
482
|
NeoAgentController controller,
|
|
@@ -281,13 +526,13 @@ Future<void> _showTrelloSetupDialog(
|
|
|
281
526
|
style: TextStyle(color: _textSecondary),
|
|
282
527
|
),
|
|
283
528
|
const SizedBox(height: 16),
|
|
284
|
-
|
|
529
|
+
_IntegrationSetupStatusItem(
|
|
285
530
|
label: 'API Key',
|
|
286
531
|
status: apiKeyConfigured ? 'Configured' : 'Not configured',
|
|
287
532
|
isConnected: apiKeyConfigured,
|
|
288
533
|
),
|
|
289
534
|
const SizedBox(height: 12),
|
|
290
|
-
|
|
535
|
+
_IntegrationSetupStatusItem(
|
|
291
536
|
label: 'Connected Account',
|
|
292
537
|
status: hasConnectedAccount
|
|
293
538
|
? '$accountCount ${accountCount == 1 ? 'connected account' : 'connected accounts'}'
|
|
@@ -335,7 +580,9 @@ Future<void> _showTrelloSetupDialog(
|
|
|
335
580
|
decoration: BoxDecoration(
|
|
336
581
|
color: _danger.withValues(alpha: 0.1),
|
|
337
582
|
borderRadius: BorderRadius.circular(4),
|
|
338
|
-
border: Border.all(
|
|
583
|
+
border: Border.all(
|
|
584
|
+
color: _danger.withValues(alpha: 0.3),
|
|
585
|
+
),
|
|
339
586
|
),
|
|
340
587
|
child: Text(
|
|
341
588
|
formError,
|
|
@@ -530,8 +777,8 @@ Future<void> _showTrelloSetupDialog(
|
|
|
530
777
|
tokenInputController.dispose();
|
|
531
778
|
}
|
|
532
779
|
|
|
533
|
-
class
|
|
534
|
-
const
|
|
780
|
+
class _IntegrationSetupStatusItem extends StatelessWidget {
|
|
781
|
+
const _IntegrationSetupStatusItem({
|
|
535
782
|
required this.label,
|
|
536
783
|
required this.status,
|
|
537
784
|
required this.isConnected,
|
|
@@ -884,11 +1131,13 @@ class _OfficialIntegrationIcon extends StatelessWidget {
|
|
|
884
1131
|
Widget build(BuildContext context) {
|
|
885
1132
|
final color = switch (item.icon) {
|
|
886
1133
|
'google' => const Color(0xFF4285F4),
|
|
1134
|
+
'home_assistant' => const Color(0xFF41BDF5),
|
|
887
1135
|
'trello' => const Color(0xFF0C66E4),
|
|
888
1136
|
_ => _accent,
|
|
889
1137
|
};
|
|
890
1138
|
final label = switch (item.icon) {
|
|
891
1139
|
'google' => 'G',
|
|
1140
|
+
'home_assistant' => 'H',
|
|
892
1141
|
'trello' => 'T',
|
|
893
1142
|
_ => item.label.isNotEmpty ? item.label[0] : '?',
|
|
894
1143
|
};
|
|
@@ -822,7 +822,7 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
822
822
|
const SizedBox(height: 10),
|
|
823
823
|
Text(
|
|
824
824
|
controller.backendUrl,
|
|
825
|
-
style: GoogleFonts.
|
|
825
|
+
style: GoogleFonts.geistMono(
|
|
826
826
|
fontSize: 12,
|
|
827
827
|
color: _textSecondary,
|
|
828
828
|
),
|