@creezio/observability 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.
Files changed (209) hide show
  1. package/README.md +328 -0
  2. package/dist/.creezio-src-hash.json +4 -0
  3. package/dist/api-endpoints/http-routes.d.ts +11 -0
  4. package/dist/api-endpoints/http-routes.d.ts.map +1 -0
  5. package/dist/api-endpoints/http-routes.js +11 -0
  6. package/dist/api-endpoints/http-routes.js.map +1 -0
  7. package/dist/api-endpoints/index.d.ts +4 -0
  8. package/dist/api-endpoints/index.d.ts.map +1 -0
  9. package/dist/api-endpoints/index.js +3 -0
  10. package/dist/api-endpoints/index.js.map +1 -0
  11. package/dist/api-endpoints/registry.d.ts +66 -0
  12. package/dist/api-endpoints/registry.d.ts.map +1 -0
  13. package/dist/api-endpoints/registry.js +102 -0
  14. package/dist/api-endpoints/registry.js.map +1 -0
  15. package/dist/api-mount.d.ts +7 -0
  16. package/dist/api-mount.d.ts.map +1 -0
  17. package/dist/api-mount.js +114 -0
  18. package/dist/api-mount.js.map +1 -0
  19. package/dist/helpers.d.ts +23 -0
  20. package/dist/helpers.d.ts.map +1 -0
  21. package/dist/helpers.js +36 -0
  22. package/dist/helpers.js.map +1 -0
  23. package/dist/index.d.ts +35 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +24 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/memory-store.d.ts +3 -0
  28. package/dist/memory-store.d.ts.map +1 -0
  29. package/dist/memory-store.js +113 -0
  30. package/dist/memory-store.js.map +1 -0
  31. package/dist/ops/emit.d.ts +8 -0
  32. package/dist/ops/emit.d.ts.map +1 -0
  33. package/dist/ops/emit.js +15 -0
  34. package/dist/ops/emit.js.map +1 -0
  35. package/dist/ops/fleet-activity.d.ts +50 -0
  36. package/dist/ops/fleet-activity.d.ts.map +1 -0
  37. package/dist/ops/fleet-activity.js +123 -0
  38. package/dist/ops/fleet-activity.js.map +1 -0
  39. package/dist/ops/fleet-agent.d.ts +102 -0
  40. package/dist/ops/fleet-agent.d.ts.map +1 -0
  41. package/dist/ops/fleet-agent.js +352 -0
  42. package/dist/ops/fleet-agent.js.map +1 -0
  43. package/dist/ops/fleet-samples.d.ts +33 -0
  44. package/dist/ops/fleet-samples.d.ts.map +1 -0
  45. package/dist/ops/fleet-samples.js +223 -0
  46. package/dist/ops/fleet-samples.js.map +1 -0
  47. package/dist/ops/journal.d.ts +56 -0
  48. package/dist/ops/journal.d.ts.map +1 -0
  49. package/dist/ops/journal.js +307 -0
  50. package/dist/ops/journal.js.map +1 -0
  51. package/dist/ops/rules.d.ts +16 -0
  52. package/dist/ops/rules.d.ts.map +1 -0
  53. package/dist/ops/rules.js +98 -0
  54. package/dist/ops/rules.js.map +1 -0
  55. package/dist/ops/types.d.ts +69 -0
  56. package/dist/ops/types.d.ts.map +1 -0
  57. package/dist/ops/types.js +119 -0
  58. package/dist/ops/types.js.map +1 -0
  59. package/dist/request-logs/config.d.ts +19 -0
  60. package/dist/request-logs/config.d.ts.map +1 -0
  61. package/dist/request-logs/config.js +36 -0
  62. package/dist/request-logs/config.js.map +1 -0
  63. package/dist/request-logs/http-routes.d.ts +8 -0
  64. package/dist/request-logs/http-routes.d.ts.map +1 -0
  65. package/dist/request-logs/http-routes.js +29 -0
  66. package/dist/request-logs/http-routes.js.map +1 -0
  67. package/dist/request-logs/index.d.ts +6 -0
  68. package/dist/request-logs/index.d.ts.map +1 -0
  69. package/dist/request-logs/index.js +5 -0
  70. package/dist/request-logs/index.js.map +1 -0
  71. package/dist/request-logs/middleware.d.ts +11 -0
  72. package/dist/request-logs/middleware.d.ts.map +1 -0
  73. package/dist/request-logs/middleware.js +198 -0
  74. package/dist/request-logs/middleware.js.map +1 -0
  75. package/dist/request-logs/request-logs.d.ts +60 -0
  76. package/dist/request-logs/request-logs.d.ts.map +1 -0
  77. package/dist/request-logs/request-logs.js +260 -0
  78. package/dist/request-logs/request-logs.js.map +1 -0
  79. package/dist/schema.d.ts +3 -0
  80. package/dist/schema.d.ts.map +1 -0
  81. package/dist/schema.js +21 -0
  82. package/dist/schema.js.map +1 -0
  83. package/dist/sqlite-driver.d.ts +16 -0
  84. package/dist/sqlite-driver.d.ts.map +1 -0
  85. package/dist/sqlite-driver.js +15 -0
  86. package/dist/sqlite-driver.js.map +1 -0
  87. package/dist/sqlite-store.d.ts +12 -0
  88. package/dist/sqlite-store.d.ts.map +1 -0
  89. package/dist/sqlite-store.js +176 -0
  90. package/dist/sqlite-store.js.map +1 -0
  91. package/dist/types.d.ts +67 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +9 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/usage/adapters.d.ts +31 -0
  96. package/dist/usage/adapters.d.ts.map +1 -0
  97. package/dist/usage/adapters.js +37 -0
  98. package/dist/usage/adapters.js.map +1 -0
  99. package/dist/usage/http-routes.d.ts +23 -0
  100. package/dist/usage/http-routes.d.ts.map +1 -0
  101. package/dist/usage/http-routes.js +150 -0
  102. package/dist/usage/http-routes.js.map +1 -0
  103. package/dist/usage/index.d.ts +11 -0
  104. package/dist/usage/index.d.ts.map +1 -0
  105. package/dist/usage/index.js +7 -0
  106. package/dist/usage/index.js.map +1 -0
  107. package/dist/usage/ui-brand.d.ts +23 -0
  108. package/dist/usage/ui-brand.d.ts.map +1 -0
  109. package/dist/usage/ui-brand.js +18 -0
  110. package/dist/usage/ui-brand.js.map +1 -0
  111. package/dist/usage/usage-analytics-productivity.d.ts +73 -0
  112. package/dist/usage/usage-analytics-productivity.d.ts.map +1 -0
  113. package/dist/usage/usage-analytics-productivity.js +317 -0
  114. package/dist/usage/usage-analytics-productivity.js.map +1 -0
  115. package/dist/usage/usage-analytics-shared.d.ts +5 -0
  116. package/dist/usage/usage-analytics-shared.d.ts.map +1 -0
  117. package/dist/usage/usage-analytics-shared.js +20 -0
  118. package/dist/usage/usage-analytics-shared.js.map +1 -0
  119. package/dist/usage/usage-analytics.d.ts +127 -0
  120. package/dist/usage/usage-analytics.d.ts.map +1 -0
  121. package/dist/usage/usage-analytics.js +338 -0
  122. package/dist/usage/usage-analytics.js.map +1 -0
  123. package/dist-cjs/api-endpoints/http-routes.js +14 -0
  124. package/dist-cjs/api-endpoints/http-routes.js.map +1 -0
  125. package/dist-cjs/api-endpoints/index.js +10 -0
  126. package/dist-cjs/api-endpoints/index.js.map +1 -0
  127. package/dist-cjs/api-endpoints/registry.js +107 -0
  128. package/dist-cjs/api-endpoints/registry.js.map +1 -0
  129. package/dist-cjs/api-mount.js +117 -0
  130. package/dist-cjs/api-mount.js.map +1 -0
  131. package/dist-cjs/helpers.js +41 -0
  132. package/dist-cjs/helpers.js.map +1 -0
  133. package/dist-cjs/index.js +118 -0
  134. package/dist-cjs/index.js.map +1 -0
  135. package/dist-cjs/memory-store.js +119 -0
  136. package/dist-cjs/memory-store.js.map +1 -0
  137. package/dist-cjs/ops/emit.js +18 -0
  138. package/dist-cjs/ops/emit.js.map +1 -0
  139. package/dist-cjs/ops/fleet-activity.js +131 -0
  140. package/dist-cjs/ops/fleet-activity.js.map +1 -0
  141. package/dist-cjs/ops/fleet-agent.js +358 -0
  142. package/dist-cjs/ops/fleet-agent.js.map +1 -0
  143. package/dist-cjs/ops/fleet-samples.js +229 -0
  144. package/dist-cjs/ops/fleet-samples.js.map +1 -0
  145. package/dist-cjs/ops/journal.js +326 -0
  146. package/dist-cjs/ops/journal.js.map +1 -0
  147. package/dist-cjs/ops/rules.js +102 -0
  148. package/dist-cjs/ops/rules.js.map +1 -0
  149. package/dist-cjs/ops/types.js +126 -0
  150. package/dist-cjs/ops/types.js.map +1 -0
  151. package/dist-cjs/package.json +3 -0
  152. package/dist-cjs/request-logs/config.js +42 -0
  153. package/dist-cjs/request-logs/config.js.map +1 -0
  154. package/dist-cjs/request-logs/http-routes.js +32 -0
  155. package/dist-cjs/request-logs/http-routes.js.map +1 -0
  156. package/dist-cjs/request-logs/index.js +27 -0
  157. package/dist-cjs/request-logs/index.js.map +1 -0
  158. package/dist-cjs/request-logs/middleware.js +203 -0
  159. package/dist-cjs/request-logs/middleware.js.map +1 -0
  160. package/dist-cjs/request-logs/request-logs.js +277 -0
  161. package/dist-cjs/request-logs/request-logs.js.map +1 -0
  162. package/dist-cjs/schema.js +24 -0
  163. package/dist-cjs/schema.js.map +1 -0
  164. package/dist-cjs/sqlite-driver.js +18 -0
  165. package/dist-cjs/sqlite-driver.js.map +1 -0
  166. package/dist-cjs/sqlite-store.js +182 -0
  167. package/dist-cjs/sqlite-store.js.map +1 -0
  168. package/dist-cjs/types.js +12 -0
  169. package/dist-cjs/types.js.map +1 -0
  170. package/dist-cjs/usage/adapters.js +47 -0
  171. package/dist-cjs/usage/adapters.js.map +1 -0
  172. package/dist-cjs/usage/http-routes.js +154 -0
  173. package/dist-cjs/usage/http-routes.js.map +1 -0
  174. package/dist-cjs/usage/index.js +34 -0
  175. package/dist-cjs/usage/index.js.map +1 -0
  176. package/dist-cjs/usage/ui-brand.js +23 -0
  177. package/dist-cjs/usage/ui-brand.js.map +1 -0
  178. package/dist-cjs/usage/usage-analytics-productivity.js +321 -0
  179. package/dist-cjs/usage/usage-analytics-productivity.js.map +1 -0
  180. package/dist-cjs/usage/usage-analytics-shared.js +23 -0
  181. package/dist-cjs/usage/usage-analytics-shared.js.map +1 -0
  182. package/dist-cjs/usage/usage-analytics.js +353 -0
  183. package/dist-cjs/usage/usage-analytics.js.map +1 -0
  184. package/fleet-collector/AGENTS.md +7 -0
  185. package/fleet-collector/README.md +83 -0
  186. package/fleet-collector/configure-fleet-npm.sh +108 -0
  187. package/fleet-collector/env.mjs +174 -0
  188. package/fleet-collector/ops-api.mjs +455 -0
  189. package/fleet-collector/public/admin.html +696 -0
  190. package/fleet-collector/public/index.html +941 -0
  191. package/fleet-collector/server.mjs +564 -0
  192. package/fleet-collector/test-fleet-collector.mjs +413 -0
  193. package/fleet-collector/test-server-admin.mjs +475 -0
  194. package/package.json +109 -0
  195. package/scripts/test-api-endpoints-registry.mjs +60 -0
  196. package/ui/analytics-client.tsx +834 -0
  197. package/ui/analytics-productivity-panel.tsx +513 -0
  198. package/ui/api-endpoints-client.tsx +228 -0
  199. package/ui/index.ts +39 -0
  200. package/ui/primitives/badge.tsx +37 -0
  201. package/ui/primitives/button.tsx +44 -0
  202. package/ui/primitives/card.tsx +44 -0
  203. package/ui/primitives/cn.ts +6 -0
  204. package/ui/primitives/input.tsx +19 -0
  205. package/ui/primitives/tabs.tsx +51 -0
  206. package/ui/request-logs-client.tsx +327 -0
  207. package/ui/session-usage-analytics-provider.tsx +28 -0
  208. package/ui/usage-analytics-client.ts +479 -0
  209. package/ui/usage-analytics-provider.tsx +84 -0
@@ -0,0 +1,327 @@
1
+ "use client";
2
+
3
+ import { Fragment, useCallback, useEffect, useState } from "react";
4
+ import { ChevronDown, ChevronRight, RefreshCw, Trash2 } from "lucide-react";
5
+ import { Badge } from "./primitives/badge";
6
+ import { Button } from "./primitives/button";
7
+ import { cn } from "./primitives/cn";
8
+ import type { RequestLogEntry, RequestLogSource } from "../dist/request-logs/request-logs.js";
9
+
10
+ type SourceFilter = "all" | RequestLogSource;
11
+
12
+ type ApiResponse = {
13
+ logs: RequestLogEntry[];
14
+ total: number;
15
+ capacity: number;
16
+ };
17
+
18
+ function formatTs(iso: string): string {
19
+ try {
20
+ return new Date(iso).toLocaleString("fr-FR", {
21
+ day: "2-digit",
22
+ month: "2-digit",
23
+ hour: "2-digit",
24
+ minute: "2-digit",
25
+ second: "2-digit",
26
+ });
27
+ } catch {
28
+ return iso;
29
+ }
30
+ }
31
+
32
+ function methodLabel(entry: RequestLogEntry): string {
33
+ if (entry.source === "mcp") {
34
+ if (entry.detail.tool) return entry.detail.tool;
35
+ if (entry.detail.jsonrpcMethod) return entry.detail.jsonrpcMethod;
36
+ }
37
+ return entry.method;
38
+ }
39
+
40
+ function isErrorEntry(entry: RequestLogEntry): boolean {
41
+ return (
42
+ entry.status >= 400 ||
43
+ entry.detail.ok === false ||
44
+ Boolean(entry.detail.error)
45
+ );
46
+ }
47
+
48
+ function statusVariant(
49
+ entry: RequestLogEntry,
50
+ ): "success" | "danger" | "warning" | "muted" {
51
+ if (isErrorEntry(entry)) return "danger";
52
+ if (entry.status >= 300) return "warning";
53
+ if (entry.status > 0) return "success";
54
+ return "muted";
55
+ }
56
+
57
+ export function RequestLogsClient() {
58
+ const [logs, setLogs] = useState<RequestLogEntry[]>([]);
59
+ const [total, setTotal] = useState(0);
60
+ const [capacity, setCapacity] = useState(1000);
61
+ const [source, setSource] = useState<SourceFilter>("all");
62
+ const [errorsOnly, setErrorsOnly] = useState(false);
63
+ const [q, setQ] = useState("");
64
+ const [qDebounced, setQDebounced] = useState("");
65
+ const [expanded, setExpanded] = useState<Record<string, boolean>>({});
66
+ const [loading, setLoading] = useState(false);
67
+ const [error, setError] = useState<string | null>(null);
68
+ const [autoRefresh, setAutoRefresh] = useState(true);
69
+
70
+ useEffect(() => {
71
+ const t = window.setTimeout(() => setQDebounced(q.trim()), 250);
72
+ return () => window.clearTimeout(t);
73
+ }, [q]);
74
+
75
+ const load = useCallback(async () => {
76
+ setLoading(true);
77
+ setError(null);
78
+ try {
79
+ const params = new URLSearchParams({ limit: "200" });
80
+ if (source !== "all") params.set("source", source);
81
+ if (errorsOnly) params.set("errorsOnly", "1");
82
+ if (qDebounced) params.set("q", qDebounced);
83
+ const res = await fetch(`/api/v1/admin/request-logs?${params}`);
84
+ if (!res.ok) {
85
+ const body = (await res.json().catch(() => ({}))) as { error?: string };
86
+ throw new Error(body.error || `HTTP ${res.status}`);
87
+ }
88
+ const data = (await res.json()) as ApiResponse;
89
+ setLogs(data.logs || []);
90
+ setTotal(data.total ?? 0);
91
+ setCapacity(data.capacity ?? 1000);
92
+ } catch (e) {
93
+ setError(e instanceof Error ? e.message : "Erreur de chargement");
94
+ } finally {
95
+ setLoading(false);
96
+ }
97
+ }, [source, errorsOnly, qDebounced]);
98
+
99
+ useEffect(() => {
100
+ void load();
101
+ }, [load]);
102
+
103
+ useEffect(() => {
104
+ if (!autoRefresh) return;
105
+ const id = window.setInterval(() => void load(), 2500);
106
+ return () => window.clearInterval(id);
107
+ }, [autoRefresh, load]);
108
+
109
+ async function clearLogs() {
110
+ if (!window.confirm("Vider tous les logs en mémoire ?")) return;
111
+ try {
112
+ const res = await fetch("/api/v1/admin/request-logs", { method: "DELETE" });
113
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
114
+ setExpanded({});
115
+ await load();
116
+ } catch (e) {
117
+ setError(e instanceof Error ? e.message : "Échec purge");
118
+ }
119
+ }
120
+
121
+ function toggle(id: string) {
122
+ setExpanded((prev) => ({ ...prev, [id]: !prev[id] }));
123
+ }
124
+
125
+ const filters: { id: SourceFilter; label: string }[] = [
126
+ { id: "all", label: "Tous" },
127
+ { id: "mcp", label: "MCP" },
128
+ { id: "api", label: "API" },
129
+ ];
130
+
131
+ return (
132
+ <div className="mt-2 space-y-4">
133
+ <div className="flex flex-wrap items-center gap-2">
134
+ <div className="flex rounded-lg border bg-white p-0.5 shadow-sm">
135
+ {filters.map((f) => (
136
+ <button
137
+ key={f.id}
138
+ type="button"
139
+ onClick={() => setSource(f.id)}
140
+ className={cn(
141
+ "rounded-md px-3 py-1.5 text-sm transition-colors",
142
+ source === f.id
143
+ ? "bg-slate-900 text-white"
144
+ : "text-slate-600 hover:bg-slate-50",
145
+ )}
146
+ >
147
+ {f.label}
148
+ </button>
149
+ ))}
150
+ </div>
151
+ <label className="flex items-center gap-2 rounded-lg border bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm">
152
+ <input
153
+ type="checkbox"
154
+ checked={errorsOnly}
155
+ onChange={(e) => setErrorsOnly(e.target.checked)}
156
+ className="rounded border-slate-300"
157
+ />
158
+ Erreurs seulement
159
+ </label>
160
+ <input
161
+ type="search"
162
+ value={q}
163
+ onChange={(e) => setQ(e.target.value)}
164
+ placeholder="Rechercher (tool, path, erreur…)"
165
+ className="min-w-[14rem] flex-1 rounded-lg border bg-white px-3 py-1.5 text-sm shadow-sm outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
166
+ />
167
+ <label className="flex items-center gap-2 rounded-lg border bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm">
168
+ <input
169
+ type="checkbox"
170
+ checked={autoRefresh}
171
+ onChange={(e) => setAutoRefresh(e.target.checked)}
172
+ className="rounded border-slate-300"
173
+ />
174
+ Auto-refresh
175
+ </label>
176
+ <Button
177
+ type="button"
178
+ variant="outline"
179
+ size="sm"
180
+ onClick={() => void load()}
181
+ disabled={loading}
182
+ >
183
+ <RefreshCw className={cn("mr-1.5 h-3.5 w-3.5", loading && "animate-spin")} />
184
+ Actualiser
185
+ </Button>
186
+ <Button type="button" variant="outline" size="sm" onClick={() => void clearLogs()}>
187
+ <Trash2 className="mr-1.5 h-3.5 w-3.5" />
188
+ Vider
189
+ </Button>
190
+ </div>
191
+
192
+ <div className="text-xs text-slate-500">
193
+ {total.toLocaleString("fr-FR")} entrée(s) affichable(s) — buffer{" "}
194
+ {capacity.toLocaleString("fr-FR")} (mémoire processus local)
195
+ </div>
196
+
197
+ {error ? (
198
+ <div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800">
199
+ {error}
200
+ </div>
201
+ ) : null}
202
+
203
+ <div className="overflow-x-auto rounded-xl border bg-white shadow-sm">
204
+ <table className="min-w-[960px] w-full text-left text-sm">
205
+ <thead className="sticky top-0 border-b bg-slate-50 text-xs uppercase tracking-wide text-slate-500">
206
+ <tr>
207
+ <th className="w-8 px-2 py-3" />
208
+ <th className="px-3 py-3">Horodatage</th>
209
+ <th className="px-3 py-3">Source</th>
210
+ <th className="px-3 py-3">Méthode / tool</th>
211
+ <th className="px-3 py-3">Path</th>
212
+ <th className="px-3 py-3">Status</th>
213
+ <th className="px-3 py-3">Durée</th>
214
+ <th className="px-3 py-3">Erreur</th>
215
+ </tr>
216
+ </thead>
217
+ <tbody>
218
+ {logs.map((entry) => {
219
+ const open = Boolean(expanded[entry.id]);
220
+ const err = isErrorEntry(entry);
221
+ return (
222
+ <Fragment key={entry.id}>
223
+ <tr
224
+ className={cn(
225
+ "border-b last:border-0 hover:bg-slate-50/80",
226
+ err && "bg-red-50/40",
227
+ )}
228
+ >
229
+ <td className="px-2 py-2">
230
+ <button
231
+ type="button"
232
+ onClick={() => toggle(entry.id)}
233
+ className="rounded p-1 text-slate-400 hover:bg-slate-100 hover:text-slate-700"
234
+ aria-expanded={open}
235
+ aria-label={open ? "Masquer le détail" : "Afficher le détail"}
236
+ >
237
+ {open ? (
238
+ <ChevronDown className="h-3.5 w-3.5" />
239
+ ) : (
240
+ <ChevronRight className="h-3.5 w-3.5" />
241
+ )}
242
+ </button>
243
+ </td>
244
+ <td className="whitespace-nowrap px-3 py-2 font-mono text-xs text-slate-600">
245
+ {formatTs(entry.ts)}
246
+ </td>
247
+ <td className="px-3 py-2">
248
+ <Badge variant={entry.source === "mcp" ? "info" : "secondary"}>
249
+ {entry.source.toUpperCase()}
250
+ </Badge>
251
+ </td>
252
+ <td className="px-3 py-2 font-medium text-slate-900">
253
+ <button
254
+ type="button"
255
+ onClick={() => toggle(entry.id)}
256
+ className="text-left hover:underline"
257
+ >
258
+ {methodLabel(entry)}
259
+ </button>
260
+ {entry.source === "mcp" &&
261
+ entry.detail.tool &&
262
+ entry.detail.jsonrpcMethod ? (
263
+ <div className="mt-0.5 font-mono text-[10px] text-slate-400">
264
+ {entry.detail.jsonrpcMethod}
265
+ </div>
266
+ ) : null}
267
+ </td>
268
+ <td className="max-w-[16rem] truncate px-3 py-2 font-mono text-xs text-slate-600">
269
+ {entry.path}
270
+ </td>
271
+ <td className="px-3 py-2">
272
+ <Badge variant={statusVariant(entry)}>
273
+ {entry.status || "—"}
274
+ </Badge>
275
+ </td>
276
+ <td className="whitespace-nowrap px-3 py-2 tabular-nums text-slate-600">
277
+ {entry.durationMs.toLocaleString("fr-FR")} ms
278
+ </td>
279
+ <td
280
+ className={cn(
281
+ "max-w-[20rem] truncate px-3 py-2 text-xs",
282
+ err ? "text-red-700" : "text-slate-400",
283
+ )}
284
+ title={entry.detail.error || ""}
285
+ >
286
+ {entry.detail.error || (err ? "erreur" : "—")}
287
+ </td>
288
+ </tr>
289
+ {open ? (
290
+ <tr className="border-b bg-slate-50/60">
291
+ <td colSpan={8} className="px-4 py-3">
292
+ <pre className="max-h-80 overflow-auto rounded-lg bg-slate-950 p-3 text-[11px] leading-relaxed text-slate-100">
293
+ {JSON.stringify(
294
+ {
295
+ id: entry.id,
296
+ ts: entry.ts,
297
+ source: entry.source,
298
+ method: entry.method,
299
+ path: entry.path,
300
+ status: entry.status,
301
+ durationMs: entry.durationMs,
302
+ detail: entry.detail,
303
+ },
304
+ null,
305
+ 2,
306
+ )}
307
+ </pre>
308
+ </td>
309
+ </tr>
310
+ ) : null}
311
+ </Fragment>
312
+ );
313
+ })}
314
+ {!logs.length ? (
315
+ <tr>
316
+ <td colSpan={8} className="px-4 py-10 text-center text-slate-500">
317
+ Aucun log pour ces filtres. Lancez un appel MCP (ChatGPT) ou une
318
+ action API, puis actualisez.
319
+ </td>
320
+ </tr>
321
+ ) : null}
322
+ </tbody>
323
+ </table>
324
+ </div>
325
+ </div>
326
+ );
327
+ }
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Bridge session auth → tracker usage (POST /api/v1/analytics/events).
5
+ * À monter sous `SessionProvider` (typiquement BrandChrome factory).
6
+ * Suspense interne pour `useSearchParams` (App Router Next).
7
+ */
8
+
9
+ import { Suspense, type ReactNode } from "react";
10
+ import { useSession } from "@creezio/auth/ui";
11
+ import { UsageAnalyticsProvider } from "./usage-analytics-provider";
12
+
13
+ function SessionUsageAnalyticsInner({ children }: { children: ReactNode }) {
14
+ const { me } = useSession();
15
+ return <UsageAnalyticsProvider me={me}>{children}</UsageAnalyticsProvider>;
16
+ }
17
+
18
+ export function SessionUsageAnalyticsProvider({
19
+ children,
20
+ }: {
21
+ children: ReactNode;
22
+ }) {
23
+ return (
24
+ <Suspense fallback={null}>
25
+ <SessionUsageAnalyticsInner>{children}</SessionUsageAnalyticsInner>
26
+ </Suspense>
27
+ );
28
+ }