ltcai 5.5.0 → 6.0.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 (44) hide show
  1. package/README.md +43 -24
  2. package/docs/CHANGELOG.md +69 -0
  3. package/frontend/openapi.json +716 -3
  4. package/frontend/src/api/client.ts +119 -2
  5. package/frontend/src/api/openapi.ts +621 -4
  6. package/frontend/src/components/FirstRunGuide.tsx +3 -3
  7. package/frontend/src/features/review/ReviewCard.tsx +91 -0
  8. package/frontend/src/features/review/ReviewInbox.tsx +112 -0
  9. package/frontend/src/features/review/reviewHelpers.ts +69 -0
  10. package/frontend/src/i18n.ts +8 -8
  11. package/frontend/src/pages/Act.tsx +28 -3
  12. package/frontend/src/routes.ts +2 -0
  13. package/lattice_brain/__init__.py +1 -1
  14. package/lattice_brain/runtime/multi_agent.py +1 -1
  15. package/latticeai/__init__.py +1 -1
  16. package/latticeai/api/review_queue.py +162 -0
  17. package/latticeai/app_factory.py +235 -456
  18. package/latticeai/core/marketplace.py +1 -1
  19. package/latticeai/core/workspace_os.py +86 -1
  20. package/latticeai/runtime/app_context_runtime.py +13 -0
  21. package/latticeai/runtime/automation_runtime.py +64 -0
  22. package/latticeai/runtime/bootstrap.py +48 -0
  23. package/latticeai/runtime/context_runtime.py +43 -0
  24. package/latticeai/runtime/hooks_runtime.py +77 -0
  25. package/latticeai/runtime/lifespan_runtime.py +138 -0
  26. package/latticeai/runtime/persistence_runtime.py +87 -0
  27. package/latticeai/runtime/platform_services_runtime.py +39 -0
  28. package/latticeai/runtime/router_registration.py +570 -0
  29. package/latticeai/runtime/web_runtime.py +65 -0
  30. package/latticeai/services/review_queue.py +271 -0
  31. package/latticeai/services/run_executor.py +33 -0
  32. package/latticeai/services/triggers.py +30 -1
  33. package/package.json +1 -1
  34. package/src-tauri/Cargo.lock +1 -1
  35. package/src-tauri/Cargo.toml +1 -1
  36. package/src-tauri/tauri.conf.json +1 -1
  37. package/static/app/asset-manifest.json +5 -5
  38. package/static/app/assets/index-D2zafMYb.js +16 -0
  39. package/static/app/assets/index-D2zafMYb.js.map +1 -0
  40. package/static/app/assets/index-xRn29gI8.css +2 -0
  41. package/static/app/index.html +2 -2
  42. package/static/app/assets/index-C7vzwUjU.js +0 -16
  43. package/static/app/assets/index-C7vzwUjU.js.map +0 -1
  44. package/static/app/assets/index-HN4f2wbe.css +0 -2
@@ -0,0 +1,570 @@
1
+ """Router registration helpers for app-factory decomposition.
2
+
3
+ The first router extraction step keeps router construction at the existing
4
+ call sites and centralizes only the registration operation. This preserves the
5
+ exact include order while creating a narrow seam for the later
6
+ ``register_routers(app, deps)`` extraction.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+
14
+ def build_static_routes_bundle(
15
+ *,
16
+ create_static_routes_router: Any,
17
+ static_dir: Any,
18
+ invite_gate_enabled: bool,
19
+ invite_code: str,
20
+ app_mode: str,
21
+ model_router: Any,
22
+ require_user: Any,
23
+ ) -> dict[str, Any]:
24
+ """Build static route support while preserving the legacy exported names."""
25
+
26
+ static_routes = create_static_routes_router(
27
+ static_dir=static_dir,
28
+ invite_gate_enabled=invite_gate_enabled,
29
+ invite_code=invite_code,
30
+ app_mode=app_mode,
31
+ model_router=model_router,
32
+ require_user=require_user,
33
+ )
34
+ return {
35
+ "STATIC_ROUTES": static_routes,
36
+ "ui_file_response": static_routes.ui_file_response,
37
+ "local_sysinfo": static_routes.local_sysinfo,
38
+ }
39
+
40
+
41
+ def build_auth_admin_security_router_bundle(
42
+ *,
43
+ create_auth_router: Any,
44
+ load_users: Any,
45
+ save_users: Any,
46
+ hash_password: Any,
47
+ verify_and_migrate_password: Any,
48
+ create_session: Any,
49
+ get_session_email: Any,
50
+ invalidate_session: Any,
51
+ extract_bearer_token: Any,
52
+ get_user_role: Any,
53
+ require_user: Any,
54
+ check_ip_rate_limit: Any,
55
+ client_ip: Any,
56
+ get_sso_settings: Any,
57
+ get_sso_discovery: Any,
58
+ public_sso_config: Any,
59
+ open_registration: bool,
60
+ session_ttl: int,
61
+ require_auth: bool,
62
+ ensure_identity: Any,
63
+ create_admin_router: Any,
64
+ require_admin: Any,
65
+ get_history: Any,
66
+ get_audit_log: Any,
67
+ audit_file: Any,
68
+ public_user: Any,
69
+ load_vpc_config: Any,
70
+ save_vpc_config: Any,
71
+ build_admin_audit_report: Any,
72
+ build_sensitivity_report: Any,
73
+ append_audit_event: Any,
74
+ save_sso_config: Any,
75
+ knowledge_graph: Any,
76
+ enable_graph: bool,
77
+ logger: Any,
78
+ invite_code: str,
79
+ invite_gate_enabled: bool,
80
+ default_port: int,
81
+ policy_matrix: Any,
82
+ build_product_hardening_status: Any,
83
+ config: Any,
84
+ kg_portability: Any,
85
+ device_identity: Any,
86
+ create_invitations_router: Any,
87
+ invitation_store: Any,
88
+ workspace_service: Any,
89
+ user_id_for_email: Any,
90
+ create_security_router: Any,
91
+ classify_sensitive_message: Any,
92
+ ) -> dict[str, Any]:
93
+ """Build auth/admin/security routers and their legacy helper closures."""
94
+
95
+ auth_router = create_auth_router(
96
+ load_users=load_users,
97
+ save_users=save_users,
98
+ hash_password=hash_password,
99
+ verify_and_migrate=verify_and_migrate_password,
100
+ create_session=create_session,
101
+ get_session_email=get_session_email,
102
+ invalidate_session=invalidate_session,
103
+ extract_bearer_token=extract_bearer_token,
104
+ get_user_role=get_user_role,
105
+ require_user=require_user,
106
+ check_ip_rate_limit=check_ip_rate_limit,
107
+ client_ip=client_ip,
108
+ get_sso_settings=get_sso_settings,
109
+ get_sso_discovery=get_sso_discovery,
110
+ public_sso_config=public_sso_config,
111
+ open_registration=open_registration,
112
+ session_ttl=session_ttl,
113
+ require_auth=require_auth,
114
+ ensure_identity=ensure_identity,
115
+ )
116
+
117
+ def graph_stats_safe() -> dict[str, Any]:
118
+ try:
119
+ return knowledge_graph.stats() if (enable_graph and knowledge_graph) else {"disabled": True}
120
+ except Exception as exc: # pragma: no cover - defensive legacy behavior
121
+ return {"error": str(exc)}
122
+
123
+ def product_hardening_status() -> Any:
124
+ return build_product_hardening_status(
125
+ config=config,
126
+ portability=kg_portability,
127
+ device_identity=device_identity,
128
+ )
129
+
130
+ admin_router = create_admin_router(
131
+ require_admin=require_admin,
132
+ require_user=require_user,
133
+ load_users=load_users,
134
+ save_users=save_users,
135
+ get_user_role=get_user_role,
136
+ get_history=get_history,
137
+ get_audit_log=get_audit_log,
138
+ public_user=public_user,
139
+ load_vpc_config=load_vpc_config,
140
+ save_vpc_config=save_vpc_config,
141
+ build_admin_audit_report=build_admin_audit_report,
142
+ build_sensitivity_report=build_sensitivity_report,
143
+ append_audit_event=append_audit_event,
144
+ public_sso_config=public_sso_config,
145
+ save_sso_config=save_sso_config,
146
+ get_graph_stats=graph_stats_safe,
147
+ enable_graph=enable_graph,
148
+ invite_code=invite_code,
149
+ invite_gate_enabled=invite_gate_enabled,
150
+ default_port=default_port,
151
+ policy_matrix=policy_matrix,
152
+ product_hardening_status=product_hardening_status,
153
+ )
154
+ invitations_router = create_invitations_router(
155
+ invitation_store=invitation_store,
156
+ workspace_service=workspace_service,
157
+ require_admin=require_admin,
158
+ require_user=require_user,
159
+ user_id_for_email=user_id_for_email,
160
+ append_audit_event=append_audit_event,
161
+ )
162
+
163
+ def security_audit_events_safe() -> list[dict[str, Any]]:
164
+ try:
165
+ return get_audit_log(audit_file)
166
+ except Exception as exc: # pragma: no cover - defensive legacy behavior
167
+ logger.warning("security audit events load failed: %s", exc)
168
+ return []
169
+
170
+ def security_list_uploaded_files() -> list[dict[str, Any]]:
171
+ files: list[dict[str, Any]] = []
172
+ for idx, event in enumerate(security_audit_events_safe()):
173
+ if event.get("event_type") != "document_upload":
174
+ continue
175
+ files.append({
176
+ "file_id": str(event.get("filename") or idx),
177
+ "filename": event.get("filename"),
178
+ "user_email": event.get("user_email"),
179
+ "user_nickname": event.get("user_nickname"),
180
+ "uploaded_at": event.get("timestamp"),
181
+ "ext": event.get("ext"),
182
+ "bytes": event.get("bytes"),
183
+ "sensitivity": event.get("sensitivity") or "none",
184
+ "sensitive_labels": event.get("sensitive_labels") or [],
185
+ "content_preview": event.get("content_preview"),
186
+ })
187
+ return files
188
+
189
+ security_router = create_security_router(
190
+ require_admin=require_admin,
191
+ get_history=get_history,
192
+ get_audit_events=security_audit_events_safe,
193
+ classify_sensitive_message=classify_sensitive_message,
194
+ build_sensitivity_report=build_sensitivity_report,
195
+ list_uploaded_files=security_list_uploaded_files,
196
+ append_audit_event=append_audit_event,
197
+ )
198
+
199
+ return {
200
+ "auth_router": auth_router,
201
+ "admin_router": admin_router,
202
+ "invitations_router": invitations_router,
203
+ "security_router": security_router,
204
+ "_graph_stats_safe": graph_stats_safe,
205
+ "_product_hardening_status": product_hardening_status,
206
+ "_security_audit_events_safe": security_audit_events_safe,
207
+ "_security_list_uploaded_files": security_list_uploaded_files,
208
+ }
209
+
210
+
211
+ def register_router(app: Any, router: Any) -> Any:
212
+ """Include one router and return it for optional caller-side bookkeeping."""
213
+
214
+ app.include_router(router)
215
+ return router
216
+
217
+
218
+ def register_routers(app: Any, *routers: Any) -> tuple[Any, ...]:
219
+ """Include routers in the given order and return them unchanged."""
220
+
221
+ for router in routers:
222
+ register_router(app, router)
223
+ return routers
224
+
225
+
226
+ def register_foundation_routers(
227
+ app: Any,
228
+ *,
229
+ static_router: Any,
230
+ auth_router: Any,
231
+ admin_router: Any,
232
+ invitations_router: Any,
233
+ security_router: Any,
234
+ create_workspace_router: Any,
235
+ context: Any,
236
+ ) -> tuple[Any, ...]:
237
+ """Register early static/auth/admin/security/workspace routers in order."""
238
+
239
+ workspace_router = create_workspace_router(context)
240
+ return register_routers(
241
+ app,
242
+ static_router,
243
+ auth_router,
244
+ admin_router,
245
+ invitations_router,
246
+ security_router,
247
+ workspace_router,
248
+ )
249
+
250
+
251
+ def register_platform_feature_routers(
252
+ app: Any,
253
+ *,
254
+ create_plugins_router: Any,
255
+ plugin_registry: Any,
256
+ require_user: Any,
257
+ require_admin: Any,
258
+ append_audit_event: Any,
259
+ platform: Any,
260
+ ui_file_response: Any,
261
+ static_dir: Any,
262
+ create_workflow_designer_router: Any,
263
+ store: Any,
264
+ get_current_user: Any,
265
+ workspace_graph: Any,
266
+ hooks: Any,
267
+ run_executor: Any,
268
+ trigger_service: Any,
269
+ create_agents_router: Any,
270
+ agent_runtime: Any,
271
+ create_marketplace_router: Any,
272
+ template_catalog: Any,
273
+ create_realtime_router: Any,
274
+ realtime_bus: Any,
275
+ ) -> tuple[Any, ...]:
276
+ """Register plugin/workflow/agent/marketplace/realtime routes in order."""
277
+
278
+ return register_routers(
279
+ app,
280
+ create_plugins_router(
281
+ registry=plugin_registry,
282
+ require_user=require_user,
283
+ require_admin=require_admin,
284
+ append_audit_event=append_audit_event,
285
+ register_skill=platform.register_plugin_skill,
286
+ plugin_runners_factory=lambda: platform.plugin_capability_runners(None, None),
287
+ ui_file_response=ui_file_response,
288
+ static_dir=static_dir,
289
+ ),
290
+ create_workflow_designer_router(
291
+ store=store,
292
+ require_user=require_user,
293
+ get_current_user=get_current_user,
294
+ gate_read=platform.gate_read,
295
+ gate_write=platform.gate_write,
296
+ workspace_graph=workspace_graph,
297
+ build_runners=platform.build_workflow_runners,
298
+ append_audit_event=append_audit_event,
299
+ ui_file_response=ui_file_response,
300
+ static_dir=static_dir,
301
+ hooks=hooks,
302
+ run_executor=run_executor,
303
+ trigger_service=trigger_service,
304
+ ),
305
+ create_agents_router(
306
+ store=store,
307
+ orchestrator_factory=platform.build_orchestrator,
308
+ require_user=require_user,
309
+ get_current_user=get_current_user,
310
+ gate_read=platform.gate_read,
311
+ gate_write=platform.gate_write,
312
+ workspace_graph=workspace_graph,
313
+ append_audit_event=append_audit_event,
314
+ ui_file_response=ui_file_response,
315
+ static_dir=static_dir,
316
+ agent_runtime=agent_runtime,
317
+ run_executor=run_executor,
318
+ ),
319
+ create_marketplace_router(
320
+ store=store,
321
+ catalog=template_catalog,
322
+ require_user=require_user,
323
+ gate_read=platform.gate_read,
324
+ gate_write=platform.gate_write,
325
+ workspace_graph=workspace_graph,
326
+ ),
327
+ create_realtime_router(
328
+ bus=realtime_bus,
329
+ require_user=require_user,
330
+ get_current_user=get_current_user,
331
+ allowed_scopes=platform.allowed_scopes,
332
+ ui_file_response=ui_file_response,
333
+ static_dir=static_dir,
334
+ ),
335
+ )
336
+
337
+
338
+ def register_health_and_model_routers(
339
+ app: Any,
340
+ *,
341
+ create_health_router: Any,
342
+ model_service: Any,
343
+ engine_status: Any,
344
+ get_current_user: Any,
345
+ require_auth: bool,
346
+ app_version: str,
347
+ app_mode: str,
348
+ create_models_router: Any,
349
+ model_router: Any,
350
+ require_user: Any,
351
+ load_users: Any,
352
+ get_user_role: Any,
353
+ install_engine: Any,
354
+ verify_cloud_models: Any,
355
+ normalize_local_model_request: Any,
356
+ download_hf_model: Any,
357
+ prepare_and_load_model: Any,
358
+ prepare_and_load_model_stream: Any,
359
+ sse_event: Any,
360
+ ensure_ollama_server: Any,
361
+ local_binary: Any,
362
+ filter_lower_family_versions: Any,
363
+ list_compat_profiles: Any,
364
+ set_user_api_key: Any,
365
+ engine_model_catalog: Any,
366
+ model_engine_aliases: Any,
367
+ cloud_verify_ttl_seconds: int,
368
+ is_public_mode: bool,
369
+ allow_local_models: bool,
370
+ ) -> tuple[Any, ...]:
371
+ """Register health and model management routes in legacy order."""
372
+
373
+ return register_routers(
374
+ app,
375
+ create_health_router(
376
+ model_service=model_service,
377
+ engine_status=engine_status,
378
+ get_current_user=get_current_user,
379
+ require_auth=require_auth,
380
+ app_version=app_version,
381
+ app_mode=app_mode,
382
+ ),
383
+ create_models_router(
384
+ model_router=model_router,
385
+ require_user=require_user,
386
+ get_current_user=get_current_user,
387
+ load_users=load_users,
388
+ get_user_role=get_user_role,
389
+ install_engine=install_engine,
390
+ verify_cloud_models=verify_cloud_models,
391
+ normalize_local_model_request=normalize_local_model_request,
392
+ download_hf_model=download_hf_model,
393
+ prepare_and_load_model=prepare_and_load_model,
394
+ prepare_and_load_model_stream=prepare_and_load_model_stream,
395
+ sse_event=sse_event,
396
+ ensure_ollama_server=ensure_ollama_server,
397
+ local_binary=local_binary,
398
+ engine_status=engine_status,
399
+ filter_lower_family_versions=filter_lower_family_versions,
400
+ list_compat_profiles=list_compat_profiles,
401
+ set_user_api_key=set_user_api_key,
402
+ engine_model_catalog=engine_model_catalog,
403
+ model_engine_aliases=model_engine_aliases,
404
+ cloud_verify_ttl_seconds=cloud_verify_ttl_seconds,
405
+ is_public_mode=is_public_mode,
406
+ allow_local_models=allow_local_models,
407
+ require_auth=require_auth,
408
+ ),
409
+ )
410
+
411
+
412
+ def register_interaction_routers(
413
+ app: Any,
414
+ *,
415
+ create_chat_router: Any,
416
+ context: Any,
417
+ create_search_router: Any,
418
+ search_service: Any,
419
+ allowed_workspaces_for: Any,
420
+ require_user: Any,
421
+ embedding_info: Any,
422
+ create_tools_router: Any,
423
+ ingestion_pipeline: Any,
424
+ config: Any,
425
+ data_dir: Any,
426
+ static_dir: Any,
427
+ model_router: Any,
428
+ require_admin: Any,
429
+ get_current_user: Any,
430
+ clear_history: Any,
431
+ append_audit_event: Any,
432
+ enforce_rate_limit: Any,
433
+ bytes_match_extension: Any,
434
+ classify_sensitive_message: Any,
435
+ save_to_history: Any,
436
+ enable_graph: bool,
437
+ knowledge_graph: Any,
438
+ require_graph: Any,
439
+ local_kg_watcher: Any,
440
+ load_mcp_installs: Any,
441
+ recommend_mcps: Any,
442
+ install_mcp: Any,
443
+ mcp_public_item: Any,
444
+ hooks: Any,
445
+ create_hooks_router: Any,
446
+ create_agent_registry_router: Any,
447
+ agent_registry: Any,
448
+ create_memory_router: Any,
449
+ memory_service: Any,
450
+ platform: Any,
451
+ ) -> tuple[Any, ...]:
452
+ """Register chat/search/tools/hooks/registry/memory routes in order."""
453
+
454
+ return register_routers(
455
+ app,
456
+ create_chat_router(context),
457
+ create_search_router(
458
+ service=search_service,
459
+ allowed_workspaces_for=allowed_workspaces_for,
460
+ require_user=require_user,
461
+ embedding_info=embedding_info,
462
+ ),
463
+ create_tools_router(
464
+ ingestion_pipeline=ingestion_pipeline,
465
+ config=config,
466
+ data_dir=data_dir,
467
+ static_dir=static_dir,
468
+ model_router=model_router,
469
+ require_user=require_user,
470
+ require_admin=require_admin,
471
+ get_current_user=get_current_user,
472
+ clear_history=clear_history,
473
+ append_audit_event=append_audit_event,
474
+ enforce_rate_limit=enforce_rate_limit,
475
+ bytes_match_extension=bytes_match_extension,
476
+ classify_sensitive_message=classify_sensitive_message,
477
+ save_to_history=save_to_history,
478
+ enable_graph=enable_graph,
479
+ knowledge_graph=knowledge_graph,
480
+ require_graph=require_graph,
481
+ local_kg_watcher=local_kg_watcher,
482
+ load_mcp_installs=load_mcp_installs,
483
+ recommend_mcps=recommend_mcps,
484
+ install_mcp=install_mcp,
485
+ mcp_public_item=mcp_public_item,
486
+ hooks=hooks,
487
+ ),
488
+ create_hooks_router(
489
+ registry=hooks,
490
+ require_user=require_user,
491
+ append_audit_event=append_audit_event,
492
+ ),
493
+ create_agent_registry_router(
494
+ registry=agent_registry,
495
+ require_user=require_user,
496
+ append_audit_event=append_audit_event,
497
+ ),
498
+ create_memory_router(
499
+ service=memory_service,
500
+ require_user=require_user,
501
+ get_current_user=get_current_user,
502
+ gate_read=platform.gate_read,
503
+ gate_write=platform.gate_write,
504
+ append_audit_event=append_audit_event,
505
+ ),
506
+ )
507
+
508
+
509
+ def register_review_and_brain_tail_routers(
510
+ app: Any,
511
+ *,
512
+ create_review_queue_router: Any,
513
+ review_queue: Any,
514
+ require_user: Any,
515
+ gate_read: Any,
516
+ gate_write: Any,
517
+ run_review_item: Any,
518
+ append_audit_event: Any,
519
+ create_browser_router: Any,
520
+ ingestion_pipeline: Any,
521
+ create_portability_router: Any,
522
+ kg_portability: Any,
523
+ require_admin: Any,
524
+ build_brain_network: Any,
525
+ device_identity: Any,
526
+ data_dir: Any,
527
+ create_network_router: Any,
528
+ create_garden_router: Any,
529
+ gardener: Any,
530
+ create_setup_router: Any,
531
+ model_router: Any,
532
+ ) -> Any:
533
+ """Register the final review/browser/brain tail routes in legacy order."""
534
+
535
+ register_routers(
536
+ app,
537
+ create_review_queue_router(
538
+ service=review_queue,
539
+ require_user=require_user,
540
+ gate_read=gate_read,
541
+ gate_write=gate_write,
542
+ run_review_item=run_review_item,
543
+ append_audit_event=append_audit_event,
544
+ ),
545
+ create_browser_router(
546
+ pipeline=ingestion_pipeline,
547
+ require_user=require_user,
548
+ ),
549
+ create_portability_router(
550
+ service=kg_portability,
551
+ require_user=require_user,
552
+ require_admin=require_admin,
553
+ ),
554
+ )
555
+ brain_network = build_brain_network(
556
+ identity=device_identity,
557
+ portability=kg_portability,
558
+ data_dir=data_dir,
559
+ )
560
+ register_routers(
561
+ app,
562
+ create_network_router(
563
+ network=brain_network,
564
+ identity=device_identity,
565
+ require_user=require_user,
566
+ ),
567
+ create_garden_router(gardener=gardener, require_user=require_user),
568
+ create_setup_router(model_router=model_router, require_user=require_user),
569
+ )
570
+ return brain_network
@@ -0,0 +1,65 @@
1
+ """FastAPI shell assembly for the application factory.
2
+
3
+ This seam owns only the outer web shell: app construction, CORS middleware,
4
+ and static asset mounts. Router registration stays in ``app_factory`` until
5
+ the route snapshot/reorder step is reviewed.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+ from typing import Any, Dict, Iterable
12
+
13
+
14
+ def build_web_runtime(
15
+ *,
16
+ app_mode: str,
17
+ app_version: str,
18
+ lifespan: Any,
19
+ default_host: str,
20
+ default_port: int,
21
+ cors_extra_origins: Iterable[str],
22
+ cors_allow_network: bool,
23
+ static_dir: Path,
24
+ ) -> Dict[str, Any]:
25
+ """Create the FastAPI shell and mount static assets in legacy order."""
26
+
27
+ from fastapi import FastAPI
28
+ from fastapi.middleware.cors import CORSMiddleware
29
+ from fastapi.staticfiles import StaticFiles
30
+
31
+ app = FastAPI(
32
+ title=f"Lattice AI Server ({app_mode})",
33
+ version=app_version,
34
+ lifespan=lifespan,
35
+ )
36
+
37
+ cors_allowed_origins = [
38
+ f"http://localhost:{default_port}",
39
+ f"http://127.0.0.1:{default_port}",
40
+ *cors_extra_origins,
41
+ ]
42
+ if cors_allow_network:
43
+ cors_allowed_origins = cors_allowed_origins + [
44
+ f"http://{default_host}:{default_port}",
45
+ f"https://{default_host}:{default_port}",
46
+ ]
47
+
48
+ app.add_middleware(
49
+ CORSMiddleware,
50
+ allow_origins=cors_allowed_origins,
51
+ allow_methods=["*"],
52
+ allow_headers=["*"],
53
+ allow_credentials=True,
54
+ )
55
+
56
+ static_dir.mkdir(parents=True, exist_ok=True)
57
+ app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
58
+ icons_dir = static_dir / "icons"
59
+ if icons_dir.exists():
60
+ app.mount("/icons", StaticFiles(directory=str(icons_dir)), name="icons")
61
+
62
+ return {
63
+ "app": app,
64
+ "CORS_ALLOWED_ORIGINS": cors_allowed_origins,
65
+ }