ltcai 11.2.0 → 11.4.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.
- package/README.md +46 -53
- package/docs/CHANGELOG.md +61 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/MULTI_AGENT_RUNTIME.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +6 -2
- package/docs/PERMISSION_MODE.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/kg-schema.md +2 -2
- package/docs/v11.3.0_PLAN.md +202 -0
- package/docs/v11.4.0_RUST_FOUNDATION_PLAN.md +176 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/_kg_common/__init__.py +287 -0
- package/lattice_brain/graph/_kg_common/extraction.py +516 -0
- package/lattice_brain/graph/_kg_common/relations.py +161 -0
- package/lattice_brain/graph/_kg_common/text.py +479 -0
- package/lattice_brain/graph/discovery_index/__init__.py +35 -0
- package/lattice_brain/graph/discovery_index/cleanup.py +182 -0
- package/lattice_brain/graph/discovery_index/extract.py +137 -0
- package/lattice_brain/graph/discovery_index/scan.py +411 -0
- package/lattice_brain/graph/discovery_index/upsert.py +495 -0
- package/lattice_brain/graph/projection/__init__.py +42 -0
- package/lattice_brain/graph/projection/curation.py +500 -0
- package/lattice_brain/graph/{projection.py → projection/v2_schema.py} +15 -477
- package/lattice_brain/graph/retrieval/__init__.py +54 -0
- package/lattice_brain/graph/retrieval/context.py +197 -0
- package/lattice_brain/graph/retrieval/graph_view.py +319 -0
- package/lattice_brain/graph/retrieval/hybrid.py +488 -0
- package/lattice_brain/graph/retrieval/maintenance.py +121 -0
- package/lattice_brain/graph/retrieval/signals.py +95 -0
- package/lattice_brain/graph/retrieval_vector/__init__.py +42 -0
- package/lattice_brain/graph/retrieval_vector/fingerprint.py +97 -0
- package/lattice_brain/graph/retrieval_vector/indexing.py +347 -0
- package/lattice_brain/graph/retrieval_vector/search.py +560 -0
- package/lattice_brain/graph/retrieval_vector/status.py +374 -0
- package/lattice_brain/ingestion/__init__.py +130 -0
- package/lattice_brain/ingestion/_contract.py +90 -0
- package/lattice_brain/ingestion/constants.py +127 -0
- package/lattice_brain/ingestion/folder_scan.py +57 -0
- package/lattice_brain/ingestion/folders.py +258 -0
- package/lattice_brain/ingestion/hashing.py +26 -0
- package/lattice_brain/ingestion/jobs_api.py +107 -0
- package/lattice_brain/ingestion/models.py +80 -0
- package/lattice_brain/ingestion/pipeline.py +486 -0
- package/lattice_brain/ingestion/quality.py +209 -0
- package/lattice_brain/ingestion/routing.py +295 -0
- package/lattice_brain/multimodal/__init__.py +164 -0
- package/lattice_brain/multimodal/audio.py +77 -0
- package/lattice_brain/multimodal/common.py +118 -0
- package/lattice_brain/multimodal/images.py +498 -0
- package/lattice_brain/multimodal/ports.py +169 -0
- package/lattice_brain/multimodal/video.py +410 -0
- package/lattice_brain/portability/__init__.py +90 -0
- package/lattice_brain/portability/_contract.py +42 -0
- package/lattice_brain/portability/backups.py +338 -0
- package/lattice_brain/portability/bundles.py +136 -0
- package/lattice_brain/portability/constants.py +93 -0
- package/lattice_brain/portability/fsops.py +138 -0
- package/lattice_brain/portability/service.py +41 -0
- package/lattice_brain/{portability.py → portability/sharing.py} +44 -677
- package/lattice_brain/runtime/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/chronicle.py +63 -0
- package/latticeai/core/agent/__init__.py +93 -0
- package/latticeai/core/agent/_contract.py +79 -0
- package/latticeai/core/agent/context.py +57 -0
- package/latticeai/core/agent/deps.py +125 -0
- package/latticeai/core/agent/execution.py +622 -0
- package/latticeai/core/agent/planning.py +145 -0
- package/latticeai/core/agent/recovery.py +157 -0
- package/latticeai/core/agent/runtime.py +210 -0
- package/latticeai/core/agent/verification.py +231 -0
- package/latticeai/core/embedding_providers/__init__.py +151 -0
- package/latticeai/core/embedding_providers/base.py +199 -0
- package/latticeai/core/embedding_providers/captions.py +162 -0
- package/latticeai/core/embedding_providers/profiles.py +126 -0
- package/latticeai/core/embedding_providers/text.py +350 -0
- package/latticeai/core/embedding_providers/vision.py +352 -0
- package/latticeai/core/file_generation/__init__.py +115 -0
- package/latticeai/core/file_generation/bundles.py +76 -0
- package/latticeai/core/file_generation/extraction.py +154 -0
- package/latticeai/core/file_generation/inference.py +235 -0
- package/latticeai/core/file_generation/orchestration.py +152 -0
- package/latticeai/core/file_generation/prompting.py +117 -0
- package/latticeai/core/file_generation/repair.py +114 -0
- package/latticeai/core/file_generation/sanitize.py +61 -0
- package/latticeai/core/file_generation/validation.py +201 -0
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/messages.py +9 -0
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/integrations/telegram_bot/__init__.py +123 -0
- package/latticeai/integrations/telegram_bot/__main__.py +17 -0
- package/latticeai/integrations/telegram_bot/config.py +86 -0
- package/latticeai/integrations/telegram_bot/dispatch.py +311 -0
- package/latticeai/integrations/telegram_bot/flows.py +478 -0
- package/latticeai/integrations/telegram_bot/helpers.py +322 -0
- package/latticeai/integrations/telegram_bot/screens.py +394 -0
- package/latticeai/models/router/__init__.py +88 -0
- package/latticeai/models/router/_contract.py +66 -0
- package/latticeai/models/router/branding.py +56 -0
- package/latticeai/models/router/catalog.py +69 -0
- package/latticeai/models/router/documents.py +199 -0
- package/latticeai/models/router/errors.py +37 -0
- package/latticeai/models/router/generation.py +258 -0
- package/latticeai/models/router/loading.py +291 -0
- package/latticeai/models/router/local_models.py +85 -0
- package/latticeai/models/router/registry.py +147 -0
- package/latticeai/runtime/build_phases/__init__.py +82 -0
- package/latticeai/runtime/build_phases/features.py +407 -0
- package/latticeai/runtime/build_phases/foundation.py +555 -0
- package/latticeai/runtime/build_phases/web.py +492 -0
- package/latticeai/runtime/runtime_context.py +1 -0
- package/latticeai/services/architecture_readiness.py +48 -19
- package/latticeai/services/brain_intelligence/__init__.py +58 -0
- package/latticeai/services/brain_intelligence/_contract.py +71 -0
- package/latticeai/services/brain_intelligence/consistency.py +193 -0
- package/latticeai/services/brain_intelligence/constants.py +47 -0
- package/latticeai/services/brain_intelligence/digest.py +258 -0
- package/latticeai/services/brain_intelligence/health.py +331 -0
- package/latticeai/services/brain_intelligence/proposals.py +264 -0
- package/latticeai/services/brain_intelligence/sampling.py +84 -0
- package/latticeai/services/brain_intelligence/service.py +48 -0
- package/latticeai/services/chronicle.py +557 -0
- package/latticeai/services/memory_service/__init__.py +52 -0
- package/latticeai/services/memory_service/_contract.py +100 -0
- package/latticeai/services/memory_service/brief.py +431 -0
- package/latticeai/services/memory_service/constants.py +57 -0
- package/latticeai/services/memory_service/maintenance.py +138 -0
- package/latticeai/services/memory_service/manager.py +186 -0
- package/latticeai/services/memory_service/proof.py +136 -0
- package/latticeai/services/memory_service/recall.py +225 -0
- package/latticeai/services/memory_service/service.py +48 -0
- package/latticeai/services/memory_service/stores.py +110 -0
- package/latticeai/services/model_runtime/__init__.py +322 -0
- package/latticeai/services/model_runtime/cloud.py +87 -0
- package/latticeai/services/model_runtime/download.py +282 -0
- package/latticeai/services/model_runtime/engines.py +341 -0
- package/latticeai/services/model_runtime/loading.py +178 -0
- package/latticeai/services/model_runtime/service.py +129 -0
- package/latticeai/services/model_runtime/state.py +131 -0
- package/latticeai/services/model_runtime/status.py +255 -0
- package/latticeai/services/product_readiness.py +15 -7
- package/latticeai/setup/wizard/__init__.py +126 -0
- package/latticeai/setup/wizard/catalog.py +172 -0
- package/latticeai/setup/wizard/detect.py +323 -0
- package/latticeai/setup/wizard/install.py +348 -0
- package/latticeai/setup/wizard/paths.py +168 -0
- package/latticeai/setup/wizard/plans.py +74 -0
- package/latticeai/setup/wizard/recommend.py +320 -0
- package/package.json +6 -2
- package/scripts/bump_version.py +14 -0
- package/scripts/capture_release_evidence.mjs +33 -21
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_i18n_namespace_coverage.mjs +41 -4
- package/scripts/check_max_file_lines.mjs +102 -0
- package/scripts/check_release_evidence_bound.mjs +30 -15
- package/scripts/check_screenshot_pixel_delta.py +34 -4
- package/scripts/check_server_i18n.mjs +1 -0
- package/scripts/generate_rust_parity_fixtures.py +562 -0
- package/scripts/lib/mock_server_fingerprint.mjs +94 -0
- package/scripts/release_screen_claims.json +31 -2
- package/src-tauri/Cargo.lock +361 -3
- package/src-tauri/Cargo.toml +6 -1
- package/src-tauri/src/backend.rs +349 -0
- package/src-tauri/src/folder.rs +33 -0
- package/src-tauri/src/main.rs +97 -399
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +41 -37
- package/static/app/assets/Act-yYpYnn0v.js +1 -0
- package/static/app/assets/AdminConsole-DL3Cr5pL.js +1 -0
- package/static/app/assets/{Brain-tuhI4sOC.js → Brain-C1HBN0Wf.js} +2 -2
- package/static/app/assets/BrainHome-DoXRhUUC.js +2 -0
- package/static/app/assets/BrainSignals-6yR6ir5t.js +1 -0
- package/static/app/assets/Capture-CFIRsFNE.js +1 -0
- package/static/app/assets/Chronicle-BZbEgiwN.js +1 -0
- package/static/app/assets/CommandPalette-D2pMxC2I.js +1 -0
- package/static/app/assets/Library-DwO3yZST.js +1 -0
- package/static/app/assets/{LivingBrain-DBwhto14.js → LivingBrain-Jn1GK0-S.js} +1 -1
- package/static/app/assets/ProductFlow-B-w1R4Oo.js +1 -0
- package/static/app/assets/ReviewCard-6B27X8Vg.js +3 -0
- package/static/app/assets/System-DW8F-2xL.js +1 -0
- package/static/app/assets/arrow-left-DXvKg9U6.js +1 -0
- package/static/app/assets/{bot-Cia42c2h.js → bot-IM_E_Y12.js} +1 -1
- package/static/app/assets/brain-Ci1CkWjM.js +1 -0
- package/static/app/assets/{button-2j2Ijzgq.js → button-COwyqfHM.js} +1 -1
- package/static/app/assets/circle-check-DfInj-qD.js +1 -0
- package/static/app/assets/{circle-pause-BEFeWpVW.js → circle-pause-DEM4A1Y5.js} +1 -1
- package/static/app/assets/{circle-play-ujXMcHxl.js → circle-play-C9djDuLd.js} +1 -1
- package/static/app/assets/{cpu-k4awryFq.js → cpu-DFdo1gw-.js} +1 -1
- package/static/app/assets/{download-DFbLJ_ig.js → download-SnJL6oqk.js} +1 -1
- package/static/app/assets/{folder-open-7y_b6xkM.js → folder-open-CqZeDkjE.js} +1 -1
- package/static/app/assets/{hard-drive-Bidh02Kr.js → hard-drive-j1jJXYYf.js} +1 -1
- package/static/app/assets/{index-DwDl9-8Y.css → index-BLPb5lmE.css} +1 -1
- package/static/app/assets/index-_u5iUHDr.js +10 -0
- package/static/app/assets/input-B0lPdRQZ.js +1 -0
- package/static/app/assets/link-2-CoFbooHS.js +1 -0
- package/static/app/assets/{permissionCopy-Bpb83Hx9.js → permissionCopy-BsyLxtao.js} +1 -1
- package/static/app/assets/primitives-DEbN-d6p.js +1 -0
- package/static/app/assets/search-BybIWPNd.js +1 -0
- package/static/app/assets/{share-2-BH1M-WNi.js → share-2-CVtZ_ewX.js} +1 -1
- package/static/app/assets/{shield-alert-BlKdBXcG.js → shield-alert-CBi2GNWM.js} +1 -1
- package/static/app/assets/{textarea-CCWbUfFB.js → textarea-DNMpB5ih.js} +1 -1
- package/static/app/assets/{useFocusTrap-YdHQ7pJ1.js → useFocusTrap-C83t3GXF.js} +1 -1
- package/static/app/assets/useMutation-DtbJDoyz.js +1 -0
- package/static/app/assets/{useQuery-CXQiwbVT.js → useQuery-Dcp1OChy.js} +1 -1
- package/static/app/assets/utils-BlZr7Pd4.js +4 -0
- package/static/app/assets/workspace-jJY4RuAV.js +1 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/lattice_brain/graph/_kg_common.py +0 -1331
- package/lattice_brain/graph/discovery_index.py +0 -1141
- package/lattice_brain/graph/retrieval.py +0 -1120
- package/lattice_brain/graph/retrieval_vector.py +0 -1293
- package/lattice_brain/ingestion.py +0 -1525
- package/lattice_brain/multimodal.py +0 -1258
- package/latticeai/core/agent.py +0 -1465
- package/latticeai/core/embedding_providers.py +0 -1196
- package/latticeai/core/file_generation.py +0 -1047
- package/latticeai/integrations/telegram_bot.py +0 -1390
- package/latticeai/models/router.py +0 -1007
- package/latticeai/runtime/build_phases.py +0 -1450
- package/latticeai/services/brain_intelligence.py +0 -1083
- package/latticeai/services/memory_service.py +0 -1177
- package/latticeai/services/model_runtime.py +0 -1281
- package/latticeai/setup/wizard.py +0 -1310
- package/static/app/assets/Act-AWf0SAKp.js +0 -1
- package/static/app/assets/AdminConsole-D0u8Tiyj.js +0 -1
- package/static/app/assets/BrainHome-Ts7G_Ila.js +0 -2
- package/static/app/assets/BrainSignals-jMYgQ2Ar.js +0 -1
- package/static/app/assets/Capture-CqOSzyPr.js +0 -1
- package/static/app/assets/CommandPalette-DC0Bzh-I.js +0 -1
- package/static/app/assets/Library-CX-bbhmK.js +0 -1
- package/static/app/assets/ProductFlow-BHA2cfKI.js +0 -1
- package/static/app/assets/ReviewCard-BUhCKRNM.js +0 -3
- package/static/app/assets/System-Bu2t5hn1.js +0 -1
- package/static/app/assets/arrow-left-Dzwa5zRb.js +0 -1
- package/static/app/assets/brain-DJMoqrwx.js +0 -1
- package/static/app/assets/index-BpYkzcVm.js +0 -10
- package/static/app/assets/input-DSlJJxRs.js +0 -1
- package/static/app/assets/primitives-BCx6TvfG.js +0 -1
- package/static/app/assets/search-Cgy8cCFJ.js +0 -1
- package/static/app/assets/utils-zqPZJxdx.js +0 -4
- package/static/app/assets/workspace-DXTihhfU.js +0 -1
package/src-tauri/src/main.rs
CHANGED
|
@@ -1,410 +1,125 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
//! Lattice AI desktop shell.
|
|
2
|
+
//!
|
|
3
|
+
//! Everything this file used to do itself — resolving the worker command,
|
|
4
|
+
//! spawning it, probing a TCP socket to guess whether it was up, killing and
|
|
5
|
+
//! respawning on restart — moved into the `lattice-host` crate in 11.4.0 and is
|
|
6
|
+
//! now shared with the `lattice-host` binary, so the desktop and the CLI front
|
|
7
|
+
//! door cannot drift apart. What is left here is the part that is genuinely
|
|
8
|
+
//! Tauri's: five commands, one window, and the promise that closing the window
|
|
9
|
+
//! stops the worker.
|
|
10
|
+
//!
|
|
11
|
+
//! The five command names and their response shapes are unchanged — the
|
|
12
|
+
//! frontend (`frontend/src/api/base.ts`, `frontend/src/api/client.ts`) invokes
|
|
13
|
+
//! them by name and reads `backend_status` as a plain record.
|
|
14
|
+
|
|
15
|
+
mod backend;
|
|
16
|
+
mod folder;
|
|
17
|
+
|
|
18
|
+
use std::sync::Arc;
|
|
19
|
+
|
|
20
|
+
use backend::{BackendStatus, DesktopBackend};
|
|
10
21
|
use tauri::{Manager, State};
|
|
11
22
|
|
|
12
|
-
|
|
13
|
-
origin: String,
|
|
14
|
-
command: String,
|
|
15
|
-
cwd: Option<String>,
|
|
16
|
-
child: Mutex<Option<Child>>,
|
|
17
|
-
last_error: Mutex<Option<String>>,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
#[derive(Serialize)]
|
|
21
|
-
struct BackendStatus {
|
|
22
|
-
origin: String,
|
|
23
|
-
command: String,
|
|
24
|
-
cwd: Option<String>,
|
|
25
|
-
running: bool,
|
|
26
|
-
pid: Option<u32>,
|
|
27
|
-
last_error: Option<String>,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
struct BackendLaunch {
|
|
31
|
-
command: String,
|
|
32
|
-
program: String,
|
|
33
|
-
args: Vec<String>,
|
|
34
|
-
cwd: Option<PathBuf>,
|
|
35
|
-
}
|
|
36
|
-
|
|
23
|
+
/// `backend_origin` → the origin every frontend request is sent to.
|
|
37
24
|
#[tauri::command]
|
|
38
|
-
fn backend_origin(state: State<'_,
|
|
39
|
-
state.origin.
|
|
25
|
+
fn backend_origin(state: State<'_, Arc<DesktopBackend>>) -> String {
|
|
26
|
+
state.origin().to_string()
|
|
40
27
|
}
|
|
41
28
|
|
|
29
|
+
/// `backend_status` → a live snapshot, health probe included.
|
|
30
|
+
///
|
|
31
|
+
/// Async, and therefore `Result`, because Tauri requires it of a command that
|
|
32
|
+
/// borrows state across an await. It never returns `Err`: the frontend keeps
|
|
33
|
+
/// receiving a resolved record exactly as before.
|
|
42
34
|
#[tauri::command]
|
|
43
|
-
fn backend_status(state: State<'_,
|
|
44
|
-
|
|
35
|
+
async fn backend_status(state: State<'_, Arc<DesktopBackend>>) -> Result<BackendStatus, String> {
|
|
36
|
+
Ok(state.status().await)
|
|
45
37
|
}
|
|
46
38
|
|
|
39
|
+
/// `restart_backend` → stop, start, and report what happened.
|
|
47
40
|
#[tauri::command]
|
|
48
|
-
fn restart_backend(state: State<'_,
|
|
49
|
-
|
|
50
|
-
let launch = backend_launch(&state.origin);
|
|
51
|
-
match spawn_backend(&state.origin, &launch) {
|
|
52
|
-
Ok(child) => {
|
|
53
|
-
if let Ok(mut slot) = state.child.lock() {
|
|
54
|
-
*slot = child;
|
|
55
|
-
}
|
|
56
|
-
set_error(&state, None);
|
|
57
|
-
}
|
|
58
|
-
Err(err) => set_error(&state, Some(err)),
|
|
59
|
-
}
|
|
60
|
-
status_from_state(&state)
|
|
41
|
+
async fn restart_backend(state: State<'_, Arc<DesktopBackend>>) -> Result<BackendStatus, String> {
|
|
42
|
+
Ok(state.restart().await)
|
|
61
43
|
}
|
|
62
44
|
|
|
45
|
+
/// `shutdown_backend` → stop the worker and suppress restarts.
|
|
63
46
|
#[tauri::command]
|
|
64
|
-
fn shutdown_backend(state: State<'_,
|
|
65
|
-
|
|
66
|
-
status_from_state(&state)
|
|
47
|
+
async fn shutdown_backend(state: State<'_, Arc<DesktopBackend>>) -> Result<BackendStatus, String> {
|
|
48
|
+
Ok(state.stop().await)
|
|
67
49
|
}
|
|
68
50
|
|
|
51
|
+
/// `select_folder` → the native directory picker.
|
|
69
52
|
#[tauri::command]
|
|
70
53
|
fn select_folder() -> Option<String> {
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
#[cfg(target_os = "macos")]
|
|
75
|
-
fn native_select_folder() -> Option<String> {
|
|
76
|
-
let output = Command::new("osascript")
|
|
77
|
-
.args([
|
|
78
|
-
"-e",
|
|
79
|
-
r#"POSIX path of (choose folder with prompt "Choose a folder for Lattice AI")"#,
|
|
80
|
-
])
|
|
81
|
-
.output()
|
|
82
|
-
.ok()?;
|
|
83
|
-
if !output.status.success() {
|
|
84
|
-
return None;
|
|
85
|
-
}
|
|
86
|
-
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
87
|
-
if path.is_empty() {
|
|
88
|
-
None
|
|
89
|
-
} else {
|
|
90
|
-
Some(path)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
#[cfg(not(target_os = "macos"))]
|
|
95
|
-
fn native_select_folder() -> Option<String> {
|
|
96
|
-
None
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
fn split_command(command: &str) -> Vec<String> {
|
|
100
|
-
command.split_whitespace().map(|part| part.to_string()).collect()
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
fn command_in_path(name: &str) -> Option<String> {
|
|
104
|
-
let mut dirs: Vec<PathBuf> = env::var_os("PATH")
|
|
105
|
-
.map(|value| env::split_paths(&value).collect())
|
|
106
|
-
.unwrap_or_default();
|
|
107
|
-
dirs.extend([
|
|
108
|
-
PathBuf::from("/opt/homebrew/bin"),
|
|
109
|
-
PathBuf::from("/usr/local/bin"),
|
|
110
|
-
PathBuf::from("/usr/bin"),
|
|
111
|
-
PathBuf::from("/bin"),
|
|
112
|
-
]);
|
|
113
|
-
for dir in dirs {
|
|
114
|
-
let candidate = dir.join(name);
|
|
115
|
-
if candidate.is_file() {
|
|
116
|
-
return Some(candidate.to_string_lossy().to_string());
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
None
|
|
54
|
+
folder::select_folder()
|
|
120
55
|
}
|
|
121
56
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Command::new(python)
|
|
144
|
-
.args(["-c", &format!("import {module}")])
|
|
145
|
-
.stdout(Stdio::null())
|
|
146
|
-
.stderr(Stdio::null())
|
|
147
|
-
.status()
|
|
148
|
-
.map(|status| status.success())
|
|
149
|
-
.unwrap_or(false)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
fn resource_dir() -> Option<PathBuf> {
|
|
153
|
-
let exe = env::current_exe().ok()?;
|
|
154
|
-
let macos_dir = exe.parent()?;
|
|
155
|
-
let contents_dir = macos_dir.parent()?;
|
|
156
|
-
let resources = contents_dir.join("Resources");
|
|
157
|
-
if resources.exists() {
|
|
158
|
-
Some(resources)
|
|
159
|
-
} else {
|
|
160
|
-
None
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
fn bundled_python_root() -> Option<PathBuf> {
|
|
165
|
-
let resources = resource_dir()?;
|
|
166
|
-
let up = resources.join("_up_");
|
|
167
|
-
if up.join("latticeai").join("cli").join("entrypoint.py").is_file() {
|
|
168
|
-
Some(up)
|
|
169
|
-
} else if resources.join("latticeai").join("cli").join("entrypoint.py").is_file() {
|
|
170
|
-
Some(resources)
|
|
171
|
-
} else {
|
|
172
|
-
None
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
fn desktop_runtime_dir() -> Option<PathBuf> {
|
|
177
|
-
let home = env::var("HOME").ok()?;
|
|
178
|
-
let dir = PathBuf::from(home).join(".ltcai").join("desktop-runtime");
|
|
179
|
-
let _ = std::fs::create_dir_all(&dir);
|
|
180
|
-
Some(dir)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
fn python_path_env(launch: &BackendLaunch) -> Option<String> {
|
|
184
|
-
let mut paths: Vec<PathBuf> = Vec::new();
|
|
185
|
-
if let Some(resources) = bundled_python_root() {
|
|
186
|
-
paths.push(resources);
|
|
187
|
-
}
|
|
188
|
-
if let Some(cwd) = &launch.cwd {
|
|
189
|
-
if !paths.iter().any(|path| path == cwd) {
|
|
190
|
-
paths.push(cwd.clone());
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if let Some(existing) = env::var_os("PYTHONPATH") {
|
|
194
|
-
paths.extend(env::split_paths(&existing));
|
|
195
|
-
}
|
|
196
|
-
env::join_paths(paths).ok().map(|value| value.to_string_lossy().to_string())
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
fn backend_launch(origin: &str) -> BackendLaunch {
|
|
200
|
-
let port = origin.rsplit(':').next().unwrap_or("8765").to_string();
|
|
201
|
-
if let Ok(command) = env::var("LATTICEAI_DESKTOP_BACKEND_CMD") {
|
|
202
|
-
let parts = split_command(&command);
|
|
203
|
-
if let Some(program) = parts.first() {
|
|
204
|
-
return BackendLaunch {
|
|
205
|
-
command,
|
|
206
|
-
program: program.clone(),
|
|
207
|
-
args: parts[1..].to_vec(),
|
|
208
|
-
cwd: env::var("LATTICEAI_DESKTOP_BACKEND_CWD").ok().map(PathBuf::from),
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
for name in ["LTCAI", "ltcai"] {
|
|
214
|
-
if let Some(program) = command_in_path(name) {
|
|
215
|
-
return BackendLaunch {
|
|
216
|
-
command: format!("{program} --host 127.0.0.1 --port {port}"),
|
|
217
|
-
program,
|
|
218
|
-
args: vec!["--host".into(), "127.0.0.1".into(), "--port".into(), port],
|
|
219
|
-
cwd: None,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
for python in python_candidates() {
|
|
225
|
-
if module_importable(&python, "latticeai.cli.entrypoint") {
|
|
226
|
-
return BackendLaunch {
|
|
227
|
-
command: format!("{python} -m latticeai.cli.entrypoint --host 127.0.0.1 --port {port}"),
|
|
228
|
-
program: python,
|
|
229
|
-
args: vec![
|
|
230
|
-
"-m".into(),
|
|
231
|
-
"latticeai.cli.entrypoint".into(),
|
|
232
|
-
"--host".into(),
|
|
233
|
-
"127.0.0.1".into(),
|
|
234
|
-
"--port".into(),
|
|
235
|
-
port,
|
|
236
|
-
],
|
|
237
|
-
cwd: None,
|
|
238
|
-
};
|
|
57
|
+
/// Bring the worker up, then point the webview at `{origin}/app`.
|
|
58
|
+
///
|
|
59
|
+
/// The navigation waits on the worker's own `GET /health` rather than on a TCP
|
|
60
|
+
/// connect: a bound socket only proves something is listening, and landing on
|
|
61
|
+
/// `/app` before the routes exist is what used to show a blank window.
|
|
62
|
+
fn boot(app: &tauri::App) {
|
|
63
|
+
let Some(window) = app.get_webview_window("main") else {
|
|
64
|
+
return;
|
|
65
|
+
};
|
|
66
|
+
let _ = window.set_title("Lattice AI");
|
|
67
|
+
let _ = window.show();
|
|
68
|
+
let _ = window.set_focus();
|
|
69
|
+
|
|
70
|
+
let shell = Arc::clone(&app.state::<Arc<DesktopBackend>>());
|
|
71
|
+
tauri::async_runtime::spawn(async move {
|
|
72
|
+
let status = shell.start().await;
|
|
73
|
+
if !status.healthy && !shell.supervised() {
|
|
74
|
+
// A supervised worker already had its health gate inside `start`;
|
|
75
|
+
// an adopted one (LATTICEAI_DESKTOP_BACKEND_ORIGIN) still needs it.
|
|
76
|
+
// Waiting in both places would double the worst case.
|
|
77
|
+
shell.wait_until_serving().await;
|
|
239
78
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if
|
|
244
|
-
|
|
245
|
-
return BackendLaunch {
|
|
246
|
-
command: format!("{python} -m latticeai.cli.entrypoint --host 127.0.0.1 --port {port}"),
|
|
247
|
-
program: python,
|
|
248
|
-
args: vec![
|
|
249
|
-
"-m".into(),
|
|
250
|
-
"latticeai.cli.entrypoint".into(),
|
|
251
|
-
"--host".into(),
|
|
252
|
-
"127.0.0.1".into(),
|
|
253
|
-
"--port".into(),
|
|
254
|
-
port,
|
|
255
|
-
],
|
|
256
|
-
cwd: Some(resources),
|
|
257
|
-
};
|
|
258
|
-
}
|
|
79
|
+
// Navigate either way, as the shell always has: a window showing the
|
|
80
|
+
// browser's own "cannot connect" is more use than a window showing
|
|
81
|
+
// nothing while a worker that will never arrive is waited for.
|
|
82
|
+
if let Ok(url) = tauri::Url::parse(&shell.app_url()) {
|
|
83
|
+
let _ = window.navigate(url);
|
|
259
84
|
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Stop the worker from a synchronous Tauri callback (window close, app exit).
|
|
89
|
+
///
|
|
90
|
+
/// `block_on` is safe here because these callbacks run on the event loop
|
|
91
|
+
/// thread, outside any async runtime — and the wait is what makes the shutdown
|
|
92
|
+
/// graceful: SIGTERM, then SIGKILL only if the worker ignores it, so the
|
|
93
|
+
/// worker gets to close its SQLite handles instead of being shot mid-write.
|
|
94
|
+
fn stop_worker<R: tauri::Runtime, M: Manager<R>>(manager: &M) {
|
|
95
|
+
if let Some(shell) = manager.try_state::<Arc<DesktopBackend>>() {
|
|
96
|
+
let shell = Arc::clone(&shell);
|
|
97
|
+
tauri::async_runtime::block_on(async move {
|
|
98
|
+
shell.stop().await;
|
|
99
|
+
});
|
|
273
100
|
}
|
|
274
101
|
}
|
|
275
102
|
|
|
276
|
-
fn
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
let mut cmd = Command::new(&launch.program);
|
|
285
|
-
cmd.args(&launch.args)
|
|
286
|
-
.env("LATTICEAI_HOST", "127.0.0.1")
|
|
287
|
-
.env("LATTICEAI_PORT", origin.rsplit(':').next().unwrap_or("8765"))
|
|
288
|
-
.env("LATTICEAI_ENABLE_TELEGRAM", "false")
|
|
289
|
-
.env("LATTICEAI_AUTOLOAD_MODELS", "false")
|
|
290
|
-
.env("LATTICEAI_ALLOW_MODEL_DOWNLOADS", "false")
|
|
291
|
-
.env("LATTICEAI_CORS_ALLOW_NETWORK", "false")
|
|
292
|
-
.env("LATTICEAI_ENABLE_EXTERNAL_CONNECTORS", "false")
|
|
293
|
-
.env("LATTICEAI_TUNNEL", "false")
|
|
294
|
-
.env(
|
|
295
|
-
"PATH",
|
|
296
|
-
format!(
|
|
297
|
-
"{}:{}",
|
|
298
|
-
"/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin",
|
|
299
|
-
env::var("PATH").unwrap_or_default()
|
|
300
|
-
),
|
|
301
|
-
);
|
|
302
|
-
if let Some(runtime_dir) = desktop_runtime_dir() {
|
|
303
|
-
cmd.env("LATTICEAI_AGENT_ROOT", runtime_dir.join("agent_workspace"));
|
|
304
|
-
if launch.cwd.is_none() {
|
|
305
|
-
cmd.current_dir(&runtime_dir);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if let Some(python_path) = python_path_env(launch) {
|
|
309
|
-
cmd.env("PYTHONPATH", python_path);
|
|
310
|
-
}
|
|
311
|
-
if let Some(cwd) = &launch.cwd {
|
|
312
|
-
cmd.current_dir(cwd);
|
|
313
|
-
}
|
|
314
|
-
if let Ok(home) = env::var("HOME") {
|
|
315
|
-
let log_dir = PathBuf::from(home).join(".ltcai");
|
|
316
|
-
let _ = std::fs::create_dir_all(&log_dir);
|
|
317
|
-
if let Ok(file) = OpenOptions::new().create(true).append(true).open(log_dir.join("desktop-sidecar.log")) {
|
|
318
|
-
cmd.stdout(Stdio::from(file));
|
|
319
|
-
} else {
|
|
320
|
-
cmd.stdout(Stdio::null());
|
|
103
|
+
fn main() {
|
|
104
|
+
let shell = match DesktopBackend::resolve() {
|
|
105
|
+
Ok(shell) => Arc::new(shell),
|
|
106
|
+
Err(err) => {
|
|
107
|
+
eprintln!("lattice-ai-desktop: cannot prepare the backend: {err}");
|
|
108
|
+
std::process::exit(1);
|
|
321
109
|
}
|
|
322
|
-
|
|
323
|
-
|
|
110
|
+
};
|
|
111
|
+
eprintln!(
|
|
112
|
+
"lattice-ai-desktop: backend {} ({})",
|
|
113
|
+
shell.origin(),
|
|
114
|
+
if shell.supervised() {
|
|
115
|
+
"supervised here"
|
|
324
116
|
} else {
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
} else {
|
|
328
|
-
cmd.stdout(Stdio::null()).stderr(Stdio::null());
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
cmd.spawn()
|
|
332
|
-
.map(Some)
|
|
333
|
-
.map_err(|err| format!("Failed to start desktop backend '{}': {}", launch.command, err))
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
fn kill_backend(state: &BackendState) {
|
|
337
|
-
if let Ok(mut child) = state.child.lock() {
|
|
338
|
-
if let Some(mut process) = child.take() {
|
|
339
|
-
let _ = process.kill();
|
|
340
|
-
let _ = process.wait();
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
fn status_from_state(state: &BackendState) -> BackendStatus {
|
|
346
|
-
let mut running = false;
|
|
347
|
-
let mut pid = None;
|
|
348
|
-
if let Ok(mut child_slot) = state.child.lock() {
|
|
349
|
-
if let Some(child) = child_slot.as_mut() {
|
|
350
|
-
match child.try_wait() {
|
|
351
|
-
Ok(Some(status)) => {
|
|
352
|
-
set_error(state, Some(format!("Desktop backend exited with status {}", status)));
|
|
353
|
-
*child_slot = None;
|
|
354
|
-
}
|
|
355
|
-
Ok(None) => {
|
|
356
|
-
running = true;
|
|
357
|
-
pid = Some(child.id());
|
|
358
|
-
}
|
|
359
|
-
Err(err) => set_error(state, Some(format!("Unable to inspect desktop backend: {}", err))),
|
|
360
|
-
}
|
|
117
|
+
"external — nothing will be spawned"
|
|
361
118
|
}
|
|
362
|
-
|
|
363
|
-
let last_error = state.last_error.lock().ok().and_then(|guard| guard.clone());
|
|
364
|
-
BackendStatus {
|
|
365
|
-
origin: state.origin.clone(),
|
|
366
|
-
command: state.command.clone(),
|
|
367
|
-
cwd: state.cwd.clone(),
|
|
368
|
-
running,
|
|
369
|
-
pid,
|
|
370
|
-
last_error,
|
|
371
|
-
}
|
|
372
|
-
}
|
|
119
|
+
);
|
|
373
120
|
|
|
374
|
-
fn wait_for_backend(origin: &str) {
|
|
375
|
-
let host_port = origin
|
|
376
|
-
.trim_start_matches("http://")
|
|
377
|
-
.trim_start_matches("https://")
|
|
378
|
-
.split('/')
|
|
379
|
-
.next()
|
|
380
|
-
.unwrap_or("127.0.0.1:8765")
|
|
381
|
-
.to_string();
|
|
382
|
-
for _ in 0..45 {
|
|
383
|
-
if std::net::TcpStream::connect(&host_port).is_ok() {
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
fn main() {
|
|
391
|
-
let origin = env::var("LATTICEAI_DESKTOP_BACKEND_ORIGIN")
|
|
392
|
-
.unwrap_or_else(|_| "http://127.0.0.1:8765".to_string());
|
|
393
|
-
let launch = backend_launch(&origin);
|
|
394
|
-
let command = launch.command.clone();
|
|
395
|
-
let cwd = launch.cwd.as_ref().map(|path| path.to_string_lossy().to_string());
|
|
396
|
-
let (child, last_error) = match spawn_backend(&origin, &launch) {
|
|
397
|
-
Ok(child) => (child, None),
|
|
398
|
-
Err(err) => (None, Some(err)),
|
|
399
|
-
};
|
|
400
121
|
tauri::Builder::default()
|
|
401
|
-
.manage(
|
|
402
|
-
origin,
|
|
403
|
-
command,
|
|
404
|
-
cwd,
|
|
405
|
-
child: Mutex::new(child),
|
|
406
|
-
last_error: Mutex::new(last_error),
|
|
407
|
-
})
|
|
122
|
+
.manage(shell)
|
|
408
123
|
.invoke_handler(tauri::generate_handler![
|
|
409
124
|
backend_origin,
|
|
410
125
|
backend_status,
|
|
@@ -413,27 +128,12 @@ fn main() {
|
|
|
413
128
|
select_folder
|
|
414
129
|
])
|
|
415
130
|
.setup(|app| {
|
|
416
|
-
|
|
417
|
-
let _ = window.set_title("Lattice AI");
|
|
418
|
-
let _ = window.show();
|
|
419
|
-
let _ = window.set_focus();
|
|
420
|
-
let origin = app.state::<BackendState>().origin.clone();
|
|
421
|
-
let target = format!("{}/app", origin.trim_end_matches('/'));
|
|
422
|
-
let window_for_nav = window.clone();
|
|
423
|
-
std::thread::spawn(move || {
|
|
424
|
-
wait_for_backend(&origin);
|
|
425
|
-
if let Ok(url) = tauri::Url::parse(&target) {
|
|
426
|
-
let _ = window_for_nav.navigate(url);
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
}
|
|
131
|
+
boot(app);
|
|
430
132
|
Ok(())
|
|
431
133
|
})
|
|
432
134
|
.on_window_event(|window, event| {
|
|
433
135
|
if matches!(event, tauri::WindowEvent::CloseRequested { .. }) {
|
|
434
|
-
|
|
435
|
-
kill_backend(&state);
|
|
436
|
-
}
|
|
136
|
+
stop_worker(window);
|
|
437
137
|
}
|
|
438
138
|
})
|
|
439
139
|
.build(tauri::generate_context!())
|
|
@@ -443,9 +143,7 @@ fn main() {
|
|
|
443
143
|
event,
|
|
444
144
|
tauri::RunEvent::ExitRequested { .. } | tauri::RunEvent::Exit
|
|
445
145
|
) {
|
|
446
|
-
|
|
447
|
-
kill_backend(&state);
|
|
448
|
-
}
|
|
146
|
+
stop_worker(app_handle);
|
|
449
147
|
}
|
|
450
148
|
});
|
|
451
149
|
}
|
|
@@ -1,45 +1,49 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "11.
|
|
2
|
+
"version": "11.4.0",
|
|
3
3
|
"generated_at": "vite",
|
|
4
4
|
"entrypoints": {
|
|
5
|
-
"app": "/static/app/assets/index-
|
|
5
|
+
"app": "/static/app/assets/index-_u5iUHDr.js"
|
|
6
6
|
},
|
|
7
7
|
"assets": {
|
|
8
8
|
"../node_modules/@tauri-apps/api/core.js": "/static/app/assets/core-CwxXejkd.js",
|
|
9
|
-
"_BrainSignals-
|
|
10
|
-
"_LivingBrain-
|
|
11
|
-
"_ReviewCard-
|
|
12
|
-
"_arrow-left-
|
|
13
|
-
"_bot-
|
|
14
|
-
"_brain-
|
|
15
|
-
"_button-
|
|
16
|
-
"_circle-
|
|
17
|
-
"_circle-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"src/
|
|
39
|
-
"src/
|
|
40
|
-
"src/
|
|
41
|
-
"src/
|
|
42
|
-
"src/pages/
|
|
43
|
-
"src/pages/
|
|
9
|
+
"_BrainSignals-6yR6ir5t.js": "/static/app/assets/BrainSignals-6yR6ir5t.js",
|
|
10
|
+
"_LivingBrain-Jn1GK0-S.js": "/static/app/assets/LivingBrain-Jn1GK0-S.js",
|
|
11
|
+
"_ReviewCard-6B27X8Vg.js": "/static/app/assets/ReviewCard-6B27X8Vg.js",
|
|
12
|
+
"_arrow-left-DXvKg9U6.js": "/static/app/assets/arrow-left-DXvKg9U6.js",
|
|
13
|
+
"_bot-IM_E_Y12.js": "/static/app/assets/bot-IM_E_Y12.js",
|
|
14
|
+
"_brain-Ci1CkWjM.js": "/static/app/assets/brain-Ci1CkWjM.js",
|
|
15
|
+
"_button-COwyqfHM.js": "/static/app/assets/button-COwyqfHM.js",
|
|
16
|
+
"_circle-check-DfInj-qD.js": "/static/app/assets/circle-check-DfInj-qD.js",
|
|
17
|
+
"_circle-pause-DEM4A1Y5.js": "/static/app/assets/circle-pause-DEM4A1Y5.js",
|
|
18
|
+
"_circle-play-C9djDuLd.js": "/static/app/assets/circle-play-C9djDuLd.js",
|
|
19
|
+
"_cpu-DFdo1gw-.js": "/static/app/assets/cpu-DFdo1gw-.js",
|
|
20
|
+
"_download-SnJL6oqk.js": "/static/app/assets/download-SnJL6oqk.js",
|
|
21
|
+
"_folder-open-CqZeDkjE.js": "/static/app/assets/folder-open-CqZeDkjE.js",
|
|
22
|
+
"_hard-drive-j1jJXYYf.js": "/static/app/assets/hard-drive-j1jJXYYf.js",
|
|
23
|
+
"_input-B0lPdRQZ.js": "/static/app/assets/input-B0lPdRQZ.js",
|
|
24
|
+
"_link-2-CoFbooHS.js": "/static/app/assets/link-2-CoFbooHS.js",
|
|
25
|
+
"_permissionCopy-BsyLxtao.js": "/static/app/assets/permissionCopy-BsyLxtao.js",
|
|
26
|
+
"_primitives-DEbN-d6p.js": "/static/app/assets/primitives-DEbN-d6p.js",
|
|
27
|
+
"_search-BybIWPNd.js": "/static/app/assets/search-BybIWPNd.js",
|
|
28
|
+
"_share-2-CVtZ_ewX.js": "/static/app/assets/share-2-CVtZ_ewX.js",
|
|
29
|
+
"_shield-alert-CBi2GNWM.js": "/static/app/assets/shield-alert-CBi2GNWM.js",
|
|
30
|
+
"_textarea-DNMpB5ih.js": "/static/app/assets/textarea-DNMpB5ih.js",
|
|
31
|
+
"_useFocusTrap-C83t3GXF.js": "/static/app/assets/useFocusTrap-C83t3GXF.js",
|
|
32
|
+
"_useMutation-DtbJDoyz.js": "/static/app/assets/useMutation-DtbJDoyz.js",
|
|
33
|
+
"_useQuery-Dcp1OChy.js": "/static/app/assets/useQuery-Dcp1OChy.js",
|
|
34
|
+
"_utils-BlZr7Pd4.js": "/static/app/assets/utils-BlZr7Pd4.js",
|
|
35
|
+
"_workspace-jJY4RuAV.js": "/static/app/assets/workspace-jJY4RuAV.js",
|
|
36
|
+
"index.html": "/static/app/assets/index-_u5iUHDr.js",
|
|
37
|
+
"assets/index-BLPb5lmE.css": "/static/app/assets/index-BLPb5lmE.css",
|
|
38
|
+
"src/components/ProductFlow.tsx": "/static/app/assets/ProductFlow-B-w1R4Oo.js",
|
|
39
|
+
"src/features/admin/AdminConsole.tsx": "/static/app/assets/AdminConsole-DL3Cr5pL.js",
|
|
40
|
+
"src/features/brain/BrainHome.tsx": "/static/app/assets/BrainHome-DoXRhUUC.js",
|
|
41
|
+
"src/features/command/CommandPalette.tsx": "/static/app/assets/CommandPalette-D2pMxC2I.js",
|
|
42
|
+
"src/pages/Act.tsx": "/static/app/assets/Act-yYpYnn0v.js",
|
|
43
|
+
"src/pages/Brain.tsx": "/static/app/assets/Brain-C1HBN0Wf.js",
|
|
44
|
+
"src/pages/Capture.tsx": "/static/app/assets/Capture-CFIRsFNE.js",
|
|
45
|
+
"src/pages/Chronicle.tsx": "/static/app/assets/Chronicle-BZbEgiwN.js",
|
|
46
|
+
"src/pages/Library.tsx": "/static/app/assets/Library-DwO3yZST.js",
|
|
47
|
+
"src/pages/System.tsx": "/static/app/assets/System-DW8F-2xL.js"
|
|
44
48
|
}
|
|
45
49
|
}
|