@creezio/shell-ui 0.22.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 +336 -0
- package/dist/adapters/nav-shell.d.ts +54 -0
- package/dist/adapters/nav-shell.d.ts.map +1 -0
- package/dist/adapters/nav-shell.js +112 -0
- package/dist/adapters/nav-shell.js.map +1 -0
- package/dist/brand.d.ts +64 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +59 -0
- package/dist/brand.js.map +1 -0
- package/dist/core-nav.d.ts +17 -0
- package/dist/core-nav.d.ts.map +1 -0
- package/dist/core-nav.js +40 -0
- package/dist/core-nav.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-scopes.d.ts +24 -0
- package/dist/lib/api-scopes.d.ts.map +1 -0
- package/dist/lib/api-scopes.js +78 -0
- package/dist/lib/api-scopes.js.map +1 -0
- package/dist/lib/catalog-suspense-key.d.ts +3 -0
- package/dist/lib/catalog-suspense-key.d.ts.map +1 -0
- package/dist/lib/catalog-suspense-key.js +12 -0
- package/dist/lib/catalog-suspense-key.js.map +1 -0
- package/dist/lib/data-changed.d.ts +51 -0
- package/dist/lib/data-changed.d.ts.map +1 -0
- package/dist/lib/data-changed.js +227 -0
- package/dist/lib/data-changed.js.map +1 -0
- package/dist/lib/desktop-home-path.d.ts +14 -0
- package/dist/lib/desktop-home-path.d.ts.map +1 -0
- package/dist/lib/desktop-home-path.js +29 -0
- package/dist/lib/desktop-home-path.js.map +1 -0
- package/dist/lib/geo-distance.d.ts +6 -0
- package/dist/lib/geo-distance.d.ts.map +1 -0
- package/dist/lib/geo-distance.js +27 -0
- package/dist/lib/geo-distance.js.map +1 -0
- package/dist/lib/img.d.ts +2 -0
- package/dist/lib/img.d.ts.map +1 -0
- package/dist/lib/img.js +40 -0
- package/dist/lib/img.js.map +1 -0
- package/dist/lib/keepalive-eviction.d.ts +14 -0
- package/dist/lib/keepalive-eviction.d.ts.map +1 -0
- package/dist/lib/keepalive-eviction.js +44 -0
- package/dist/lib/keepalive-eviction.js.map +1 -0
- package/dist/lib/ops-track.d.ts +29 -0
- package/dist/lib/ops-track.d.ts.map +1 -0
- package/dist/lib/ops-track.js +37 -0
- package/dist/lib/ops-track.js.map +1 -0
- package/dist/lib/optimize-cover-url.d.ts +10 -0
- package/dist/lib/optimize-cover-url.d.ts.map +1 -0
- package/dist/lib/optimize-cover-url.js +80 -0
- package/dist/lib/optimize-cover-url.js.map +1 -0
- package/dist/lib/page-trails.d.ts +9 -0
- package/dist/lib/page-trails.d.ts.map +1 -0
- package/dist/lib/page-trails.js +20 -0
- package/dist/lib/page-trails.js.map +1 -0
- package/dist/lib/public-origin.d.ts +32 -0
- package/dist/lib/public-origin.d.ts.map +1 -0
- package/dist/lib/public-origin.js +125 -0
- package/dist/lib/public-origin.js.map +1 -0
- package/dist/lib/server-incident.d.ts +12 -0
- package/dist/lib/server-incident.d.ts.map +1 -0
- package/dist/lib/server-incident.js +72 -0
- package/dist/lib/server-incident.js.map +1 -0
- package/dist/lib/tab-document-url.d.ts +13 -0
- package/dist/lib/tab-document-url.d.ts.map +1 -0
- package/dist/lib/tab-document-url.js +60 -0
- package/dist/lib/tab-document-url.js.map +1 -0
- package/dist/lib/utils.d.ts +20 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +87 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/nav-catalog.d.ts +97 -0
- package/dist/nav-catalog.d.ts.map +1 -0
- package/dist/nav-catalog.js +282 -0
- package/dist/nav-catalog.js.map +1 -0
- package/dist/registry.d.ts +11 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +60 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist-cjs/adapters/nav-shell.js +115 -0
- package/dist-cjs/adapters/nav-shell.js.map +1 -0
- package/dist-cjs/brand.js +66 -0
- package/dist-cjs/brand.js.map +1 -0
- package/dist-cjs/core-nav.js +43 -0
- package/dist-cjs/core-nav.js.map +1 -0
- package/dist-cjs/index.js +95 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/lib/api-scopes.js +86 -0
- package/dist-cjs/lib/api-scopes.js.map +1 -0
- package/dist-cjs/lib/catalog-suspense-key.js +15 -0
- package/dist-cjs/lib/catalog-suspense-key.js.map +1 -0
- package/dist-cjs/lib/data-changed.js +236 -0
- package/dist-cjs/lib/data-changed.js.map +1 -0
- package/dist-cjs/lib/desktop-home-path.js +34 -0
- package/dist-cjs/lib/desktop-home-path.js.map +1 -0
- package/dist-cjs/lib/geo-distance.js +30 -0
- package/dist-cjs/lib/geo-distance.js.map +1 -0
- package/dist-cjs/lib/img.js +43 -0
- package/dist-cjs/lib/img.js.map +1 -0
- package/dist-cjs/lib/keepalive-eviction.js +49 -0
- package/dist-cjs/lib/keepalive-eviction.js.map +1 -0
- package/dist-cjs/lib/ops-track.js +41 -0
- package/dist-cjs/lib/ops-track.js.map +1 -0
- package/dist-cjs/lib/optimize-cover-url.js +83 -0
- package/dist-cjs/lib/optimize-cover-url.js.map +1 -0
- package/dist-cjs/lib/page-trails.js +25 -0
- package/dist-cjs/lib/page-trails.js.map +1 -0
- package/dist-cjs/lib/public-origin.js +130 -0
- package/dist-cjs/lib/public-origin.js.map +1 -0
- package/dist-cjs/lib/server-incident.js +75 -0
- package/dist-cjs/lib/server-incident.js.map +1 -0
- package/dist-cjs/lib/tab-document-url.js +65 -0
- package/dist-cjs/lib/tab-document-url.js.map +1 -0
- package/dist-cjs/lib/utils.js +97 -0
- package/dist-cjs/lib/utils.js.map +1 -0
- package/dist-cjs/nav-catalog.js +293 -0
- package/dist-cjs/nav-catalog.js.map +1 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/registry.js +64 -0
- package/dist-cjs/registry.js.map +1 -0
- package/dist-cjs/types.js +3 -0
- package/dist-cjs/types.js.map +1 -0
- package/package.json +163 -0
- package/ui/app-error-boundary.tsx +59 -0
- package/ui/data-table.tsx +121 -0
- package/ui/desktop/auth-window-chrome.tsx +51 -0
- package/ui/desktop/desktop-bridge.tsx +87 -0
- package/ui/desktop/external-site-slot.tsx +313 -0
- package/ui/desktop/external-site-surface.ts +58 -0
- package/ui/desktop/site-link.tsx +105 -0
- package/ui/desktop/window-chrome-controls.tsx +5 -0
- package/ui/desktop-types.ts +30 -0
- package/ui/faceted-filters.tsx +119 -0
- package/ui/global-search-host.ts +20 -0
- package/ui/global-search.tsx +66 -0
- package/ui/index.ts +178 -0
- package/ui/kit.ts +30 -0
- package/ui/layout/app-shell.tsx +58 -0
- package/ui/layout/desktop-update-banner.tsx +100 -0
- package/ui/layout/entity-header.tsx +44 -0
- package/ui/layout/native-os-nav.ts +104 -0
- package/ui/layout/nav-catalog-loader.tsx +98 -0
- package/ui/layout/nav-icons.ts +67 -0
- package/ui/layout/page-chrome.tsx +90 -0
- package/ui/layout/page-toolbar-context.tsx +82 -0
- package/ui/layout/sandbox-banner.tsx +38 -0
- package/ui/layout/section-view-shell.tsx +73 -0
- package/ui/layout/sidebar-host.ts +144 -0
- package/ui/layout/sidebar.tsx +1150 -0
- package/ui/lib/ai-screencast-hub.ts +127 -0
- package/ui/lib/ai-workspace-client.ts +46 -0
- package/ui/lib/aid.ts +22 -0
- package/ui/lib/desktop-host.ts +114 -0
- package/ui/lib/fleet-tracker-client.ts +237 -0
- package/ui/lib/hermes-ui.ts +114 -0
- package/ui/lib/n8n-ui.ts +115 -0
- package/ui/lib/use-creezio-resource.tsx +121 -0
- package/ui/lib/use-shell-ui-brand.ts +22 -0
- package/ui/list-toolbar.tsx +309 -0
- package/ui/os-pages/desktop-settings-page.tsx +158 -0
- package/ui/os-pages/index.ts +8 -0
- package/ui/page-loading/entity-page-loading.tsx +59 -0
- package/ui/page-loading/list-page-loading.tsx +61 -0
- package/ui/pagination.tsx +56 -0
- package/ui/primitives/avatar.tsx +33 -0
- package/ui/primitives/badge.tsx +36 -0
- package/ui/primitives/breadcrumb.tsx +93 -0
- package/ui/primitives/button.tsx +44 -0
- package/ui/primitives/card.tsx +44 -0
- package/ui/primitives/chart.tsx +317 -0
- package/ui/primitives/command.tsx +111 -0
- package/ui/primitives/dialog.tsx +100 -0
- package/ui/primitives/dropdown-menu.tsx +187 -0
- package/ui/primitives/input.tsx +19 -0
- package/ui/primitives/label.tsx +17 -0
- package/ui/primitives/resizable.tsx +45 -0
- package/ui/primitives/scroll-area.tsx +41 -0
- package/ui/primitives/select.tsx +73 -0
- package/ui/primitives/separator.tsx +26 -0
- package/ui/primitives/sheet.tsx +90 -0
- package/ui/primitives/skeleton.tsx +7 -0
- package/ui/primitives/sonner.tsx +22 -0
- package/ui/primitives/tabs.tsx +51 -0
- package/ui/primitives/textarea.tsx +22 -0
- package/ui/primitives/tooltip.tsx +31 -0
- package/ui/pwa/client-error-reporter.tsx +62 -0
- package/ui/pwa/register-sw.tsx +50 -0
- package/ui/range-filters.tsx +174 -0
- package/ui/search/global-search-config.ts +32 -0
- package/ui/search/global-search-provider.tsx +528 -0
- package/ui/search/search-history.ts +74 -0
- package/ui/search-input.tsx +146 -0
- package/ui/settings/account-settings.tsx +140 -0
- package/ui/settings/agent-profile-settings.tsx +184 -0
- package/ui/settings/api-keys-settings.tsx +349 -0
- package/ui/settings/desktop-background-settings.tsx +128 -0
- package/ui/settings/desktop-connection-settings.tsx +251 -0
- package/ui/settings/desktop-embed-env-panel.tsx +257 -0
- package/ui/settings/desktop-fleet-telemetry-settings.tsx +269 -0
- package/ui/settings/desktop-hermes-settings.tsx +341 -0
- package/ui/settings/desktop-llm-keys.tsx +192 -0
- package/ui/settings/desktop-n8n-settings.tsx +306 -0
- package/ui/settings/desktop-tunnel.tsx +264 -0
- package/ui/settings/desktop-update-settings.tsx +175 -0
- package/ui/settings/factory-reset-settings.tsx +89 -0
- package/ui/settings/host-only-settings.tsx +62 -0
- package/ui/settings/locked-config-field.tsx +30 -0
- package/ui/settings/ops-diagnostic-settings.tsx +242 -0
- package/ui/settings/search-reindex-settings.tsx +158 -0
- package/ui/settings/server-mode-cards.tsx +801 -0
- package/ui/theme/tailwind-preset.cjs +82 -0
- package/ui/theme/theme.css +328 -0
- package/ui/workspace/ai-activity-panel-host.tsx +19 -0
- package/ui/workspace/ai-workspace-agent-host.tsx +54 -0
- package/ui/workspace/ai-workspace-banner.tsx +168 -0
- package/ui/workspace/impersonation-banner.tsx +39 -0
- package/ui/workspace/keep-alive.tsx +220 -0
- package/ui/workspace/tab-workspace-context.tsx +1172 -0
- package/ui/workspace/tab-workspace-host.ts +89 -0
- package/ui/workspace/types.ts +500 -0
- package/ui/workspace/use-location-search.tsx +50 -0
- package/ui/workspace/window-chrome-controls.tsx +116 -0
- package/ui/workspace/workspace-config.ts +47 -0
- package/ui/workspace/workspace-root.tsx +147 -0
- package/ui/workspace/workspace-shell.tsx +292 -0
- package/ui/workspace/workspace-tab-bar.tsx +572 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { getShellDesktopApi, getShellUiBrand } from "@creezio/shell-ui";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration → Support & télémétrie — consentement granulaire flotte.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useCallback, useEffect, useState } from "react";
|
|
10
|
+
import { toast } from "sonner";
|
|
11
|
+
import { Loader2, Shield } from "lucide-react";
|
|
12
|
+
import { Button } from "../primitives/button";
|
|
13
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../primitives/card";
|
|
14
|
+
import { Label } from "../primitives/label";
|
|
15
|
+
import { ServerOperatorNotice } from "./server-mode-cards";
|
|
16
|
+
|
|
17
|
+
type FleetScopeId =
|
|
18
|
+
| "heartbeat"
|
|
19
|
+
| "crashes"
|
|
20
|
+
| "ops"
|
|
21
|
+
| "sessions"
|
|
22
|
+
| "users"
|
|
23
|
+
| "request_logs"
|
|
24
|
+
| "hermes_stats"
|
|
25
|
+
| "hermes_chats"
|
|
26
|
+
| "assistant_chats"
|
|
27
|
+
| "plugins"
|
|
28
|
+
| "actions"
|
|
29
|
+
| "remote_commands";
|
|
30
|
+
|
|
31
|
+
type FleetTelemetryConfig = {
|
|
32
|
+
enabled: boolean;
|
|
33
|
+
scopes: Record<FleetScopeId, boolean>;
|
|
34
|
+
consentAt: string | null;
|
|
35
|
+
consentVersion: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const SCOPE_META: Array<{
|
|
39
|
+
id: FleetScopeId;
|
|
40
|
+
label: string;
|
|
41
|
+
detail: string;
|
|
42
|
+
sensitive?: boolean;
|
|
43
|
+
}> = [
|
|
44
|
+
{
|
|
45
|
+
id: "heartbeat",
|
|
46
|
+
label: "Présence & santé",
|
|
47
|
+
detail: "Online, version, slug, état des services locaux",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "crashes",
|
|
51
|
+
label: "Incidents / crashes",
|
|
52
|
+
detail: "Rapports de crash vers le cockpit support",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "ops",
|
|
56
|
+
label: "Boîte noire (boot & logs)",
|
|
57
|
+
detail: "Décisions de boot, durées, anomalies + tail du log principal — redacté",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "sessions",
|
|
61
|
+
label: "Sessions collaborateurs",
|
|
62
|
+
detail: "Qui est connecté (jamais les mots de passe)",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "users",
|
|
66
|
+
label: "Comptes locaux",
|
|
67
|
+
detail: "Liste users + rôles — pas de secrets / recovery",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "request_logs",
|
|
71
|
+
label: "Logs API / MCP",
|
|
72
|
+
detail: "Échantillon redacté (Authorization / BYOK masqués)",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "hermes_stats",
|
|
76
|
+
label: "Hermes — stats",
|
|
77
|
+
detail: "Compteurs appels / erreurs / latence",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "hermes_chats",
|
|
81
|
+
label: "Hermes — contenu des chats",
|
|
82
|
+
detail: "Messages Work / sessions Hermes",
|
|
83
|
+
sensitive: true,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "assistant_chats",
|
|
87
|
+
label: "Assistant in-app — chats",
|
|
88
|
+
detail: "Conversations assistant",
|
|
89
|
+
sensitive: true,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "plugins",
|
|
93
|
+
label: "Plugins",
|
|
94
|
+
detail: "Inventaire manifest + état",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "actions",
|
|
98
|
+
label: "Journal d’actions",
|
|
99
|
+
detail: "Événements UI / commandes métier agrégés",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "remote_commands",
|
|
103
|
+
label: "Accepter le pilotage distant",
|
|
104
|
+
detail: "Allowlist : update-check, restart embed, envoi diagnostics (pas de shell)",
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
export function DesktopFleetTelemetrySettings() {
|
|
109
|
+
const [desktop, setDesktop] = useState(false);
|
|
110
|
+
const [loading, setLoading] = useState(true);
|
|
111
|
+
const [busy, setBusy] = useState(false);
|
|
112
|
+
const [cfg, setCfg] = useState<FleetTelemetryConfig | null>(null);
|
|
113
|
+
|
|
114
|
+
const refresh = useCallback(async () => {
|
|
115
|
+
const api = getShellDesktopApi();
|
|
116
|
+
if (!api?.getFleetTelemetry) return;
|
|
117
|
+
try {
|
|
118
|
+
setCfg(await api.getFleetTelemetry());
|
|
119
|
+
} catch {
|
|
120
|
+
// Client distant : canal host-only refusé — la section est masquée
|
|
121
|
+
// par HostOnlySettings, ne pas laisser un rejet non catché au montage.
|
|
122
|
+
}
|
|
123
|
+
}, []);
|
|
124
|
+
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
const api = getShellDesktopApi();
|
|
127
|
+
if (!api?.getFleetTelemetry) {
|
|
128
|
+
setLoading(false);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
setDesktop(true);
|
|
132
|
+
void refresh().finally(() => setLoading(false));
|
|
133
|
+
}, [refresh]);
|
|
134
|
+
|
|
135
|
+
async function apply(patch: {
|
|
136
|
+
enabled?: boolean;
|
|
137
|
+
scopes?: Partial<Record<FleetScopeId, boolean>>;
|
|
138
|
+
preset?: "basic" | "off" | "keep";
|
|
139
|
+
}) {
|
|
140
|
+
const api = getShellDesktopApi();
|
|
141
|
+
if (!api?.setFleetTelemetry) return;
|
|
142
|
+
setBusy(true);
|
|
143
|
+
try {
|
|
144
|
+
const next = await api.setFleetTelemetry(patch);
|
|
145
|
+
setCfg(next);
|
|
146
|
+
toast.success("Préférences télémétrie enregistrées");
|
|
147
|
+
} catch (e) {
|
|
148
|
+
toast.error(e instanceof Error ? e.message : "Échec enregistrement");
|
|
149
|
+
} finally {
|
|
150
|
+
setBusy(false);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function onScope(id: FleetScopeId, value: boolean) {
|
|
155
|
+
const meta = SCOPE_META.find((m) => m.id === id);
|
|
156
|
+
if (value && meta?.sensitive) {
|
|
157
|
+
const ok = window.confirm(
|
|
158
|
+
"Je comprends que le contenu des conversations sera visible par le support Creezio.",
|
|
159
|
+
);
|
|
160
|
+
if (!ok) return;
|
|
161
|
+
}
|
|
162
|
+
await apply({ scopes: { [id]: value } });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Web (serveur headless) : consentement posé par l'opérateur de l'instance.
|
|
166
|
+
if (!desktop) return <ServerOperatorNotice label="la télémétrie support" />;
|
|
167
|
+
if (loading || !cfg) {
|
|
168
|
+
return (
|
|
169
|
+
<Card>
|
|
170
|
+
<CardContent className="flex items-center gap-2 py-8 text-sm text-muted-foreground">
|
|
171
|
+
<Loader2 className="h-4 w-4 animate-spin" /> Chargement…
|
|
172
|
+
</CardContent>
|
|
173
|
+
</Card>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Card>
|
|
179
|
+
<CardHeader>
|
|
180
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
181
|
+
<Shield className="h-4 w-4" />
|
|
182
|
+
Support & télémétrie
|
|
183
|
+
</CardTitle>
|
|
184
|
+
<CardDescription>
|
|
185
|
+
Partage optionnel avec Creezio Support. Tout est désactivé par défaut.
|
|
186
|
+
Les secrets (clés IA, JWT, tokens tunnel, mots de passe) ne sont jamais
|
|
187
|
+
envoyés.
|
|
188
|
+
</CardDescription>
|
|
189
|
+
</CardHeader>
|
|
190
|
+
<CardContent className="space-y-6">
|
|
191
|
+
<label className="flex cursor-pointer items-start justify-between gap-4 rounded-md border px-3 py-3">
|
|
192
|
+
<div className="space-y-1">
|
|
193
|
+
<Label className="text-sm font-medium">
|
|
194
|
+
Partager des données avec Creezio Support
|
|
195
|
+
</Label>
|
|
196
|
+
<p className="text-xs text-muted-foreground">
|
|
197
|
+
{cfg.enabled
|
|
198
|
+
? cfg.consentAt
|
|
199
|
+
? `Actif depuis ${new Date(cfg.consentAt).toLocaleString("fr-FR")}`
|
|
200
|
+
: "Actif"
|
|
201
|
+
: "Aucun envoi vers le cockpit flotte tant que c’est désactivé"}
|
|
202
|
+
</p>
|
|
203
|
+
</div>
|
|
204
|
+
<input
|
|
205
|
+
type="checkbox"
|
|
206
|
+
className="mt-1 h-4 w-4"
|
|
207
|
+
checked={cfg.enabled}
|
|
208
|
+
disabled={busy}
|
|
209
|
+
onChange={(e) => void apply({ enabled: e.target.checked })}
|
|
210
|
+
/>
|
|
211
|
+
</label>
|
|
212
|
+
|
|
213
|
+
<div className="flex flex-wrap gap-2">
|
|
214
|
+
<Button
|
|
215
|
+
type="button"
|
|
216
|
+
size="sm"
|
|
217
|
+
variant="secondary"
|
|
218
|
+
disabled={busy}
|
|
219
|
+
onClick={() => void apply({ preset: "basic" })}
|
|
220
|
+
>
|
|
221
|
+
Activer le support basique
|
|
222
|
+
</Button>
|
|
223
|
+
<Button
|
|
224
|
+
type="button"
|
|
225
|
+
size="sm"
|
|
226
|
+
variant="outline"
|
|
227
|
+
disabled={busy}
|
|
228
|
+
onClick={() => void apply({ preset: "off" })}
|
|
229
|
+
>
|
|
230
|
+
Tout désactiver
|
|
231
|
+
</Button>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<div
|
|
235
|
+
className={`space-y-3 ${cfg.enabled ? "" : "pointer-events-none opacity-50"}`}
|
|
236
|
+
>
|
|
237
|
+
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
238
|
+
Catégories
|
|
239
|
+
</p>
|
|
240
|
+
{SCOPE_META.map((m) => (
|
|
241
|
+
<label
|
|
242
|
+
key={m.id}
|
|
243
|
+
className="flex cursor-pointer items-start justify-between gap-4 border-b border-border/60 pb-3 last:border-0"
|
|
244
|
+
>
|
|
245
|
+
<div className="min-w-0 space-y-0.5">
|
|
246
|
+
<span className="text-sm font-medium">
|
|
247
|
+
{m.label}
|
|
248
|
+
{m.sensitive ? (
|
|
249
|
+
<span className="ml-2 text-[10px] font-normal uppercase text-amber-700">
|
|
250
|
+
sensible
|
|
251
|
+
</span>
|
|
252
|
+
) : null}
|
|
253
|
+
</span>
|
|
254
|
+
<p className="text-xs text-muted-foreground">{m.detail}</p>
|
|
255
|
+
</div>
|
|
256
|
+
<input
|
|
257
|
+
type="checkbox"
|
|
258
|
+
className="mt-1 h-4 w-4"
|
|
259
|
+
checked={cfg.scopes[m.id] === true}
|
|
260
|
+
disabled={busy || !cfg.enabled}
|
|
261
|
+
onChange={(e) => void onScope(m.id, e.target.checked)}
|
|
262
|
+
/>
|
|
263
|
+
</label>
|
|
264
|
+
))}
|
|
265
|
+
</div>
|
|
266
|
+
</CardContent>
|
|
267
|
+
</Card>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { getShellDesktopApi, getShellUiBrand } from "@creezio/shell-ui";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hermes — stack native Creezio (toujours embarqué sur Héberger).
|
|
7
|
+
* Statut + ouverture WebUI + logs. Pas de mode distant/désactivé.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useCallback, useEffect, useState } from "react";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
import { Bot, Download, ExternalLink, Loader2, RefreshCw } from "lucide-react";
|
|
13
|
+
import { Button } from "../primitives/button";
|
|
14
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../primitives/card";
|
|
15
|
+
import { useTabWorkspaceOptional } from "../workspace/tab-workspace-host";
|
|
16
|
+
import {
|
|
17
|
+
isHermesWebuiOpenTarget,
|
|
18
|
+
resolveHermesWebuiOpenTarget,
|
|
19
|
+
} from "../lib/hermes-ui";
|
|
20
|
+
import { LockedConfigField } from "./locked-config-field";
|
|
21
|
+
import { DesktopEmbedEnvPanel } from "./desktop-embed-env-panel";
|
|
22
|
+
import { ServerServiceStatusCard } from "./server-mode-cards";
|
|
23
|
+
|
|
24
|
+
type HermesStatus = {
|
|
25
|
+
status: string;
|
|
26
|
+
mode: string;
|
|
27
|
+
apiUrl: string | null;
|
|
28
|
+
webuiUrl: string | null;
|
|
29
|
+
webuiStatus: string;
|
|
30
|
+
binaryFound: boolean;
|
|
31
|
+
binaryPath: string | null;
|
|
32
|
+
version: string | null;
|
|
33
|
+
detail: string;
|
|
34
|
+
homeDir: string | null;
|
|
35
|
+
bootstrapPhase: string;
|
|
36
|
+
bootstrapError: string | null;
|
|
37
|
+
installing: boolean;
|
|
38
|
+
logs: string[];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function statusTone(status: string): string {
|
|
42
|
+
if (status === "running") {
|
|
43
|
+
return "border-emerald-200 bg-emerald-50 text-emerald-900";
|
|
44
|
+
}
|
|
45
|
+
if (status === "installing" || status === "starting") {
|
|
46
|
+
return "border-sky-200 bg-sky-50 text-sky-900";
|
|
47
|
+
}
|
|
48
|
+
if (status === "missing" || status === "error") {
|
|
49
|
+
return "border-amber-200 bg-amber-50 text-amber-900";
|
|
50
|
+
}
|
|
51
|
+
if (status === "skipped-remote-client" || status === "remote") {
|
|
52
|
+
return "border-sky-200 bg-sky-50 text-sky-900";
|
|
53
|
+
}
|
|
54
|
+
return "border-slate-200 bg-slate-50 text-slate-800";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function statusLabel(status: string): string {
|
|
58
|
+
switch (status) {
|
|
59
|
+
case "running":
|
|
60
|
+
return "En cours";
|
|
61
|
+
case "stopped":
|
|
62
|
+
return "Arrêté";
|
|
63
|
+
case "missing":
|
|
64
|
+
return "Runtime absent";
|
|
65
|
+
case "installing":
|
|
66
|
+
return "Installation…";
|
|
67
|
+
case "starting":
|
|
68
|
+
return "Démarrage…";
|
|
69
|
+
case "error":
|
|
70
|
+
return "Erreur";
|
|
71
|
+
case "skipped-remote-client":
|
|
72
|
+
return "Via l’hôte (Rejoindre)";
|
|
73
|
+
case "remote":
|
|
74
|
+
return "Via tunnel hôte";
|
|
75
|
+
default:
|
|
76
|
+
return status;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function webuiLabel(status: string): string {
|
|
81
|
+
switch (status) {
|
|
82
|
+
case "running":
|
|
83
|
+
return "WebUI kanban actif";
|
|
84
|
+
case "missing":
|
|
85
|
+
return "WebUI absent";
|
|
86
|
+
case "error":
|
|
87
|
+
return "WebUI erreur";
|
|
88
|
+
case "skipped":
|
|
89
|
+
return "WebUI via tunnel";
|
|
90
|
+
case "stopped":
|
|
91
|
+
return "WebUI arrêté";
|
|
92
|
+
default:
|
|
93
|
+
return status || "—";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function DesktopHermesSettings() {
|
|
98
|
+
const workspace = useTabWorkspaceOptional();
|
|
99
|
+
const [desktop, setDesktop] = useState(false);
|
|
100
|
+
const [loading, setLoading] = useState(true);
|
|
101
|
+
const [installing, setInstalling] = useState(false);
|
|
102
|
+
const [status, setStatus] = useState<HermesStatus | null>(null);
|
|
103
|
+
const [showLogs, setShowLogs] = useState(false);
|
|
104
|
+
const [tunnelHermes, setTunnelHermes] = useState<string | null>(null);
|
|
105
|
+
|
|
106
|
+
const refresh = useCallback(async () => {
|
|
107
|
+
const api = getShellDesktopApi();
|
|
108
|
+
if (!api?.getHermesStatus) return;
|
|
109
|
+
setLoading(true);
|
|
110
|
+
try {
|
|
111
|
+
setStatus((await api.getHermesStatus()) as HermesStatus);
|
|
112
|
+
if (api.getTunnelStatus) {
|
|
113
|
+
const t = (await api.getTunnelStatus()) as {
|
|
114
|
+
publicUrls?: { hermes?: string | null };
|
|
115
|
+
};
|
|
116
|
+
setTunnelHermes(t.publicUrls?.hermes || null);
|
|
117
|
+
}
|
|
118
|
+
} finally {
|
|
119
|
+
setLoading(false);
|
|
120
|
+
}
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
const api = getShellDesktopApi();
|
|
125
|
+
if (!api?.getHermesStatus) return;
|
|
126
|
+
setDesktop(true);
|
|
127
|
+
void refresh();
|
|
128
|
+
}, [refresh]);
|
|
129
|
+
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (!status?.installing && status?.status !== "starting" && !installing) return;
|
|
132
|
+
const t = setInterval(() => {
|
|
133
|
+
void refresh();
|
|
134
|
+
}, 2500);
|
|
135
|
+
return () => clearInterval(t);
|
|
136
|
+
}, [status?.installing, status?.status, installing, refresh]);
|
|
137
|
+
|
|
138
|
+
// Web (serveur headless) : statut via /api/v1/os/hermes/status.
|
|
139
|
+
if (!desktop) return <ServerServiceStatusCard kind="hermes" />;
|
|
140
|
+
|
|
141
|
+
async function onInstallRuntime() {
|
|
142
|
+
const api = getShellDesktopApi();
|
|
143
|
+
if (!api?.ensureHermesRuntime) {
|
|
144
|
+
toast.error("API ensureHermesRuntime indisponible — recompilez Electron.");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
setInstalling(true);
|
|
148
|
+
try {
|
|
149
|
+
const r = await api.ensureHermesRuntime();
|
|
150
|
+
if (r.ok) {
|
|
151
|
+
const restarted = api.retryHermes ? await api.retryHermes() : null;
|
|
152
|
+
if (restarted?.webuiUrl) {
|
|
153
|
+
toast.success("Hermes réparé et relancé.");
|
|
154
|
+
} else {
|
|
155
|
+
toast.error(
|
|
156
|
+
restarted?.detail ||
|
|
157
|
+
"Runtime réparé, mais Hermes ne répond pas. Consultez les logs.",
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
toast.error(r.detail || "Échec installation runtime");
|
|
162
|
+
}
|
|
163
|
+
await refresh();
|
|
164
|
+
} catch (e) {
|
|
165
|
+
toast.error(e instanceof Error ? e.message : "Erreur bootstrap");
|
|
166
|
+
} finally {
|
|
167
|
+
setInstalling(false);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function onOpenWebuiTab() {
|
|
172
|
+
let retryToast: string | number | undefined;
|
|
173
|
+
const target = await resolveHermesWebuiOpenTarget({
|
|
174
|
+
onRetry: () => {
|
|
175
|
+
retryToast = toast.loading("Relance de l’API et de la WebUI Hermes…");
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
if (retryToast !== undefined) toast.dismiss(retryToast);
|
|
179
|
+
if (!isHermesWebuiOpenTarget(target)) {
|
|
180
|
+
toast.error(target.error);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (workspace?.openExternalSite) {
|
|
184
|
+
workspace.openExternalSite({
|
|
185
|
+
siteId: target.siteId,
|
|
186
|
+
url: target.url,
|
|
187
|
+
title: target.title,
|
|
188
|
+
});
|
|
189
|
+
toast.success("Onglet Hermes ouvert");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const api = getShellDesktopApi();
|
|
193
|
+
if (api?.openTab) {
|
|
194
|
+
try {
|
|
195
|
+
await api.openTab(target.siteId, target.url);
|
|
196
|
+
toast.success("Onglet Hermes ouvert");
|
|
197
|
+
return;
|
|
198
|
+
} catch (e) {
|
|
199
|
+
toast.error(e instanceof Error ? e.message : "Ouverture impossible");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
window.open(target.url, "_blank", "noreferrer");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const busy = installing || Boolean(status?.installing);
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<Card>
|
|
210
|
+
<CardHeader>
|
|
211
|
+
<CardTitle className="flex items-center gap-2 text-base">
|
|
212
|
+
<Bot className="h-4 w-4" /> Hermes Agent
|
|
213
|
+
</CardTitle>
|
|
214
|
+
<CardDescription>
|
|
215
|
+
Agents natifs Creezio. Workspace et HOME confinés dans l’OS Creezio
|
|
216
|
+
— jamais dans C:\Users\…
|
|
217
|
+
</CardDescription>
|
|
218
|
+
</CardHeader>
|
|
219
|
+
<CardContent className="space-y-4">
|
|
220
|
+
<div
|
|
221
|
+
className={`rounded-md border px-3 py-2 text-sm ${statusTone(status?.status || "")}`}
|
|
222
|
+
>
|
|
223
|
+
{loading || !status ? (
|
|
224
|
+
<span className="inline-flex items-center gap-2">
|
|
225
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" /> Chargement…
|
|
226
|
+
</span>
|
|
227
|
+
) : (
|
|
228
|
+
<>
|
|
229
|
+
<div className="font-medium">
|
|
230
|
+
{statusLabel(status.status)}
|
|
231
|
+
{status.version ? ` · v${status.version}` : ""}
|
|
232
|
+
</div>
|
|
233
|
+
<div className="mt-0.5 text-xs opacity-90">{status.detail}</div>
|
|
234
|
+
<div className="mt-1 text-xs opacity-90">
|
|
235
|
+
{webuiLabel(status.webuiStatus)}
|
|
236
|
+
</div>
|
|
237
|
+
{status.bootstrapError ? (
|
|
238
|
+
<div className="mt-1 text-xs text-amber-800">{status.bootstrapError}</div>
|
|
239
|
+
) : null}
|
|
240
|
+
</>
|
|
241
|
+
)}
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div>
|
|
245
|
+
<div className="mb-2 text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
246
|
+
Service (pré-configuré)
|
|
247
|
+
</div>
|
|
248
|
+
<div className="grid gap-2 sm:grid-cols-2">
|
|
249
|
+
<LockedConfigField
|
|
250
|
+
label="Mode"
|
|
251
|
+
value="Embarqué (natif Creezio)"
|
|
252
|
+
/>
|
|
253
|
+
<LockedConfigField
|
|
254
|
+
label="home_mode"
|
|
255
|
+
value="profile"
|
|
256
|
+
hint="HOME outils = hermes-home/home (sandbox OS)."
|
|
257
|
+
/>
|
|
258
|
+
<LockedConfigField
|
|
259
|
+
label="Workspace agent"
|
|
260
|
+
value={
|
|
261
|
+
status?.homeDir
|
|
262
|
+
? `${status.homeDir.replace(/[/\\]+$/, "")}/workspace`
|
|
263
|
+
: "…/hermes-home/workspace"
|
|
264
|
+
}
|
|
265
|
+
hint="Seul répertoire de travail autorisé."
|
|
266
|
+
/>
|
|
267
|
+
<LockedConfigField
|
|
268
|
+
label="HERMES_HOME"
|
|
269
|
+
value={status?.homeDir || "…/hermes-home"}
|
|
270
|
+
/>
|
|
271
|
+
<LockedConfigField
|
|
272
|
+
label="API locale"
|
|
273
|
+
value={status?.apiUrl || "http://127.0.0.1:18642"}
|
|
274
|
+
/>
|
|
275
|
+
<LockedConfigField
|
|
276
|
+
label="WebUI locale"
|
|
277
|
+
value={status?.webuiUrl || "http://127.0.0.1:18797"}
|
|
278
|
+
/>
|
|
279
|
+
<LockedConfigField
|
|
280
|
+
label="URL publique tunnel"
|
|
281
|
+
value={tunnelHermes || "Tunnel non réservé"}
|
|
282
|
+
/>
|
|
283
|
+
<LockedConfigField
|
|
284
|
+
label="GATEWAY_ALLOW_ALL_USERS"
|
|
285
|
+
value="true"
|
|
286
|
+
hint="Desktop local — messagerie multi-user non utilisée."
|
|
287
|
+
/>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<DesktopEmbedEnvPanel service="hermes" />
|
|
292
|
+
|
|
293
|
+
<div className="flex flex-wrap gap-2">
|
|
294
|
+
<Button
|
|
295
|
+
size="sm"
|
|
296
|
+
variant="secondary"
|
|
297
|
+
onClick={() => void onOpenWebuiTab()}
|
|
298
|
+
disabled={busy}
|
|
299
|
+
>
|
|
300
|
+
<ExternalLink className="mr-1 h-3.5 w-3.5" />
|
|
301
|
+
Ouvrir dans un onglet
|
|
302
|
+
</Button>
|
|
303
|
+
<Button
|
|
304
|
+
size="sm"
|
|
305
|
+
variant="outline"
|
|
306
|
+
onClick={() => void onInstallRuntime()}
|
|
307
|
+
disabled={busy || status?.status === "skipped-remote-client"}
|
|
308
|
+
>
|
|
309
|
+
{installing || status?.installing ? (
|
|
310
|
+
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
|
311
|
+
) : (
|
|
312
|
+
<Download className="mr-1 h-3.5 w-3.5" />
|
|
313
|
+
)}
|
|
314
|
+
Réparer
|
|
315
|
+
</Button>
|
|
316
|
+
<Button
|
|
317
|
+
size="sm"
|
|
318
|
+
variant="outline"
|
|
319
|
+
onClick={() => void refresh()}
|
|
320
|
+
disabled={loading}
|
|
321
|
+
>
|
|
322
|
+
<RefreshCw className="mr-1 h-3.5 w-3.5" /> Actualiser
|
|
323
|
+
</Button>
|
|
324
|
+
<Button
|
|
325
|
+
size="sm"
|
|
326
|
+
variant="ghost"
|
|
327
|
+
onClick={() => setShowLogs((v) => !v)}
|
|
328
|
+
>
|
|
329
|
+
{showLogs ? "Masquer les logs" : "Logs"}
|
|
330
|
+
</Button>
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
{showLogs && status?.logs?.length ? (
|
|
334
|
+
<pre className="max-h-48 overflow-auto rounded-md border bg-slate-950 p-2 font-mono text-[11px] leading-relaxed text-slate-100">
|
|
335
|
+
{status.logs.join("\n")}
|
|
336
|
+
</pre>
|
|
337
|
+
) : null}
|
|
338
|
+
</CardContent>
|
|
339
|
+
</Card>
|
|
340
|
+
);
|
|
341
|
+
}
|