lojol469-cmd
commited on
Commit
·
b54b689
1
Parent(s):
30b5e11
Déploiement de l'agent Kibali sur Hugging Face
Browse files- .dockerignore +10 -6
- .gitattributes +2 -0
- CARTABON1/server.js +152 -114
- Dockerfile +28 -7
- docker-compose.yml +8 -14
- kibali-ui/package-lock.json +1451 -7
- kibali-ui/package.json +10 -1
- kibali-ui/src/App.jsx +505 -176
- main.py +353 -235
- requirements.txt +9 -8
- tools/todo.py +244 -32
.dockerignore
CHANGED
|
@@ -1,10 +1,14 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
*.pyc
|
| 3 |
*.pt
|
| 4 |
*.pkl
|
| 5 |
-
model_cache
|
| 6 |
-
data
|
| 7 |
-
tools
|
| 8 |
-
kibali_env
|
| 9 |
-
frontend
|
| 10 |
*.jar
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# --- À EXCLURE (Trop lourd ou inutile) ---
|
| 2 |
+
kibali_env/
|
| 3 |
+
model_cache/
|
| 4 |
+
frontend/
|
| 5 |
+
__pycache__/
|
| 6 |
*.pyc
|
| 7 |
*.pt
|
| 8 |
*.pkl
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
*.jar
|
| 10 |
+
.git/
|
| 11 |
+
.gitignore
|
| 12 |
+
|
| 13 |
+
# --- À NE PAS EXCLURE ---
|
| 14 |
+
# Assure-toi que "tools" n'est PAS écrit ici !
|
.gitattributes
CHANGED
|
@@ -1 +1,3 @@
|
|
| 1 |
model_cache/** filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 1 |
model_cache/** filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
data/** filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.pyc filter=lfs diff=lfs merge=lfs -text
|
CARTABON1/server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import express from "express";
|
| 2 |
import mongoose from "mongoose";
|
| 3 |
import dotenv from "dotenv";
|
|
@@ -5,9 +6,11 @@ import cors from "cors";
|
|
| 5 |
import http from "http";
|
| 6 |
import { Server } from "socket.io";
|
| 7 |
import bcrypt from "bcrypt";
|
|
|
|
| 8 |
import multer from "multer";
|
| 9 |
import path from "path";
|
| 10 |
import fs from "fs";
|
|
|
|
| 11 |
|
| 12 |
dotenv.config();
|
| 13 |
|
|
@@ -16,17 +19,17 @@ const server = http.createServer(app);
|
|
| 16 |
const io = new Server(server, {
|
| 17 |
cors: {
|
| 18 |
origin: "*",
|
| 19 |
-
methods: ["GET", "POST"]
|
| 20 |
}
|
| 21 |
});
|
| 22 |
|
| 23 |
-
app.use(express.json());
|
| 24 |
app.use(cors());
|
|
|
|
| 25 |
|
| 26 |
if (!fs.existsSync("uploads")) {
|
| 27 |
fs.mkdirSync("uploads");
|
| 28 |
}
|
| 29 |
-
app.use("/uploads", express.static(path.join(process.cwd(), "uploads")));
|
| 30 |
|
| 31 |
const storage = multer.diskStorage({
|
| 32 |
destination: (req, file, cb) => cb(null, "uploads/"),
|
|
@@ -39,28 +42,25 @@ const upload = multer({ storage });
|
|
| 39 |
// =========================
|
| 40 |
mongoose
|
| 41 |
.connect(process.env.MONGO_URI)
|
| 42 |
-
.then(() => console.log("MongoDB connecté ✅"))
|
| 43 |
-
.catch(err => console.error("Erreur MongoDB ❌:", err));
|
| 44 |
|
| 45 |
// =========================
|
| 46 |
-
//
|
| 47 |
// =========================
|
| 48 |
const userSchema = new mongoose.Schema({
|
| 49 |
name: { type: String, required: true },
|
| 50 |
-
email: { type: String, required: true, unique: true },
|
| 51 |
-
password: { type: String }, // null
|
| 52 |
-
googleId: { type: String }, // pour OAuth Google
|
| 53 |
-
photoUrl: { type: String }, // photo Google
|
| 54 |
latitude: { type: Number, default: 0 },
|
| 55 |
longitude: { type: Number, default: 0 },
|
|
|
|
|
|
|
| 56 |
createdAt: { type: Date, default: Date.now }
|
| 57 |
});
|
| 58 |
|
| 59 |
const User = mongoose.model("User", userSchema);
|
| 60 |
|
| 61 |
-
// =========================
|
| 62 |
-
// Marker Schema (inchangé)
|
| 63 |
-
// =========================
|
| 64 |
const markerSchema = new mongoose.Schema({
|
| 65 |
latitude: { type: Number, required: true },
|
| 66 |
longitude: { type: Number, required: true },
|
|
@@ -69,109 +69,147 @@ const markerSchema = new mongoose.Schema({
|
|
| 69 |
color: { type: String, default: "#ff0000" },
|
| 70 |
photos: { type: [String], default: [] },
|
| 71 |
videos: { type: [String], default: [] },
|
| 72 |
-
createdBy: { type: mongoose.Schema.Types.ObjectId, ref: "User"
|
| 73 |
createdAt: { type: Date, default: Date.now }
|
| 74 |
});
|
| 75 |
const Marker = mongoose.model("Marker", markerSchema);
|
| 76 |
|
| 77 |
// =========================
|
| 78 |
-
//
|
| 79 |
// =========================
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
//
|
| 83 |
-
|
|
|
|
|
|
|
| 84 |
try {
|
| 85 |
-
const
|
| 86 |
-
res.json(
|
| 87 |
-
} catch (err) {
|
| 88 |
-
res.status(500).json({ error: err.message });
|
| 89 |
-
}
|
| 90 |
-
});
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
const existingUser = await User.findOne({ email });
|
| 97 |
-
if (existingUser) return res.status(400).json({ message: "Email déjà utilisé" });
|
| 98 |
|
| 99 |
-
const
|
| 100 |
-
|
|
|
|
|
|
|
| 101 |
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
});
|
| 105 |
} catch (err) {
|
| 106 |
-
|
|
|
|
| 107 |
}
|
| 108 |
});
|
| 109 |
|
| 110 |
-
|
| 111 |
-
app.post("/login", async (req, res) => {
|
| 112 |
try {
|
| 113 |
-
const { email,
|
| 114 |
const user = await User.findOne({ email });
|
| 115 |
-
if (!user || !user.password) return res.status(400).json({ message: "Email ou mot de passe incorrect" });
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
res.json({
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
});
|
| 123 |
} catch (err) {
|
| 124 |
-
|
|
|
|
| 125 |
}
|
| 126 |
});
|
| 127 |
|
| 128 |
-
//
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
} else {
|
| 143 |
-
// Nouveau compte Google
|
| 144 |
-
user = await User.create({
|
| 145 |
-
googleId,
|
| 146 |
-
name,
|
| 147 |
-
email,
|
| 148 |
-
photoUrl,
|
| 149 |
-
password: null // pas de mot de passe local
|
| 150 |
-
});
|
| 151 |
-
}
|
| 152 |
-
} else {
|
| 153 |
-
// Mise à jour photo/nom si changé
|
| 154 |
-
user.name = name;
|
| 155 |
-
user.photoUrl = photoUrl;
|
| 156 |
-
await user.save();
|
| 157 |
-
}
|
| 158 |
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
} catch (err) {
|
| 163 |
res.status(500).json({ message: err.message });
|
| 164 |
}
|
| 165 |
});
|
| 166 |
|
| 167 |
-
//
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
try {
|
| 170 |
-
const { latitude, longitude, title, comment, color
|
| 171 |
const photos = req.files.photos ? req.files.photos.map(f => `/uploads/${f.filename}`) : [];
|
| 172 |
const videos = req.files.videos ? req.files.videos.map(f => `/uploads/${f.filename}`) : [];
|
| 173 |
|
| 174 |
-
const
|
| 175 |
latitude: parseFloat(latitude),
|
| 176 |
longitude: parseFloat(longitude),
|
| 177 |
title,
|
|
@@ -179,34 +217,38 @@ app.post("/markers", upload.fields([{ name: "photos", maxCount: 10 }, { name: "v
|
|
| 179 |
color,
|
| 180 |
photos,
|
| 181 |
videos,
|
| 182 |
-
createdBy: userId
|
| 183 |
});
|
| 184 |
|
| 185 |
-
|
| 186 |
-
|
|
|
|
| 187 |
} catch (err) {
|
| 188 |
-
console.error(
|
| 189 |
res.status(500).json({ message: err.message });
|
| 190 |
}
|
| 191 |
});
|
| 192 |
|
| 193 |
-
app.patch("/markers/:id", upload.fields([
|
|
|
|
|
|
|
|
|
|
| 194 |
try {
|
| 195 |
-
const { title, comment, color } = req.body;
|
| 196 |
const marker = await Marker.findById(req.params.id);
|
| 197 |
if (!marker) return res.status(404).json({ message: "Marqueur non trouvé" });
|
| 198 |
|
| 199 |
-
|
| 200 |
-
if (
|
| 201 |
-
if (
|
| 202 |
-
if (
|
| 203 |
-
if (req.files.
|
|
|
|
| 204 |
|
| 205 |
await marker.save();
|
| 206 |
-
|
| 207 |
-
|
|
|
|
| 208 |
} catch (err) {
|
| 209 |
-
console.error("Erreur editMarker:", err);
|
| 210 |
res.status(500).json({ message: err.message });
|
| 211 |
}
|
| 212 |
});
|
|
@@ -215,43 +257,39 @@ app.patch("/markers/:id", upload.fields([{ name: "photos", maxCount: 10 }, { nam
|
|
| 215 |
// Socket.IO
|
| 216 |
// =========================
|
| 217 |
io.on("connection", (socket) => {
|
| 218 |
-
console.log("
|
| 219 |
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
} catch (err) {
|
| 225 |
-
console.error("Erreur envoi marqueurs:", err);
|
| 226 |
-
}
|
| 227 |
-
};
|
| 228 |
-
sendAllMarkers();
|
| 229 |
|
|
|
|
| 230 |
socket.on("updatePosition", async ({ userId, latitude, longitude }) => {
|
| 231 |
-
if (typeof latitude !== "number" || typeof longitude !== "number") return;
|
| 232 |
-
|
| 233 |
try {
|
| 234 |
const user = await User.findByIdAndUpdate(userId, { latitude, longitude }, { new: true });
|
| 235 |
if (user) {
|
| 236 |
io.emit("positionsUpdate", {
|
| 237 |
userId: user._id,
|
| 238 |
name: user.name,
|
| 239 |
-
latitude
|
| 240 |
-
longitude
|
| 241 |
});
|
| 242 |
}
|
| 243 |
} catch (err) {
|
| 244 |
-
console.error("Erreur
|
| 245 |
}
|
| 246 |
});
|
| 247 |
|
| 248 |
socket.on("disconnect", () => {
|
| 249 |
-
console.log("
|
| 250 |
});
|
| 251 |
});
|
| 252 |
|
| 253 |
// =========================
|
| 254 |
-
//
|
| 255 |
// =========================
|
| 256 |
-
const PORT = process.env.PORT ||
|
| 257 |
-
server.listen(PORT, () =>
|
|
|
|
|
|
|
|
|
| 1 |
+
// server.js (ou index.js)
|
| 2 |
import express from "express";
|
| 3 |
import mongoose from "mongoose";
|
| 4 |
import dotenv from "dotenv";
|
|
|
|
| 6 |
import http from "http";
|
| 7 |
import { Server } from "socket.io";
|
| 8 |
import bcrypt from "bcrypt";
|
| 9 |
+
import jwt from "jsonwebtoken";
|
| 10 |
import multer from "multer";
|
| 11 |
import path from "path";
|
| 12 |
import fs from "fs";
|
| 13 |
+
import crypto from "crypto";
|
| 14 |
|
| 15 |
dotenv.config();
|
| 16 |
|
|
|
|
| 19 |
const io = new Server(server, {
|
| 20 |
cors: {
|
| 21 |
origin: "*",
|
| 22 |
+
methods: ["GET", "POST", "PATCH"]
|
| 23 |
}
|
| 24 |
});
|
| 25 |
|
| 26 |
+
app.use(express.json({ limit: "50mb" }));
|
| 27 |
app.use(cors());
|
| 28 |
+
app.use("/uploads", express.static(path.join(process.cwd(), "uploads")));
|
| 29 |
|
| 30 |
if (!fs.existsSync("uploads")) {
|
| 31 |
fs.mkdirSync("uploads");
|
| 32 |
}
|
|
|
|
| 33 |
|
| 34 |
const storage = multer.diskStorage({
|
| 35 |
destination: (req, file, cb) => cb(null, "uploads/"),
|
|
|
|
| 42 |
// =========================
|
| 43 |
mongoose
|
| 44 |
.connect(process.env.MONGO_URI)
|
| 45 |
+
.then(() => console.log("MongoDB connecté avec succès ✅"))
|
| 46 |
+
.catch(err => console.error("Erreur connexion MongoDB ❌:", err));
|
| 47 |
|
| 48 |
// =========================
|
| 49 |
+
// Modèles
|
| 50 |
// =========================
|
| 51 |
const userSchema = new mongoose.Schema({
|
| 52 |
name: { type: String, required: true },
|
| 53 |
+
email: { type: String, required: true, unique: true, lowercase: true },
|
| 54 |
+
password: { type: String }, // hashé ou null si OTP uniquement
|
|
|
|
|
|
|
| 55 |
latitude: { type: Number, default: 0 },
|
| 56 |
longitude: { type: Number, default: 0 },
|
| 57 |
+
otp: { type: String }, // OTP temporaire
|
| 58 |
+
otpExpires: { type: Date }, // expiration OTP
|
| 59 |
createdAt: { type: Date, default: Date.now }
|
| 60 |
});
|
| 61 |
|
| 62 |
const User = mongoose.model("User", userSchema);
|
| 63 |
|
|
|
|
|
|
|
|
|
|
| 64 |
const markerSchema = new mongoose.Schema({
|
| 65 |
latitude: { type: Number, required: true },
|
| 66 |
longitude: { type: Number, required: true },
|
|
|
|
| 69 |
color: { type: String, default: "#ff0000" },
|
| 70 |
photos: { type: [String], default: [] },
|
| 71 |
videos: { type: [String], default: [] },
|
| 72 |
+
createdBy: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
|
| 73 |
createdAt: { type: Date, default: Date.now }
|
| 74 |
});
|
| 75 |
const Marker = mongoose.model("Marker", markerSchema);
|
| 76 |
|
| 77 |
// =========================
|
| 78 |
+
// Helpers
|
| 79 |
// =========================
|
| 80 |
+
const generateOTP = () => Math.floor(100000 + Math.random() * 900000).toString();
|
| 81 |
+
|
| 82 |
+
const sendOTPByEmail = async (email, otp) => {
|
| 83 |
+
// À intégrer avec Nodemailer, SMTP, ou service comme Brevo/SendGrid
|
| 84 |
+
console.log(`📧 OTP pour ${email} : ${otp}`); // Simulation en console
|
| 85 |
+
// Exemple avec Nodemailer (à décommenter et configurer)
|
| 86 |
+
/*
|
| 87 |
+
const transporter = nodemailer.createTransport({
|
| 88 |
+
host: "smtp.gmail.com",
|
| 89 |
+
port: 587,
|
| 90 |
+
auth: { user: "tonemail@gmail.com", pass: "app-password" }
|
| 91 |
+
});
|
| 92 |
+
await transporter.sendMail({
|
| 93 |
+
from: "Kibali <no-reply@kibali.ga>",
|
| 94 |
+
to: email,
|
| 95 |
+
subject: "Votre code OTP Kibali",
|
| 96 |
+
text: `Votre code de connexion est : ${otp}\nValable 10 minutes.`
|
| 97 |
+
});
|
| 98 |
+
*/
|
| 99 |
+
};
|
| 100 |
|
| 101 |
+
// =========================
|
| 102 |
+
// Routes Auth avec OTP
|
| 103 |
+
// =========================
|
| 104 |
+
app.post("/auth/request-otp", async (req, res) => {
|
| 105 |
try {
|
| 106 |
+
const { email, name } = req.body;
|
| 107 |
+
if (!email) return res.status(400).json({ message: "Email requis" });
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
+
let user = await User.findOne({ email });
|
| 110 |
+
if (!user) {
|
| 111 |
+
user = await User.create({ email, name: name || email.split("@")[0], password: null });
|
| 112 |
+
}
|
|
|
|
|
|
|
| 113 |
|
| 114 |
+
const otp = generateOTP();
|
| 115 |
+
user.otp = otp;
|
| 116 |
+
user.otpExpires = Date.now() + 10 * 60 * 1000; // 10 minutes
|
| 117 |
+
await user.save();
|
| 118 |
|
| 119 |
+
await sendOTPByEmail(email, otp);
|
| 120 |
+
|
| 121 |
+
res.json({ message: "OTP envoyé à votre email" });
|
| 122 |
} catch (err) {
|
| 123 |
+
console.error(err);
|
| 124 |
+
res.status(500).json({ message: "Erreur serveur" });
|
| 125 |
}
|
| 126 |
});
|
| 127 |
|
| 128 |
+
app.post("/auth/verify-otp", async (req, res) => {
|
|
|
|
| 129 |
try {
|
| 130 |
+
const { email, otp } = req.body;
|
| 131 |
const user = await User.findOne({ email });
|
|
|
|
| 132 |
|
| 133 |
+
if (!user || !user.otp || user.otp !== otp || user.otpExpires < Date.now()) {
|
| 134 |
+
return res.status(400).json({ message: "OTP invalide ou expiré" });
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
// OTP valide → générer JWT
|
| 138 |
+
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: "7d" });
|
| 139 |
+
|
| 140 |
+
// Nettoyer OTP
|
| 141 |
+
user.otp = undefined;
|
| 142 |
+
user.otpExpires = undefined;
|
| 143 |
+
await user.save();
|
| 144 |
|
| 145 |
res.json({
|
| 146 |
+
token,
|
| 147 |
+
user: {
|
| 148 |
+
_id: user._id,
|
| 149 |
+
name: user.name,
|
| 150 |
+
email: user.email,
|
| 151 |
+
latitude: user.latitude,
|
| 152 |
+
longitude: user.longitude
|
| 153 |
+
}
|
| 154 |
});
|
| 155 |
} catch (err) {
|
| 156 |
+
console.error(err);
|
| 157 |
+
res.status(500).json({ message: "Erreur serveur" });
|
| 158 |
}
|
| 159 |
});
|
| 160 |
|
| 161 |
+
// =========================
|
| 162 |
+
// Routes protégées (vérification JWT)
|
| 163 |
+
// =========================
|
| 164 |
+
const authenticateToken = (req, res, next) => {
|
| 165 |
+
const authHeader = req.headers["authorization"];
|
| 166 |
+
const token = authHeader && authHeader.split(" ")[1];
|
| 167 |
+
if (!token) return res.status(401).json({ message: "Accès refusé" });
|
| 168 |
+
|
| 169 |
+
jwt.verify(token, process.env.JWT_SECRET, (err, payload) => {
|
| 170 |
+
if (err) return res.status(403).json({ message: "Token invalide" });
|
| 171 |
+
req.userId = payload.userId;
|
| 172 |
+
next();
|
| 173 |
+
});
|
| 174 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
+
// Mise à jour position utilisateur
|
| 177 |
+
app.post("/update-position", authenticateToken, async (req, res) => {
|
| 178 |
+
try {
|
| 179 |
+
const { latitude, longitude } = req.body;
|
| 180 |
+
const user = await User.findByIdAndUpdate(
|
| 181 |
+
req.userId,
|
| 182 |
+
{ latitude, longitude },
|
| 183 |
+
{ new: true }
|
| 184 |
+
);
|
| 185 |
+
res.json({ latitude: user.latitude, longitude: user.longitude });
|
| 186 |
} catch (err) {
|
| 187 |
res.status(500).json({ message: err.message });
|
| 188 |
}
|
| 189 |
});
|
| 190 |
|
| 191 |
+
// =========================
|
| 192 |
+
// Routes Markers
|
| 193 |
+
// =========================
|
| 194 |
+
app.get("/markers", async (req, res) => {
|
| 195 |
+
try {
|
| 196 |
+
const markers = await Marker.find().populate("createdBy", "name email");
|
| 197 |
+
res.json(markers);
|
| 198 |
+
} catch (err) {
|
| 199 |
+
res.status(500).json({ error: err.message });
|
| 200 |
+
}
|
| 201 |
+
});
|
| 202 |
+
|
| 203 |
+
app.post("/markers", authenticateToken, upload.fields([
|
| 204 |
+
{ name: "photos", maxCount: 10 },
|
| 205 |
+
{ name: "videos", maxCount: 10 }
|
| 206 |
+
]), async (req, res) => {
|
| 207 |
try {
|
| 208 |
+
const { latitude, longitude, title, comment, color } = req.body;
|
| 209 |
const photos = req.files.photos ? req.files.photos.map(f => `/uploads/${f.filename}`) : [];
|
| 210 |
const videos = req.files.videos ? req.files.videos.map(f => `/uploads/${f.filename}`) : [];
|
| 211 |
|
| 212 |
+
const marker = await Marker.create({
|
| 213 |
latitude: parseFloat(latitude),
|
| 214 |
longitude: parseFloat(longitude),
|
| 215 |
title,
|
|
|
|
| 217 |
color,
|
| 218 |
photos,
|
| 219 |
videos,
|
| 220 |
+
createdBy: req.userId
|
| 221 |
});
|
| 222 |
|
| 223 |
+
const populated = await marker.populate("createdBy", "name email");
|
| 224 |
+
io.emit("newMarker", populated);
|
| 225 |
+
res.status(201).json(populated);
|
| 226 |
} catch (err) {
|
| 227 |
+
console.error(err);
|
| 228 |
res.status(500).json({ message: err.message });
|
| 229 |
}
|
| 230 |
});
|
| 231 |
|
| 232 |
+
app.patch("/markers/:id", authenticateToken, upload.fields([
|
| 233 |
+
{ name: "photos", maxCount: 10 },
|
| 234 |
+
{ name: "videos", maxCount: 10 }
|
| 235 |
+
]), async (req, res) => {
|
| 236 |
try {
|
|
|
|
| 237 |
const marker = await Marker.findById(req.params.id);
|
| 238 |
if (!marker) return res.status(404).json({ message: "Marqueur non trouvé" });
|
| 239 |
|
| 240 |
+
const updates = req.body;
|
| 241 |
+
if (updates.title !== undefined) marker.title = updates.title;
|
| 242 |
+
if (updates.comment !== undefined) marker.comment = updates.comment;
|
| 243 |
+
if (updates.color !== undefined) marker.color = updates.color;
|
| 244 |
+
if (req.files.photos) marker.photos.push(...req.files.photos.map(f => `/uploads/${f.filename}`));
|
| 245 |
+
if (req.files.videos) marker.videos.push(...req.files.videos.map(f => `/uploads/${f.filename}`));
|
| 246 |
|
| 247 |
await marker.save();
|
| 248 |
+
const populated = await marker.populate("createdBy", "name email");
|
| 249 |
+
io.emit("updatedMarker", populated);
|
| 250 |
+
res.json(populated);
|
| 251 |
} catch (err) {
|
|
|
|
| 252 |
res.status(500).json({ message: err.message });
|
| 253 |
}
|
| 254 |
});
|
|
|
|
| 257 |
// Socket.IO
|
| 258 |
// =========================
|
| 259 |
io.on("connection", (socket) => {
|
| 260 |
+
console.log("Client connecté:", socket.id);
|
| 261 |
|
| 262 |
+
// Envoyer tous les marqueurs au nouveau client
|
| 263 |
+
Marker.find().populate("createdBy", "name email").then(markers => {
|
| 264 |
+
socket.emit("allMarkers", markers);
|
| 265 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
+
// Mise à jour position en temps réel
|
| 268 |
socket.on("updatePosition", async ({ userId, latitude, longitude }) => {
|
|
|
|
|
|
|
| 269 |
try {
|
| 270 |
const user = await User.findByIdAndUpdate(userId, { latitude, longitude }, { new: true });
|
| 271 |
if (user) {
|
| 272 |
io.emit("positionsUpdate", {
|
| 273 |
userId: user._id,
|
| 274 |
name: user.name,
|
| 275 |
+
latitude,
|
| 276 |
+
longitude
|
| 277 |
});
|
| 278 |
}
|
| 279 |
} catch (err) {
|
| 280 |
+
console.error("Erreur position:", err);
|
| 281 |
}
|
| 282 |
});
|
| 283 |
|
| 284 |
socket.on("disconnect", () => {
|
| 285 |
+
console.log("Client déconnecté:", socket.id);
|
| 286 |
});
|
| 287 |
});
|
| 288 |
|
| 289 |
// =========================
|
| 290 |
+
// Démarrage
|
| 291 |
// =========================
|
| 292 |
+
const PORT = process.env.PORT || 8000;
|
| 293 |
+
server.listen(PORT, () => {
|
| 294 |
+
console.log(`🚀 Serveur Kibali démarré sur http://localhost:${PORT}`);
|
| 295 |
+
});
|
Dockerfile
CHANGED
|
@@ -1,15 +1,36 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
|
|
|
|
|
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
COPY requirements.txt .
|
| 7 |
-
RUN
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
|
|
|
| 10 |
|
| 11 |
-
|
| 12 |
EXPOSE 8000
|
| 13 |
|
| 14 |
-
#
|
| 15 |
-
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
|
|
| 1 |
+
# --- STAGE 1 : Build du Frontend (Vite) ---
|
| 2 |
+
FROM node:18-alpine AS build-frontend
|
| 3 |
+
WORKDIR /app/frontend
|
| 4 |
+
COPY kibali-ui/package*.json ./
|
| 5 |
+
RUN npm install
|
| 6 |
+
COPY kibali-ui/ ./
|
| 7 |
+
RUN npm run build
|
| 8 |
|
| 9 |
+
# --- STAGE 2 : Backend + Serveur Statique ---
|
| 10 |
+
FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04
|
| 11 |
WORKDIR /app
|
| 12 |
|
| 13 |
+
# Installation de Python
|
| 14 |
+
RUN apt-get update && apt-get install -y \
|
| 15 |
+
python3-pip \
|
| 16 |
+
python3-dev \
|
| 17 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 18 |
+
|
| 19 |
+
# Installation de PyTorch
|
| 20 |
+
RUN pip3 install --no-cache-dir torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
|
| 21 |
+
|
| 22 |
+
# Installation des dépendances
|
| 23 |
COPY requirements.txt .
|
| 24 |
+
RUN pip3 install --no-cache-dir -r requirements.txt
|
| 25 |
+
|
| 26 |
+
# On récupère le dossier 'dist' de Vite et on le renomme 'static'
|
| 27 |
+
COPY --from=build-frontend /app/frontend/dist ./static
|
| 28 |
|
| 29 |
+
# Copie du code Python
|
| 30 |
+
COPY . .
|
| 31 |
|
| 32 |
+
ENV PYTHONUNBUFFERED=1
|
| 33 |
EXPOSE 8000
|
| 34 |
|
| 35 |
+
# Commande corrigée pour Ubuntu (python3)
|
| 36 |
+
CMD ["python3", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
docker-compose.yml
CHANGED
|
@@ -5,6 +5,7 @@ services:
|
|
| 5 |
build: .
|
| 6 |
image: kibali-engine:latest
|
| 7 |
container_name: kibali-engine
|
|
|
|
| 8 |
deploy:
|
| 9 |
resources:
|
| 10 |
reservations:
|
|
@@ -13,19 +14,12 @@ services:
|
|
| 13 |
count: 1
|
| 14 |
capabilities: [gpu]
|
| 15 |
volumes:
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
ports:
|
| 18 |
- "8000:8000"
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
kibali-ui:
|
| 22 |
-
build: .
|
| 23 |
-
image: kibali-ui:latest
|
| 24 |
-
container_name: kibali-ui
|
| 25 |
-
ports:
|
| 26 |
-
- "8501:8501"
|
| 27 |
-
depends_on:
|
| 28 |
-
- kibali-engine
|
| 29 |
-
volumes:
|
| 30 |
-
- /home/belikan/geoscan/agent_kibali:/app
|
| 31 |
-
command: streamlit run app.py --server.port 8501 --server.address 0.0.0.0
|
|
|
|
| 5 |
build: .
|
| 6 |
image: kibali-engine:latest
|
| 7 |
container_name: kibali-engine
|
| 8 |
+
# Configuration GPU vitale pour l'IA
|
| 9 |
deploy:
|
| 10 |
resources:
|
| 11 |
reservations:
|
|
|
|
| 14 |
count: 1
|
| 15 |
capabilities: [gpu]
|
| 16 |
volumes:
|
| 17 |
+
# On ne monte que les dossiers de données persistantes
|
| 18 |
+
- ./model_cache:/app/model_cache
|
| 19 |
+
- ./data:/app/data
|
| 20 |
+
# On évite de monter tout le dossier /app pour ne pas écraser
|
| 21 |
+
# le build de Vite fait pendant le Dockerfile
|
| 22 |
ports:
|
| 23 |
- "8000:8000"
|
| 24 |
+
# Lancement de l'API avec uvicorn
|
| 25 |
+
command: python3 -m uvicorn main:app --host 0.0.0.0 --port 8000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kibali-ui/package-lock.json
CHANGED
|
@@ -9,13 +9,22 @@
|
|
| 9 |
"version": "0.0.0",
|
| 10 |
"dependencies": {
|
| 11 |
"axios": "^1.13.2",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
"firebase": "^12.7.0",
|
|
|
|
| 13 |
"leaflet": "^1.9.4",
|
| 14 |
"lucide-react": "^0.562.0",
|
|
|
|
|
|
|
| 15 |
"react": "^19.2.0",
|
| 16 |
"react-dom": "^19.2.0",
|
| 17 |
"react-leaflet": "^5.0.0",
|
| 18 |
-
"react-markdown": "^10.1.0"
|
|
|
|
|
|
|
| 19 |
},
|
| 20 |
"devDependencies": {
|
| 21 |
"@eslint/js": "^9.39.1",
|
|
@@ -1654,6 +1663,15 @@
|
|
| 1654 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 1655 |
}
|
| 1656 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1657 |
"node_modules/@protobufjs/aspromise": {
|
| 1658 |
"version": "1.1.2",
|
| 1659 |
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
|
@@ -2044,6 +2062,12 @@
|
|
| 2044 |
"win32"
|
| 2045 |
]
|
| 2046 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2047 |
"node_modules/@types/babel__core": {
|
| 2048 |
"version": "7.20.5",
|
| 2049 |
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
|
@@ -2089,6 +2113,15 @@
|
|
| 2089 |
"@babel/types": "^7.28.2"
|
| 2090 |
}
|
| 2091 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2092 |
"node_modules/@types/debug": {
|
| 2093 |
"version": "4.1.12",
|
| 2094 |
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
|
@@ -2178,6 +2211,21 @@
|
|
| 2178 |
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
| 2179 |
"license": "MIT"
|
| 2180 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2181 |
"node_modules/@ungap/structured-clone": {
|
| 2182 |
"version": "1.3.0",
|
| 2183 |
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
|
@@ -2205,6 +2253,44 @@
|
|
| 2205 |
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
| 2206 |
}
|
| 2207 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2208 |
"node_modules/acorn": {
|
| 2209 |
"version": "8.15.0",
|
| 2210 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
|
@@ -2269,6 +2355,12 @@
|
|
| 2269 |
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
| 2270 |
}
|
| 2271 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2272 |
"node_modules/argparse": {
|
| 2273 |
"version": "2.0.1",
|
| 2274 |
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
|
@@ -2347,6 +2439,15 @@
|
|
| 2347 |
"dev": true,
|
| 2348 |
"license": "MIT"
|
| 2349 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2350 |
"node_modules/baseline-browser-mapping": {
|
| 2351 |
"version": "2.9.11",
|
| 2352 |
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
|
|
@@ -2357,6 +2458,44 @@
|
|
| 2357 |
"baseline-browser-mapping": "dist/cli.js"
|
| 2358 |
}
|
| 2359 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2360 |
"node_modules/brace-expansion": {
|
| 2361 |
"version": "1.1.12",
|
| 2362 |
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
|
@@ -2402,6 +2541,47 @@
|
|
| 2402 |
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
| 2403 |
}
|
| 2404 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2405 |
"node_modules/call-bind-apply-helpers": {
|
| 2406 |
"version": "1.0.2",
|
| 2407 |
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
|
@@ -2415,6 +2595,22 @@
|
|
| 2415 |
"node": ">= 0.4"
|
| 2416 |
}
|
| 2417 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2418 |
"node_modules/callsites": {
|
| 2419 |
"version": "3.1.0",
|
| 2420 |
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
|
@@ -2574,6 +2770,43 @@
|
|
| 2574 |
"dev": true,
|
| 2575 |
"license": "MIT"
|
| 2576 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2577 |
"node_modules/convert-source-map": {
|
| 2578 |
"version": "2.0.0",
|
| 2579 |
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
|
@@ -2581,6 +2814,37 @@
|
|
| 2581 |
"dev": true,
|
| 2582 |
"license": "MIT"
|
| 2583 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2584 |
"node_modules/cross-spawn": {
|
| 2585 |
"version": "7.0.6",
|
| 2586 |
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
|
@@ -2648,6 +2912,15 @@
|
|
| 2648 |
"node": ">=0.4.0"
|
| 2649 |
}
|
| 2650 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2651 |
"node_modules/dequal": {
|
| 2652 |
"version": "2.0.3",
|
| 2653 |
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
|
@@ -2670,6 +2943,18 @@
|
|
| 2670 |
"url": "https://github.com/sponsors/wooorm"
|
| 2671 |
}
|
| 2672 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2673 |
"node_modules/dunder-proto": {
|
| 2674 |
"version": "1.0.1",
|
| 2675 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
@@ -2684,6 +2969,21 @@
|
|
| 2684 |
"node": ">= 0.4"
|
| 2685 |
}
|
| 2686 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2687 |
"node_modules/electron-to-chromium": {
|
| 2688 |
"version": "1.5.267",
|
| 2689 |
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
|
|
@@ -2697,6 +2997,79 @@
|
|
| 2697 |
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
| 2698 |
"license": "MIT"
|
| 2699 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2700 |
"node_modules/es-define-property": {
|
| 2701 |
"version": "1.0.1",
|
| 2702 |
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
|
@@ -2793,6 +3166,12 @@
|
|
| 2793 |
"node": ">=6"
|
| 2794 |
}
|
| 2795 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2796 |
"node_modules/escape-string-regexp": {
|
| 2797 |
"version": "4.0.0",
|
| 2798 |
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
|
@@ -3000,6 +3379,83 @@
|
|
| 3000 |
"node": ">=0.10.0"
|
| 3001 |
}
|
| 3002 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3003 |
"node_modules/extend": {
|
| 3004 |
"version": "3.0.2",
|
| 3005 |
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
|
@@ -3070,6 +3526,27 @@
|
|
| 3070 |
"node": ">=16.0.0"
|
| 3071 |
}
|
| 3072 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3073 |
"node_modules/find-up": {
|
| 3074 |
"version": "5.0.0",
|
| 3075 |
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
|
@@ -3180,6 +3657,15 @@
|
|
| 3180 |
"node": ">= 6"
|
| 3181 |
}
|
| 3182 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3183 |
"node_modules/fraction.js": {
|
| 3184 |
"version": "5.3.4",
|
| 3185 |
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
|
|
@@ -3194,6 +3680,15 @@
|
|
| 3194 |
"url": "https://github.com/sponsors/rawify"
|
| 3195 |
}
|
| 3196 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3197 |
"node_modules/fsevents": {
|
| 3198 |
"version": "2.3.3",
|
| 3199 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
@@ -3428,17 +3923,53 @@
|
|
| 3428 |
"url": "https://opencollective.com/unified"
|
| 3429 |
}
|
| 3430 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3431 |
"node_modules/http-parser-js": {
|
| 3432 |
"version": "0.5.10",
|
| 3433 |
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
|
| 3434 |
"integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
|
| 3435 |
"license": "MIT"
|
| 3436 |
},
|
| 3437 |
-
"node_modules/
|
| 3438 |
-
"version": "7.1
|
| 3439 |
-
"resolved": "https://registry.npmjs.org/
|
| 3440 |
-
"integrity": "sha512-
|
| 3441 |
-
"license": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3442 |
},
|
| 3443 |
"node_modules/ignore": {
|
| 3444 |
"version": "5.3.2",
|
|
@@ -3477,12 +4008,27 @@
|
|
| 3477 |
"node": ">=0.8.19"
|
| 3478 |
}
|
| 3479 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3480 |
"node_modules/inline-style-parser": {
|
| 3481 |
"version": "0.2.7",
|
| 3482 |
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
|
| 3483 |
"integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
|
| 3484 |
"license": "MIT"
|
| 3485 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3486 |
"node_modules/is-alphabetical": {
|
| 3487 |
"version": "2.0.1",
|
| 3488 |
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
|
|
@@ -3571,6 +4117,12 @@
|
|
| 3571 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 3572 |
}
|
| 3573 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3574 |
"node_modules/isexe": {
|
| 3575 |
"version": "2.0.0",
|
| 3576 |
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
|
@@ -3645,6 +4197,70 @@
|
|
| 3645 |
"node": ">=6"
|
| 3646 |
}
|
| 3647 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3648 |
"node_modules/keyv": {
|
| 3649 |
"version": "4.5.4",
|
| 3650 |
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
|
@@ -3697,6 +4313,42 @@
|
|
| 3697 |
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
| 3698 |
"license": "MIT"
|
| 3699 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3700 |
"node_modules/lodash.merge": {
|
| 3701 |
"version": "4.6.2",
|
| 3702 |
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
|
@@ -3704,6 +4356,12 @@
|
|
| 3704 |
"dev": true,
|
| 3705 |
"license": "MIT"
|
| 3706 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3707 |
"node_modules/long": {
|
| 3708 |
"version": "5.3.2",
|
| 3709 |
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
|
@@ -3901,6 +4559,33 @@
|
|
| 3901 |
"url": "https://opencollective.com/unified"
|
| 3902 |
}
|
| 3903 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3904 |
"node_modules/micromark": {
|
| 3905 |
"version": "4.0.2",
|
| 3906 |
"resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
|
|
@@ -4377,12 +5062,171 @@
|
|
| 4377 |
"node": "*"
|
| 4378 |
}
|
| 4379 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4380 |
"node_modules/ms": {
|
| 4381 |
"version": "2.1.3",
|
| 4382 |
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 4383 |
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 4384 |
"license": "MIT"
|
| 4385 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4386 |
"node_modules/nanoid": {
|
| 4387 |
"version": "3.3.11",
|
| 4388 |
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
|
@@ -4409,6 +5253,35 @@
|
|
| 4409 |
"dev": true,
|
| 4410 |
"license": "MIT"
|
| 4411 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4412 |
"node_modules/node-releases": {
|
| 4413 |
"version": "2.0.27",
|
| 4414 |
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
|
@@ -4416,6 +5289,48 @@
|
|
| 4416 |
"dev": true,
|
| 4417 |
"license": "MIT"
|
| 4418 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4419 |
"node_modules/optionator": {
|
| 4420 |
"version": "0.9.4",
|
| 4421 |
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
|
@@ -4504,6 +5419,15 @@
|
|
| 4504 |
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
| 4505 |
"license": "MIT"
|
| 4506 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4507 |
"node_modules/path-exists": {
|
| 4508 |
"version": "4.0.0",
|
| 4509 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
@@ -4524,6 +5448,16 @@
|
|
| 4524 |
"node": ">=8"
|
| 4525 |
}
|
| 4526 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4527 |
"node_modules/picocolors": {
|
| 4528 |
"version": "1.1.1",
|
| 4529 |
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
@@ -4624,6 +5558,19 @@
|
|
| 4624 |
"node": ">=12.0.0"
|
| 4625 |
}
|
| 4626 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4627 |
"node_modules/proxy-from-env": {
|
| 4628 |
"version": "1.1.0",
|
| 4629 |
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
|
@@ -4634,12 +5581,50 @@
|
|
| 4634 |
"version": "2.3.1",
|
| 4635 |
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
| 4636 |
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
| 4637 |
-
"dev": true,
|
| 4638 |
"license": "MIT",
|
| 4639 |
"engines": {
|
| 4640 |
"node": ">=6"
|
| 4641 |
}
|
| 4642 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4643 |
"node_modules/react": {
|
| 4644 |
"version": "19.2.3",
|
| 4645 |
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
|
@@ -4712,6 +5697,20 @@
|
|
| 4712 |
"node": ">=0.10.0"
|
| 4713 |
}
|
| 4714 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4715 |
"node_modules/remark-parse": {
|
| 4716 |
"version": "11.0.0",
|
| 4717 |
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
|
|
@@ -4806,6 +5805,22 @@
|
|
| 4806 |
"fsevents": "~2.3.2"
|
| 4807 |
}
|
| 4808 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4809 |
"node_modules/safe-buffer": {
|
| 4810 |
"version": "5.2.1",
|
| 4811 |
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
|
@@ -4826,6 +5841,12 @@
|
|
| 4826 |
],
|
| 4827 |
"license": "MIT"
|
| 4828 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4829 |
"node_modules/scheduler": {
|
| 4830 |
"version": "0.27.0",
|
| 4831 |
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
|
@@ -4842,6 +5863,82 @@
|
|
| 4842 |
"semver": "bin/semver.js"
|
| 4843 |
}
|
| 4844 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4845 |
"node_modules/shebang-command": {
|
| 4846 |
"version": "2.0.0",
|
| 4847 |
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
|
@@ -4865,6 +5962,162 @@
|
|
| 4865 |
"node": ">=8"
|
| 4866 |
}
|
| 4867 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4868 |
"node_modules/source-map-js": {
|
| 4869 |
"version": "1.2.1",
|
| 4870 |
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
|
@@ -4885,6 +6138,41 @@
|
|
| 4885 |
"url": "https://github.com/sponsors/wooorm"
|
| 4886 |
}
|
| 4887 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4888 |
"node_modules/string-width": {
|
| 4889 |
"version": "4.2.3",
|
| 4890 |
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
@@ -4993,6 +6281,27 @@
|
|
| 4993 |
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 4994 |
}
|
| 4995 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4996 |
"node_modules/trim-lines": {
|
| 4997 |
"version": "3.0.1",
|
| 4998 |
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
|
@@ -5032,6 +6341,51 @@
|
|
| 5032 |
"node": ">= 0.8.0"
|
| 5033 |
}
|
| 5034 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5035 |
"node_modules/undici-types": {
|
| 5036 |
"version": "7.16.0",
|
| 5037 |
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
|
@@ -5125,6 +6479,15 @@
|
|
| 5125 |
"url": "https://opencollective.com/unified"
|
| 5126 |
}
|
| 5127 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5128 |
"node_modules/update-browserslist-db": {
|
| 5129 |
"version": "1.2.3",
|
| 5130 |
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
|
@@ -5166,6 +6529,21 @@
|
|
| 5166 |
"punycode": "^2.1.0"
|
| 5167 |
}
|
| 5168 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5169 |
"node_modules/vfile": {
|
| 5170 |
"version": "6.0.3",
|
| 5171 |
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
|
@@ -5275,6 +6653,15 @@
|
|
| 5275 |
"integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==",
|
| 5276 |
"license": "Apache-2.0"
|
| 5277 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5278 |
"node_modules/websocket-driver": {
|
| 5279 |
"version": "0.7.4",
|
| 5280 |
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
|
|
@@ -5298,6 +6685,19 @@
|
|
| 5298 |
"node": ">=0.8.0"
|
| 5299 |
}
|
| 5300 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5301 |
"node_modules/which": {
|
| 5302 |
"version": "2.0.2",
|
| 5303 |
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
|
@@ -5341,6 +6741,50 @@
|
|
| 5341 |
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
| 5342 |
}
|
| 5343 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5344 |
"node_modules/y18n": {
|
| 5345 |
"version": "5.0.8",
|
| 5346 |
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
|
|
|
| 9 |
"version": "0.0.0",
|
| 10 |
"dependencies": {
|
| 11 |
"axios": "^1.13.2",
|
| 12 |
+
"bcrypt": "^6.0.0",
|
| 13 |
+
"cors": "^2.8.5",
|
| 14 |
+
"dotenv": "^17.2.3",
|
| 15 |
+
"express": "^5.2.1",
|
| 16 |
"firebase": "^12.7.0",
|
| 17 |
+
"jsonwebtoken": "^9.0.3",
|
| 18 |
"leaflet": "^1.9.4",
|
| 19 |
"lucide-react": "^0.562.0",
|
| 20 |
+
"mongoose": "^9.1.0",
|
| 21 |
+
"multer": "^2.0.2",
|
| 22 |
"react": "^19.2.0",
|
| 23 |
"react-dom": "^19.2.0",
|
| 24 |
"react-leaflet": "^5.0.0",
|
| 25 |
+
"react-markdown": "^10.1.0",
|
| 26 |
+
"socket.io": "^4.8.3",
|
| 27 |
+
"socket.io-client": "^4.8.3"
|
| 28 |
},
|
| 29 |
"devDependencies": {
|
| 30 |
"@eslint/js": "^9.39.1",
|
|
|
|
| 1663 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 1664 |
}
|
| 1665 |
},
|
| 1666 |
+
"node_modules/@mongodb-js/saslprep": {
|
| 1667 |
+
"version": "1.4.4",
|
| 1668 |
+
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.4.tgz",
|
| 1669 |
+
"integrity": "sha512-p7X/ytJDIdwUfFL/CLOhKgdfJe1Fa8uw9seJYvdOmnP9JBWGWHW69HkOixXS6Wy9yvGf1MbhcS6lVmrhy4jm2g==",
|
| 1670 |
+
"license": "MIT",
|
| 1671 |
+
"dependencies": {
|
| 1672 |
+
"sparse-bitfield": "^3.0.3"
|
| 1673 |
+
}
|
| 1674 |
+
},
|
| 1675 |
"node_modules/@protobufjs/aspromise": {
|
| 1676 |
"version": "1.1.2",
|
| 1677 |
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
|
|
|
| 2062 |
"win32"
|
| 2063 |
]
|
| 2064 |
},
|
| 2065 |
+
"node_modules/@socket.io/component-emitter": {
|
| 2066 |
+
"version": "3.1.2",
|
| 2067 |
+
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
| 2068 |
+
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
| 2069 |
+
"license": "MIT"
|
| 2070 |
+
},
|
| 2071 |
"node_modules/@types/babel__core": {
|
| 2072 |
"version": "7.20.5",
|
| 2073 |
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
|
|
|
| 2113 |
"@babel/types": "^7.28.2"
|
| 2114 |
}
|
| 2115 |
},
|
| 2116 |
+
"node_modules/@types/cors": {
|
| 2117 |
+
"version": "2.8.19",
|
| 2118 |
+
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
|
| 2119 |
+
"integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
|
| 2120 |
+
"license": "MIT",
|
| 2121 |
+
"dependencies": {
|
| 2122 |
+
"@types/node": "*"
|
| 2123 |
+
}
|
| 2124 |
+
},
|
| 2125 |
"node_modules/@types/debug": {
|
| 2126 |
"version": "4.1.12",
|
| 2127 |
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
|
|
|
| 2211 |
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
| 2212 |
"license": "MIT"
|
| 2213 |
},
|
| 2214 |
+
"node_modules/@types/webidl-conversions": {
|
| 2215 |
+
"version": "7.0.3",
|
| 2216 |
+
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
| 2217 |
+
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
|
| 2218 |
+
"license": "MIT"
|
| 2219 |
+
},
|
| 2220 |
+
"node_modules/@types/whatwg-url": {
|
| 2221 |
+
"version": "13.0.0",
|
| 2222 |
+
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
|
| 2223 |
+
"integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
|
| 2224 |
+
"license": "MIT",
|
| 2225 |
+
"dependencies": {
|
| 2226 |
+
"@types/webidl-conversions": "*"
|
| 2227 |
+
}
|
| 2228 |
+
},
|
| 2229 |
"node_modules/@ungap/structured-clone": {
|
| 2230 |
"version": "1.3.0",
|
| 2231 |
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
|
|
|
| 2253 |
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
| 2254 |
}
|
| 2255 |
},
|
| 2256 |
+
"node_modules/accepts": {
|
| 2257 |
+
"version": "2.0.0",
|
| 2258 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
| 2259 |
+
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
| 2260 |
+
"license": "MIT",
|
| 2261 |
+
"dependencies": {
|
| 2262 |
+
"mime-types": "^3.0.0",
|
| 2263 |
+
"negotiator": "^1.0.0"
|
| 2264 |
+
},
|
| 2265 |
+
"engines": {
|
| 2266 |
+
"node": ">= 0.6"
|
| 2267 |
+
}
|
| 2268 |
+
},
|
| 2269 |
+
"node_modules/accepts/node_modules/mime-db": {
|
| 2270 |
+
"version": "1.54.0",
|
| 2271 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 2272 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 2273 |
+
"license": "MIT",
|
| 2274 |
+
"engines": {
|
| 2275 |
+
"node": ">= 0.6"
|
| 2276 |
+
}
|
| 2277 |
+
},
|
| 2278 |
+
"node_modules/accepts/node_modules/mime-types": {
|
| 2279 |
+
"version": "3.0.2",
|
| 2280 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
| 2281 |
+
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
| 2282 |
+
"license": "MIT",
|
| 2283 |
+
"dependencies": {
|
| 2284 |
+
"mime-db": "^1.54.0"
|
| 2285 |
+
},
|
| 2286 |
+
"engines": {
|
| 2287 |
+
"node": ">=18"
|
| 2288 |
+
},
|
| 2289 |
+
"funding": {
|
| 2290 |
+
"type": "opencollective",
|
| 2291 |
+
"url": "https://opencollective.com/express"
|
| 2292 |
+
}
|
| 2293 |
+
},
|
| 2294 |
"node_modules/acorn": {
|
| 2295 |
"version": "8.15.0",
|
| 2296 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
|
|
|
| 2355 |
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
| 2356 |
}
|
| 2357 |
},
|
| 2358 |
+
"node_modules/append-field": {
|
| 2359 |
+
"version": "1.0.0",
|
| 2360 |
+
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
| 2361 |
+
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
| 2362 |
+
"license": "MIT"
|
| 2363 |
+
},
|
| 2364 |
"node_modules/argparse": {
|
| 2365 |
"version": "2.0.1",
|
| 2366 |
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
|
|
|
| 2439 |
"dev": true,
|
| 2440 |
"license": "MIT"
|
| 2441 |
},
|
| 2442 |
+
"node_modules/base64id": {
|
| 2443 |
+
"version": "2.0.0",
|
| 2444 |
+
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
| 2445 |
+
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
| 2446 |
+
"license": "MIT",
|
| 2447 |
+
"engines": {
|
| 2448 |
+
"node": "^4.5.0 || >= 5.9"
|
| 2449 |
+
}
|
| 2450 |
+
},
|
| 2451 |
"node_modules/baseline-browser-mapping": {
|
| 2452 |
"version": "2.9.11",
|
| 2453 |
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
|
|
|
|
| 2458 |
"baseline-browser-mapping": "dist/cli.js"
|
| 2459 |
}
|
| 2460 |
},
|
| 2461 |
+
"node_modules/bcrypt": {
|
| 2462 |
+
"version": "6.0.0",
|
| 2463 |
+
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz",
|
| 2464 |
+
"integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==",
|
| 2465 |
+
"hasInstallScript": true,
|
| 2466 |
+
"license": "MIT",
|
| 2467 |
+
"dependencies": {
|
| 2468 |
+
"node-addon-api": "^8.3.0",
|
| 2469 |
+
"node-gyp-build": "^4.8.4"
|
| 2470 |
+
},
|
| 2471 |
+
"engines": {
|
| 2472 |
+
"node": ">= 18"
|
| 2473 |
+
}
|
| 2474 |
+
},
|
| 2475 |
+
"node_modules/body-parser": {
|
| 2476 |
+
"version": "2.2.1",
|
| 2477 |
+
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
|
| 2478 |
+
"integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
|
| 2479 |
+
"license": "MIT",
|
| 2480 |
+
"dependencies": {
|
| 2481 |
+
"bytes": "^3.1.2",
|
| 2482 |
+
"content-type": "^1.0.5",
|
| 2483 |
+
"debug": "^4.4.3",
|
| 2484 |
+
"http-errors": "^2.0.0",
|
| 2485 |
+
"iconv-lite": "^0.7.0",
|
| 2486 |
+
"on-finished": "^2.4.1",
|
| 2487 |
+
"qs": "^6.14.0",
|
| 2488 |
+
"raw-body": "^3.0.1",
|
| 2489 |
+
"type-is": "^2.0.1"
|
| 2490 |
+
},
|
| 2491 |
+
"engines": {
|
| 2492 |
+
"node": ">=18"
|
| 2493 |
+
},
|
| 2494 |
+
"funding": {
|
| 2495 |
+
"type": "opencollective",
|
| 2496 |
+
"url": "https://opencollective.com/express"
|
| 2497 |
+
}
|
| 2498 |
+
},
|
| 2499 |
"node_modules/brace-expansion": {
|
| 2500 |
"version": "1.1.12",
|
| 2501 |
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
|
|
|
| 2541 |
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
| 2542 |
}
|
| 2543 |
},
|
| 2544 |
+
"node_modules/bson": {
|
| 2545 |
+
"version": "7.0.0",
|
| 2546 |
+
"resolved": "https://registry.npmjs.org/bson/-/bson-7.0.0.tgz",
|
| 2547 |
+
"integrity": "sha512-Kwc6Wh4lQ5OmkqqKhYGKIuELXl+EPYSCObVE6bWsp1T/cGkOCBN0I8wF/T44BiuhHyNi1mmKVPXk60d41xZ7kw==",
|
| 2548 |
+
"license": "Apache-2.0",
|
| 2549 |
+
"engines": {
|
| 2550 |
+
"node": ">=20.19.0"
|
| 2551 |
+
}
|
| 2552 |
+
},
|
| 2553 |
+
"node_modules/buffer-equal-constant-time": {
|
| 2554 |
+
"version": "1.0.1",
|
| 2555 |
+
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
| 2556 |
+
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
| 2557 |
+
"license": "BSD-3-Clause"
|
| 2558 |
+
},
|
| 2559 |
+
"node_modules/buffer-from": {
|
| 2560 |
+
"version": "1.1.2",
|
| 2561 |
+
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
| 2562 |
+
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
| 2563 |
+
"license": "MIT"
|
| 2564 |
+
},
|
| 2565 |
+
"node_modules/busboy": {
|
| 2566 |
+
"version": "1.6.0",
|
| 2567 |
+
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
| 2568 |
+
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
| 2569 |
+
"dependencies": {
|
| 2570 |
+
"streamsearch": "^1.1.0"
|
| 2571 |
+
},
|
| 2572 |
+
"engines": {
|
| 2573 |
+
"node": ">=10.16.0"
|
| 2574 |
+
}
|
| 2575 |
+
},
|
| 2576 |
+
"node_modules/bytes": {
|
| 2577 |
+
"version": "3.1.2",
|
| 2578 |
+
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
| 2579 |
+
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
| 2580 |
+
"license": "MIT",
|
| 2581 |
+
"engines": {
|
| 2582 |
+
"node": ">= 0.8"
|
| 2583 |
+
}
|
| 2584 |
+
},
|
| 2585 |
"node_modules/call-bind-apply-helpers": {
|
| 2586 |
"version": "1.0.2",
|
| 2587 |
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
|
|
|
| 2595 |
"node": ">= 0.4"
|
| 2596 |
}
|
| 2597 |
},
|
| 2598 |
+
"node_modules/call-bound": {
|
| 2599 |
+
"version": "1.0.4",
|
| 2600 |
+
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
| 2601 |
+
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
| 2602 |
+
"license": "MIT",
|
| 2603 |
+
"dependencies": {
|
| 2604 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 2605 |
+
"get-intrinsic": "^1.3.0"
|
| 2606 |
+
},
|
| 2607 |
+
"engines": {
|
| 2608 |
+
"node": ">= 0.4"
|
| 2609 |
+
},
|
| 2610 |
+
"funding": {
|
| 2611 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 2612 |
+
}
|
| 2613 |
+
},
|
| 2614 |
"node_modules/callsites": {
|
| 2615 |
"version": "3.1.0",
|
| 2616 |
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
|
|
|
| 2770 |
"dev": true,
|
| 2771 |
"license": "MIT"
|
| 2772 |
},
|
| 2773 |
+
"node_modules/concat-stream": {
|
| 2774 |
+
"version": "2.0.0",
|
| 2775 |
+
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
| 2776 |
+
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
| 2777 |
+
"engines": [
|
| 2778 |
+
"node >= 6.0"
|
| 2779 |
+
],
|
| 2780 |
+
"license": "MIT",
|
| 2781 |
+
"dependencies": {
|
| 2782 |
+
"buffer-from": "^1.0.0",
|
| 2783 |
+
"inherits": "^2.0.3",
|
| 2784 |
+
"readable-stream": "^3.0.2",
|
| 2785 |
+
"typedarray": "^0.0.6"
|
| 2786 |
+
}
|
| 2787 |
+
},
|
| 2788 |
+
"node_modules/content-disposition": {
|
| 2789 |
+
"version": "1.0.1",
|
| 2790 |
+
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
| 2791 |
+
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
|
| 2792 |
+
"license": "MIT",
|
| 2793 |
+
"engines": {
|
| 2794 |
+
"node": ">=18"
|
| 2795 |
+
},
|
| 2796 |
+
"funding": {
|
| 2797 |
+
"type": "opencollective",
|
| 2798 |
+
"url": "https://opencollective.com/express"
|
| 2799 |
+
}
|
| 2800 |
+
},
|
| 2801 |
+
"node_modules/content-type": {
|
| 2802 |
+
"version": "1.0.5",
|
| 2803 |
+
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
| 2804 |
+
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
| 2805 |
+
"license": "MIT",
|
| 2806 |
+
"engines": {
|
| 2807 |
+
"node": ">= 0.6"
|
| 2808 |
+
}
|
| 2809 |
+
},
|
| 2810 |
"node_modules/convert-source-map": {
|
| 2811 |
"version": "2.0.0",
|
| 2812 |
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
|
|
|
| 2814 |
"dev": true,
|
| 2815 |
"license": "MIT"
|
| 2816 |
},
|
| 2817 |
+
"node_modules/cookie": {
|
| 2818 |
+
"version": "0.7.2",
|
| 2819 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
| 2820 |
+
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
| 2821 |
+
"license": "MIT",
|
| 2822 |
+
"engines": {
|
| 2823 |
+
"node": ">= 0.6"
|
| 2824 |
+
}
|
| 2825 |
+
},
|
| 2826 |
+
"node_modules/cookie-signature": {
|
| 2827 |
+
"version": "1.2.2",
|
| 2828 |
+
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
| 2829 |
+
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
| 2830 |
+
"license": "MIT",
|
| 2831 |
+
"engines": {
|
| 2832 |
+
"node": ">=6.6.0"
|
| 2833 |
+
}
|
| 2834 |
+
},
|
| 2835 |
+
"node_modules/cors": {
|
| 2836 |
+
"version": "2.8.5",
|
| 2837 |
+
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
| 2838 |
+
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
| 2839 |
+
"license": "MIT",
|
| 2840 |
+
"dependencies": {
|
| 2841 |
+
"object-assign": "^4",
|
| 2842 |
+
"vary": "^1"
|
| 2843 |
+
},
|
| 2844 |
+
"engines": {
|
| 2845 |
+
"node": ">= 0.10"
|
| 2846 |
+
}
|
| 2847 |
+
},
|
| 2848 |
"node_modules/cross-spawn": {
|
| 2849 |
"version": "7.0.6",
|
| 2850 |
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
|
|
|
| 2912 |
"node": ">=0.4.0"
|
| 2913 |
}
|
| 2914 |
},
|
| 2915 |
+
"node_modules/depd": {
|
| 2916 |
+
"version": "2.0.0",
|
| 2917 |
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
| 2918 |
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
| 2919 |
+
"license": "MIT",
|
| 2920 |
+
"engines": {
|
| 2921 |
+
"node": ">= 0.8"
|
| 2922 |
+
}
|
| 2923 |
+
},
|
| 2924 |
"node_modules/dequal": {
|
| 2925 |
"version": "2.0.3",
|
| 2926 |
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
|
|
|
| 2943 |
"url": "https://github.com/sponsors/wooorm"
|
| 2944 |
}
|
| 2945 |
},
|
| 2946 |
+
"node_modules/dotenv": {
|
| 2947 |
+
"version": "17.2.3",
|
| 2948 |
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
| 2949 |
+
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
|
| 2950 |
+
"license": "BSD-2-Clause",
|
| 2951 |
+
"engines": {
|
| 2952 |
+
"node": ">=12"
|
| 2953 |
+
},
|
| 2954 |
+
"funding": {
|
| 2955 |
+
"url": "https://dotenvx.com"
|
| 2956 |
+
}
|
| 2957 |
+
},
|
| 2958 |
"node_modules/dunder-proto": {
|
| 2959 |
"version": "1.0.1",
|
| 2960 |
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
|
|
| 2969 |
"node": ">= 0.4"
|
| 2970 |
}
|
| 2971 |
},
|
| 2972 |
+
"node_modules/ecdsa-sig-formatter": {
|
| 2973 |
+
"version": "1.0.11",
|
| 2974 |
+
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
| 2975 |
+
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
| 2976 |
+
"license": "Apache-2.0",
|
| 2977 |
+
"dependencies": {
|
| 2978 |
+
"safe-buffer": "^5.0.1"
|
| 2979 |
+
}
|
| 2980 |
+
},
|
| 2981 |
+
"node_modules/ee-first": {
|
| 2982 |
+
"version": "1.1.1",
|
| 2983 |
+
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
| 2984 |
+
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
| 2985 |
+
"license": "MIT"
|
| 2986 |
+
},
|
| 2987 |
"node_modules/electron-to-chromium": {
|
| 2988 |
"version": "1.5.267",
|
| 2989 |
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
|
|
|
|
| 2997 |
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
| 2998 |
"license": "MIT"
|
| 2999 |
},
|
| 3000 |
+
"node_modules/encodeurl": {
|
| 3001 |
+
"version": "2.0.0",
|
| 3002 |
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
| 3003 |
+
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
| 3004 |
+
"license": "MIT",
|
| 3005 |
+
"engines": {
|
| 3006 |
+
"node": ">= 0.8"
|
| 3007 |
+
}
|
| 3008 |
+
},
|
| 3009 |
+
"node_modules/engine.io": {
|
| 3010 |
+
"version": "6.6.5",
|
| 3011 |
+
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz",
|
| 3012 |
+
"integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==",
|
| 3013 |
+
"license": "MIT",
|
| 3014 |
+
"dependencies": {
|
| 3015 |
+
"@types/cors": "^2.8.12",
|
| 3016 |
+
"@types/node": ">=10.0.0",
|
| 3017 |
+
"accepts": "~1.3.4",
|
| 3018 |
+
"base64id": "2.0.0",
|
| 3019 |
+
"cookie": "~0.7.2",
|
| 3020 |
+
"cors": "~2.8.5",
|
| 3021 |
+
"debug": "~4.4.1",
|
| 3022 |
+
"engine.io-parser": "~5.2.1",
|
| 3023 |
+
"ws": "~8.18.3"
|
| 3024 |
+
},
|
| 3025 |
+
"engines": {
|
| 3026 |
+
"node": ">=10.2.0"
|
| 3027 |
+
}
|
| 3028 |
+
},
|
| 3029 |
+
"node_modules/engine.io-client": {
|
| 3030 |
+
"version": "6.6.4",
|
| 3031 |
+
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz",
|
| 3032 |
+
"integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==",
|
| 3033 |
+
"license": "MIT",
|
| 3034 |
+
"dependencies": {
|
| 3035 |
+
"@socket.io/component-emitter": "~3.1.0",
|
| 3036 |
+
"debug": "~4.4.1",
|
| 3037 |
+
"engine.io-parser": "~5.2.1",
|
| 3038 |
+
"ws": "~8.18.3",
|
| 3039 |
+
"xmlhttprequest-ssl": "~2.1.1"
|
| 3040 |
+
}
|
| 3041 |
+
},
|
| 3042 |
+
"node_modules/engine.io-parser": {
|
| 3043 |
+
"version": "5.2.3",
|
| 3044 |
+
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
| 3045 |
+
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
| 3046 |
+
"license": "MIT",
|
| 3047 |
+
"engines": {
|
| 3048 |
+
"node": ">=10.0.0"
|
| 3049 |
+
}
|
| 3050 |
+
},
|
| 3051 |
+
"node_modules/engine.io/node_modules/accepts": {
|
| 3052 |
+
"version": "1.3.8",
|
| 3053 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
| 3054 |
+
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
| 3055 |
+
"license": "MIT",
|
| 3056 |
+
"dependencies": {
|
| 3057 |
+
"mime-types": "~2.1.34",
|
| 3058 |
+
"negotiator": "0.6.3"
|
| 3059 |
+
},
|
| 3060 |
+
"engines": {
|
| 3061 |
+
"node": ">= 0.6"
|
| 3062 |
+
}
|
| 3063 |
+
},
|
| 3064 |
+
"node_modules/engine.io/node_modules/negotiator": {
|
| 3065 |
+
"version": "0.6.3",
|
| 3066 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
| 3067 |
+
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
| 3068 |
+
"license": "MIT",
|
| 3069 |
+
"engines": {
|
| 3070 |
+
"node": ">= 0.6"
|
| 3071 |
+
}
|
| 3072 |
+
},
|
| 3073 |
"node_modules/es-define-property": {
|
| 3074 |
"version": "1.0.1",
|
| 3075 |
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
|
|
|
| 3166 |
"node": ">=6"
|
| 3167 |
}
|
| 3168 |
},
|
| 3169 |
+
"node_modules/escape-html": {
|
| 3170 |
+
"version": "1.0.3",
|
| 3171 |
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
| 3172 |
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 3173 |
+
"license": "MIT"
|
| 3174 |
+
},
|
| 3175 |
"node_modules/escape-string-regexp": {
|
| 3176 |
"version": "4.0.0",
|
| 3177 |
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
|
|
|
| 3379 |
"node": ">=0.10.0"
|
| 3380 |
}
|
| 3381 |
},
|
| 3382 |
+
"node_modules/etag": {
|
| 3383 |
+
"version": "1.8.1",
|
| 3384 |
+
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
| 3385 |
+
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
| 3386 |
+
"license": "MIT",
|
| 3387 |
+
"engines": {
|
| 3388 |
+
"node": ">= 0.6"
|
| 3389 |
+
}
|
| 3390 |
+
},
|
| 3391 |
+
"node_modules/express": {
|
| 3392 |
+
"version": "5.2.1",
|
| 3393 |
+
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
| 3394 |
+
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
| 3395 |
+
"license": "MIT",
|
| 3396 |
+
"dependencies": {
|
| 3397 |
+
"accepts": "^2.0.0",
|
| 3398 |
+
"body-parser": "^2.2.1",
|
| 3399 |
+
"content-disposition": "^1.0.0",
|
| 3400 |
+
"content-type": "^1.0.5",
|
| 3401 |
+
"cookie": "^0.7.1",
|
| 3402 |
+
"cookie-signature": "^1.2.1",
|
| 3403 |
+
"debug": "^4.4.0",
|
| 3404 |
+
"depd": "^2.0.0",
|
| 3405 |
+
"encodeurl": "^2.0.0",
|
| 3406 |
+
"escape-html": "^1.0.3",
|
| 3407 |
+
"etag": "^1.8.1",
|
| 3408 |
+
"finalhandler": "^2.1.0",
|
| 3409 |
+
"fresh": "^2.0.0",
|
| 3410 |
+
"http-errors": "^2.0.0",
|
| 3411 |
+
"merge-descriptors": "^2.0.0",
|
| 3412 |
+
"mime-types": "^3.0.0",
|
| 3413 |
+
"on-finished": "^2.4.1",
|
| 3414 |
+
"once": "^1.4.0",
|
| 3415 |
+
"parseurl": "^1.3.3",
|
| 3416 |
+
"proxy-addr": "^2.0.7",
|
| 3417 |
+
"qs": "^6.14.0",
|
| 3418 |
+
"range-parser": "^1.2.1",
|
| 3419 |
+
"router": "^2.2.0",
|
| 3420 |
+
"send": "^1.1.0",
|
| 3421 |
+
"serve-static": "^2.2.0",
|
| 3422 |
+
"statuses": "^2.0.1",
|
| 3423 |
+
"type-is": "^2.0.1",
|
| 3424 |
+
"vary": "^1.1.2"
|
| 3425 |
+
},
|
| 3426 |
+
"engines": {
|
| 3427 |
+
"node": ">= 18"
|
| 3428 |
+
},
|
| 3429 |
+
"funding": {
|
| 3430 |
+
"type": "opencollective",
|
| 3431 |
+
"url": "https://opencollective.com/express"
|
| 3432 |
+
}
|
| 3433 |
+
},
|
| 3434 |
+
"node_modules/express/node_modules/mime-db": {
|
| 3435 |
+
"version": "1.54.0",
|
| 3436 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 3437 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 3438 |
+
"license": "MIT",
|
| 3439 |
+
"engines": {
|
| 3440 |
+
"node": ">= 0.6"
|
| 3441 |
+
}
|
| 3442 |
+
},
|
| 3443 |
+
"node_modules/express/node_modules/mime-types": {
|
| 3444 |
+
"version": "3.0.2",
|
| 3445 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
| 3446 |
+
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
| 3447 |
+
"license": "MIT",
|
| 3448 |
+
"dependencies": {
|
| 3449 |
+
"mime-db": "^1.54.0"
|
| 3450 |
+
},
|
| 3451 |
+
"engines": {
|
| 3452 |
+
"node": ">=18"
|
| 3453 |
+
},
|
| 3454 |
+
"funding": {
|
| 3455 |
+
"type": "opencollective",
|
| 3456 |
+
"url": "https://opencollective.com/express"
|
| 3457 |
+
}
|
| 3458 |
+
},
|
| 3459 |
"node_modules/extend": {
|
| 3460 |
"version": "3.0.2",
|
| 3461 |
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
|
|
|
| 3526 |
"node": ">=16.0.0"
|
| 3527 |
}
|
| 3528 |
},
|
| 3529 |
+
"node_modules/finalhandler": {
|
| 3530 |
+
"version": "2.1.1",
|
| 3531 |
+
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
|
| 3532 |
+
"integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
|
| 3533 |
+
"license": "MIT",
|
| 3534 |
+
"dependencies": {
|
| 3535 |
+
"debug": "^4.4.0",
|
| 3536 |
+
"encodeurl": "^2.0.0",
|
| 3537 |
+
"escape-html": "^1.0.3",
|
| 3538 |
+
"on-finished": "^2.4.1",
|
| 3539 |
+
"parseurl": "^1.3.3",
|
| 3540 |
+
"statuses": "^2.0.1"
|
| 3541 |
+
},
|
| 3542 |
+
"engines": {
|
| 3543 |
+
"node": ">= 18.0.0"
|
| 3544 |
+
},
|
| 3545 |
+
"funding": {
|
| 3546 |
+
"type": "opencollective",
|
| 3547 |
+
"url": "https://opencollective.com/express"
|
| 3548 |
+
}
|
| 3549 |
+
},
|
| 3550 |
"node_modules/find-up": {
|
| 3551 |
"version": "5.0.0",
|
| 3552 |
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
|
|
|
| 3657 |
"node": ">= 6"
|
| 3658 |
}
|
| 3659 |
},
|
| 3660 |
+
"node_modules/forwarded": {
|
| 3661 |
+
"version": "0.2.0",
|
| 3662 |
+
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
| 3663 |
+
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
| 3664 |
+
"license": "MIT",
|
| 3665 |
+
"engines": {
|
| 3666 |
+
"node": ">= 0.6"
|
| 3667 |
+
}
|
| 3668 |
+
},
|
| 3669 |
"node_modules/fraction.js": {
|
| 3670 |
"version": "5.3.4",
|
| 3671 |
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
|
|
|
|
| 3680 |
"url": "https://github.com/sponsors/rawify"
|
| 3681 |
}
|
| 3682 |
},
|
| 3683 |
+
"node_modules/fresh": {
|
| 3684 |
+
"version": "2.0.0",
|
| 3685 |
+
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
| 3686 |
+
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
| 3687 |
+
"license": "MIT",
|
| 3688 |
+
"engines": {
|
| 3689 |
+
"node": ">= 0.8"
|
| 3690 |
+
}
|
| 3691 |
+
},
|
| 3692 |
"node_modules/fsevents": {
|
| 3693 |
"version": "2.3.3",
|
| 3694 |
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
|
|
| 3923 |
"url": "https://opencollective.com/unified"
|
| 3924 |
}
|
| 3925 |
},
|
| 3926 |
+
"node_modules/http-errors": {
|
| 3927 |
+
"version": "2.0.1",
|
| 3928 |
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
| 3929 |
+
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
| 3930 |
+
"license": "MIT",
|
| 3931 |
+
"dependencies": {
|
| 3932 |
+
"depd": "~2.0.0",
|
| 3933 |
+
"inherits": "~2.0.4",
|
| 3934 |
+
"setprototypeof": "~1.2.0",
|
| 3935 |
+
"statuses": "~2.0.2",
|
| 3936 |
+
"toidentifier": "~1.0.1"
|
| 3937 |
+
},
|
| 3938 |
+
"engines": {
|
| 3939 |
+
"node": ">= 0.8"
|
| 3940 |
+
},
|
| 3941 |
+
"funding": {
|
| 3942 |
+
"type": "opencollective",
|
| 3943 |
+
"url": "https://opencollective.com/express"
|
| 3944 |
+
}
|
| 3945 |
+
},
|
| 3946 |
"node_modules/http-parser-js": {
|
| 3947 |
"version": "0.5.10",
|
| 3948 |
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
|
| 3949 |
"integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
|
| 3950 |
"license": "MIT"
|
| 3951 |
},
|
| 3952 |
+
"node_modules/iconv-lite": {
|
| 3953 |
+
"version": "0.7.1",
|
| 3954 |
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz",
|
| 3955 |
+
"integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==",
|
| 3956 |
+
"license": "MIT",
|
| 3957 |
+
"dependencies": {
|
| 3958 |
+
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
| 3959 |
+
},
|
| 3960 |
+
"engines": {
|
| 3961 |
+
"node": ">=0.10.0"
|
| 3962 |
+
},
|
| 3963 |
+
"funding": {
|
| 3964 |
+
"type": "opencollective",
|
| 3965 |
+
"url": "https://opencollective.com/express"
|
| 3966 |
+
}
|
| 3967 |
+
},
|
| 3968 |
+
"node_modules/idb": {
|
| 3969 |
+
"version": "7.1.1",
|
| 3970 |
+
"resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
|
| 3971 |
+
"integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
|
| 3972 |
+
"license": "ISC"
|
| 3973 |
},
|
| 3974 |
"node_modules/ignore": {
|
| 3975 |
"version": "5.3.2",
|
|
|
|
| 4008 |
"node": ">=0.8.19"
|
| 4009 |
}
|
| 4010 |
},
|
| 4011 |
+
"node_modules/inherits": {
|
| 4012 |
+
"version": "2.0.4",
|
| 4013 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
| 4014 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 4015 |
+
"license": "ISC"
|
| 4016 |
+
},
|
| 4017 |
"node_modules/inline-style-parser": {
|
| 4018 |
"version": "0.2.7",
|
| 4019 |
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
|
| 4020 |
"integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
|
| 4021 |
"license": "MIT"
|
| 4022 |
},
|
| 4023 |
+
"node_modules/ipaddr.js": {
|
| 4024 |
+
"version": "1.9.1",
|
| 4025 |
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
| 4026 |
+
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
| 4027 |
+
"license": "MIT",
|
| 4028 |
+
"engines": {
|
| 4029 |
+
"node": ">= 0.10"
|
| 4030 |
+
}
|
| 4031 |
+
},
|
| 4032 |
"node_modules/is-alphabetical": {
|
| 4033 |
"version": "2.0.1",
|
| 4034 |
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
|
|
|
|
| 4117 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 4118 |
}
|
| 4119 |
},
|
| 4120 |
+
"node_modules/is-promise": {
|
| 4121 |
+
"version": "4.0.0",
|
| 4122 |
+
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
| 4123 |
+
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
| 4124 |
+
"license": "MIT"
|
| 4125 |
+
},
|
| 4126 |
"node_modules/isexe": {
|
| 4127 |
"version": "2.0.0",
|
| 4128 |
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
|
|
|
| 4197 |
"node": ">=6"
|
| 4198 |
}
|
| 4199 |
},
|
| 4200 |
+
"node_modules/jsonwebtoken": {
|
| 4201 |
+
"version": "9.0.3",
|
| 4202 |
+
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
|
| 4203 |
+
"integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
|
| 4204 |
+
"license": "MIT",
|
| 4205 |
+
"dependencies": {
|
| 4206 |
+
"jws": "^4.0.1",
|
| 4207 |
+
"lodash.includes": "^4.3.0",
|
| 4208 |
+
"lodash.isboolean": "^3.0.3",
|
| 4209 |
+
"lodash.isinteger": "^4.0.4",
|
| 4210 |
+
"lodash.isnumber": "^3.0.3",
|
| 4211 |
+
"lodash.isplainobject": "^4.0.6",
|
| 4212 |
+
"lodash.isstring": "^4.0.1",
|
| 4213 |
+
"lodash.once": "^4.0.0",
|
| 4214 |
+
"ms": "^2.1.1",
|
| 4215 |
+
"semver": "^7.5.4"
|
| 4216 |
+
},
|
| 4217 |
+
"engines": {
|
| 4218 |
+
"node": ">=12",
|
| 4219 |
+
"npm": ">=6"
|
| 4220 |
+
}
|
| 4221 |
+
},
|
| 4222 |
+
"node_modules/jsonwebtoken/node_modules/semver": {
|
| 4223 |
+
"version": "7.7.3",
|
| 4224 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
| 4225 |
+
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
| 4226 |
+
"license": "ISC",
|
| 4227 |
+
"bin": {
|
| 4228 |
+
"semver": "bin/semver.js"
|
| 4229 |
+
},
|
| 4230 |
+
"engines": {
|
| 4231 |
+
"node": ">=10"
|
| 4232 |
+
}
|
| 4233 |
+
},
|
| 4234 |
+
"node_modules/jwa": {
|
| 4235 |
+
"version": "2.0.1",
|
| 4236 |
+
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
|
| 4237 |
+
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
|
| 4238 |
+
"license": "MIT",
|
| 4239 |
+
"dependencies": {
|
| 4240 |
+
"buffer-equal-constant-time": "^1.0.1",
|
| 4241 |
+
"ecdsa-sig-formatter": "1.0.11",
|
| 4242 |
+
"safe-buffer": "^5.0.1"
|
| 4243 |
+
}
|
| 4244 |
+
},
|
| 4245 |
+
"node_modules/jws": {
|
| 4246 |
+
"version": "4.0.1",
|
| 4247 |
+
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
|
| 4248 |
+
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
|
| 4249 |
+
"license": "MIT",
|
| 4250 |
+
"dependencies": {
|
| 4251 |
+
"jwa": "^2.0.1",
|
| 4252 |
+
"safe-buffer": "^5.0.1"
|
| 4253 |
+
}
|
| 4254 |
+
},
|
| 4255 |
+
"node_modules/kareem": {
|
| 4256 |
+
"version": "3.0.0",
|
| 4257 |
+
"resolved": "https://registry.npmjs.org/kareem/-/kareem-3.0.0.tgz",
|
| 4258 |
+
"integrity": "sha512-RKhaOBSPN8L7y4yAgNhDT2602G5FD6QbOIISbjN9D6mjHPeqeg7K+EB5IGSU5o81/X2Gzm3ICnAvQW3x3OP8HA==",
|
| 4259 |
+
"license": "Apache-2.0",
|
| 4260 |
+
"engines": {
|
| 4261 |
+
"node": ">=18.0.0"
|
| 4262 |
+
}
|
| 4263 |
+
},
|
| 4264 |
"node_modules/keyv": {
|
| 4265 |
"version": "4.5.4",
|
| 4266 |
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
|
|
|
| 4313 |
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
| 4314 |
"license": "MIT"
|
| 4315 |
},
|
| 4316 |
+
"node_modules/lodash.includes": {
|
| 4317 |
+
"version": "4.3.0",
|
| 4318 |
+
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
| 4319 |
+
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
|
| 4320 |
+
"license": "MIT"
|
| 4321 |
+
},
|
| 4322 |
+
"node_modules/lodash.isboolean": {
|
| 4323 |
+
"version": "3.0.3",
|
| 4324 |
+
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
| 4325 |
+
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
| 4326 |
+
"license": "MIT"
|
| 4327 |
+
},
|
| 4328 |
+
"node_modules/lodash.isinteger": {
|
| 4329 |
+
"version": "4.0.4",
|
| 4330 |
+
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
| 4331 |
+
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
| 4332 |
+
"license": "MIT"
|
| 4333 |
+
},
|
| 4334 |
+
"node_modules/lodash.isnumber": {
|
| 4335 |
+
"version": "3.0.3",
|
| 4336 |
+
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
| 4337 |
+
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
|
| 4338 |
+
"license": "MIT"
|
| 4339 |
+
},
|
| 4340 |
+
"node_modules/lodash.isplainobject": {
|
| 4341 |
+
"version": "4.0.6",
|
| 4342 |
+
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
| 4343 |
+
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
| 4344 |
+
"license": "MIT"
|
| 4345 |
+
},
|
| 4346 |
+
"node_modules/lodash.isstring": {
|
| 4347 |
+
"version": "4.0.1",
|
| 4348 |
+
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
| 4349 |
+
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
|
| 4350 |
+
"license": "MIT"
|
| 4351 |
+
},
|
| 4352 |
"node_modules/lodash.merge": {
|
| 4353 |
"version": "4.6.2",
|
| 4354 |
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
|
|
|
| 4356 |
"dev": true,
|
| 4357 |
"license": "MIT"
|
| 4358 |
},
|
| 4359 |
+
"node_modules/lodash.once": {
|
| 4360 |
+
"version": "4.1.1",
|
| 4361 |
+
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
| 4362 |
+
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
|
| 4363 |
+
"license": "MIT"
|
| 4364 |
+
},
|
| 4365 |
"node_modules/long": {
|
| 4366 |
"version": "5.3.2",
|
| 4367 |
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
|
|
|
| 4559 |
"url": "https://opencollective.com/unified"
|
| 4560 |
}
|
| 4561 |
},
|
| 4562 |
+
"node_modules/media-typer": {
|
| 4563 |
+
"version": "1.1.0",
|
| 4564 |
+
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
| 4565 |
+
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
| 4566 |
+
"license": "MIT",
|
| 4567 |
+
"engines": {
|
| 4568 |
+
"node": ">= 0.8"
|
| 4569 |
+
}
|
| 4570 |
+
},
|
| 4571 |
+
"node_modules/memory-pager": {
|
| 4572 |
+
"version": "1.5.0",
|
| 4573 |
+
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
| 4574 |
+
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
|
| 4575 |
+
"license": "MIT"
|
| 4576 |
+
},
|
| 4577 |
+
"node_modules/merge-descriptors": {
|
| 4578 |
+
"version": "2.0.0",
|
| 4579 |
+
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
| 4580 |
+
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
| 4581 |
+
"license": "MIT",
|
| 4582 |
+
"engines": {
|
| 4583 |
+
"node": ">=18"
|
| 4584 |
+
},
|
| 4585 |
+
"funding": {
|
| 4586 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 4587 |
+
}
|
| 4588 |
+
},
|
| 4589 |
"node_modules/micromark": {
|
| 4590 |
"version": "4.0.2",
|
| 4591 |
"resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
|
|
|
|
| 5062 |
"node": "*"
|
| 5063 |
}
|
| 5064 |
},
|
| 5065 |
+
"node_modules/minimist": {
|
| 5066 |
+
"version": "1.2.8",
|
| 5067 |
+
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
| 5068 |
+
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
| 5069 |
+
"license": "MIT",
|
| 5070 |
+
"funding": {
|
| 5071 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 5072 |
+
}
|
| 5073 |
+
},
|
| 5074 |
+
"node_modules/mkdirp": {
|
| 5075 |
+
"version": "0.5.6",
|
| 5076 |
+
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
| 5077 |
+
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
| 5078 |
+
"license": "MIT",
|
| 5079 |
+
"dependencies": {
|
| 5080 |
+
"minimist": "^1.2.6"
|
| 5081 |
+
},
|
| 5082 |
+
"bin": {
|
| 5083 |
+
"mkdirp": "bin/cmd.js"
|
| 5084 |
+
}
|
| 5085 |
+
},
|
| 5086 |
+
"node_modules/mongodb": {
|
| 5087 |
+
"version": "7.0.0",
|
| 5088 |
+
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz",
|
| 5089 |
+
"integrity": "sha512-vG/A5cQrvGGvZm2mTnCSz1LUcbOPl83hfB6bxULKQ8oFZauyox/2xbZOoGNl+64m8VBrETkdGCDBdOsCr3F3jg==",
|
| 5090 |
+
"license": "Apache-2.0",
|
| 5091 |
+
"dependencies": {
|
| 5092 |
+
"@mongodb-js/saslprep": "^1.3.0",
|
| 5093 |
+
"bson": "^7.0.0",
|
| 5094 |
+
"mongodb-connection-string-url": "^7.0.0"
|
| 5095 |
+
},
|
| 5096 |
+
"engines": {
|
| 5097 |
+
"node": ">=20.19.0"
|
| 5098 |
+
},
|
| 5099 |
+
"peerDependencies": {
|
| 5100 |
+
"@aws-sdk/credential-providers": "^3.806.0",
|
| 5101 |
+
"@mongodb-js/zstd": "^7.0.0",
|
| 5102 |
+
"gcp-metadata": "^7.0.1",
|
| 5103 |
+
"kerberos": "^7.0.0",
|
| 5104 |
+
"mongodb-client-encryption": ">=7.0.0 <7.1.0",
|
| 5105 |
+
"snappy": "^7.3.2",
|
| 5106 |
+
"socks": "^2.8.6"
|
| 5107 |
+
},
|
| 5108 |
+
"peerDependenciesMeta": {
|
| 5109 |
+
"@aws-sdk/credential-providers": {
|
| 5110 |
+
"optional": true
|
| 5111 |
+
},
|
| 5112 |
+
"@mongodb-js/zstd": {
|
| 5113 |
+
"optional": true
|
| 5114 |
+
},
|
| 5115 |
+
"gcp-metadata": {
|
| 5116 |
+
"optional": true
|
| 5117 |
+
},
|
| 5118 |
+
"kerberos": {
|
| 5119 |
+
"optional": true
|
| 5120 |
+
},
|
| 5121 |
+
"mongodb-client-encryption": {
|
| 5122 |
+
"optional": true
|
| 5123 |
+
},
|
| 5124 |
+
"snappy": {
|
| 5125 |
+
"optional": true
|
| 5126 |
+
},
|
| 5127 |
+
"socks": {
|
| 5128 |
+
"optional": true
|
| 5129 |
+
}
|
| 5130 |
+
}
|
| 5131 |
+
},
|
| 5132 |
+
"node_modules/mongodb-connection-string-url": {
|
| 5133 |
+
"version": "7.0.0",
|
| 5134 |
+
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.0.tgz",
|
| 5135 |
+
"integrity": "sha512-irhhjRVLE20hbkRl4zpAYLnDMM+zIZnp0IDB9akAFFUZp/3XdOfwwddc7y6cNvF2WCEtfTYRwYbIfYa2kVY0og==",
|
| 5136 |
+
"license": "Apache-2.0",
|
| 5137 |
+
"dependencies": {
|
| 5138 |
+
"@types/whatwg-url": "^13.0.0",
|
| 5139 |
+
"whatwg-url": "^14.1.0"
|
| 5140 |
+
},
|
| 5141 |
+
"engines": {
|
| 5142 |
+
"node": ">=20.19.0"
|
| 5143 |
+
}
|
| 5144 |
+
},
|
| 5145 |
+
"node_modules/mongoose": {
|
| 5146 |
+
"version": "9.1.0",
|
| 5147 |
+
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.1.0.tgz",
|
| 5148 |
+
"integrity": "sha512-RVCApwqD6q+O3rsnypmiL1K5+mkN5DwA7BO5a5ofCKh/EZB9FKvcQ4EiqHNmRye3cXhz5DmQ/aVyfBFkXnUbrg==",
|
| 5149 |
+
"license": "MIT",
|
| 5150 |
+
"dependencies": {
|
| 5151 |
+
"kareem": "3.0.0",
|
| 5152 |
+
"mongodb": "~7.0",
|
| 5153 |
+
"mpath": "0.9.0",
|
| 5154 |
+
"mquery": "6.0.0",
|
| 5155 |
+
"ms": "2.1.3",
|
| 5156 |
+
"sift": "17.1.3"
|
| 5157 |
+
},
|
| 5158 |
+
"engines": {
|
| 5159 |
+
"node": ">=20.19.0"
|
| 5160 |
+
},
|
| 5161 |
+
"funding": {
|
| 5162 |
+
"type": "opencollective",
|
| 5163 |
+
"url": "https://opencollective.com/mongoose"
|
| 5164 |
+
}
|
| 5165 |
+
},
|
| 5166 |
+
"node_modules/mpath": {
|
| 5167 |
+
"version": "0.9.0",
|
| 5168 |
+
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
|
| 5169 |
+
"integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
|
| 5170 |
+
"license": "MIT",
|
| 5171 |
+
"engines": {
|
| 5172 |
+
"node": ">=4.0.0"
|
| 5173 |
+
}
|
| 5174 |
+
},
|
| 5175 |
+
"node_modules/mquery": {
|
| 5176 |
+
"version": "6.0.0",
|
| 5177 |
+
"resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz",
|
| 5178 |
+
"integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==",
|
| 5179 |
+
"license": "MIT",
|
| 5180 |
+
"engines": {
|
| 5181 |
+
"node": ">=20.19.0"
|
| 5182 |
+
}
|
| 5183 |
+
},
|
| 5184 |
"node_modules/ms": {
|
| 5185 |
"version": "2.1.3",
|
| 5186 |
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 5187 |
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 5188 |
"license": "MIT"
|
| 5189 |
},
|
| 5190 |
+
"node_modules/multer": {
|
| 5191 |
+
"version": "2.0.2",
|
| 5192 |
+
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
|
| 5193 |
+
"integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
|
| 5194 |
+
"license": "MIT",
|
| 5195 |
+
"dependencies": {
|
| 5196 |
+
"append-field": "^1.0.0",
|
| 5197 |
+
"busboy": "^1.6.0",
|
| 5198 |
+
"concat-stream": "^2.0.0",
|
| 5199 |
+
"mkdirp": "^0.5.6",
|
| 5200 |
+
"object-assign": "^4.1.1",
|
| 5201 |
+
"type-is": "^1.6.18",
|
| 5202 |
+
"xtend": "^4.0.2"
|
| 5203 |
+
},
|
| 5204 |
+
"engines": {
|
| 5205 |
+
"node": ">= 10.16.0"
|
| 5206 |
+
}
|
| 5207 |
+
},
|
| 5208 |
+
"node_modules/multer/node_modules/media-typer": {
|
| 5209 |
+
"version": "0.3.0",
|
| 5210 |
+
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
| 5211 |
+
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
| 5212 |
+
"license": "MIT",
|
| 5213 |
+
"engines": {
|
| 5214 |
+
"node": ">= 0.6"
|
| 5215 |
+
}
|
| 5216 |
+
},
|
| 5217 |
+
"node_modules/multer/node_modules/type-is": {
|
| 5218 |
+
"version": "1.6.18",
|
| 5219 |
+
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
| 5220 |
+
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
| 5221 |
+
"license": "MIT",
|
| 5222 |
+
"dependencies": {
|
| 5223 |
+
"media-typer": "0.3.0",
|
| 5224 |
+
"mime-types": "~2.1.24"
|
| 5225 |
+
},
|
| 5226 |
+
"engines": {
|
| 5227 |
+
"node": ">= 0.6"
|
| 5228 |
+
}
|
| 5229 |
+
},
|
| 5230 |
"node_modules/nanoid": {
|
| 5231 |
"version": "3.3.11",
|
| 5232 |
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
|
|
|
| 5253 |
"dev": true,
|
| 5254 |
"license": "MIT"
|
| 5255 |
},
|
| 5256 |
+
"node_modules/negotiator": {
|
| 5257 |
+
"version": "1.0.0",
|
| 5258 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
| 5259 |
+
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
| 5260 |
+
"license": "MIT",
|
| 5261 |
+
"engines": {
|
| 5262 |
+
"node": ">= 0.6"
|
| 5263 |
+
}
|
| 5264 |
+
},
|
| 5265 |
+
"node_modules/node-addon-api": {
|
| 5266 |
+
"version": "8.5.0",
|
| 5267 |
+
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz",
|
| 5268 |
+
"integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==",
|
| 5269 |
+
"license": "MIT",
|
| 5270 |
+
"engines": {
|
| 5271 |
+
"node": "^18 || ^20 || >= 21"
|
| 5272 |
+
}
|
| 5273 |
+
},
|
| 5274 |
+
"node_modules/node-gyp-build": {
|
| 5275 |
+
"version": "4.8.4",
|
| 5276 |
+
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
| 5277 |
+
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
| 5278 |
+
"license": "MIT",
|
| 5279 |
+
"bin": {
|
| 5280 |
+
"node-gyp-build": "bin.js",
|
| 5281 |
+
"node-gyp-build-optional": "optional.js",
|
| 5282 |
+
"node-gyp-build-test": "build-test.js"
|
| 5283 |
+
}
|
| 5284 |
+
},
|
| 5285 |
"node_modules/node-releases": {
|
| 5286 |
"version": "2.0.27",
|
| 5287 |
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
|
|
|
| 5289 |
"dev": true,
|
| 5290 |
"license": "MIT"
|
| 5291 |
},
|
| 5292 |
+
"node_modules/object-assign": {
|
| 5293 |
+
"version": "4.1.1",
|
| 5294 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
| 5295 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
| 5296 |
+
"license": "MIT",
|
| 5297 |
+
"engines": {
|
| 5298 |
+
"node": ">=0.10.0"
|
| 5299 |
+
}
|
| 5300 |
+
},
|
| 5301 |
+
"node_modules/object-inspect": {
|
| 5302 |
+
"version": "1.13.4",
|
| 5303 |
+
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
| 5304 |
+
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
| 5305 |
+
"license": "MIT",
|
| 5306 |
+
"engines": {
|
| 5307 |
+
"node": ">= 0.4"
|
| 5308 |
+
},
|
| 5309 |
+
"funding": {
|
| 5310 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 5311 |
+
}
|
| 5312 |
+
},
|
| 5313 |
+
"node_modules/on-finished": {
|
| 5314 |
+
"version": "2.4.1",
|
| 5315 |
+
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
| 5316 |
+
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
| 5317 |
+
"license": "MIT",
|
| 5318 |
+
"dependencies": {
|
| 5319 |
+
"ee-first": "1.1.1"
|
| 5320 |
+
},
|
| 5321 |
+
"engines": {
|
| 5322 |
+
"node": ">= 0.8"
|
| 5323 |
+
}
|
| 5324 |
+
},
|
| 5325 |
+
"node_modules/once": {
|
| 5326 |
+
"version": "1.4.0",
|
| 5327 |
+
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
| 5328 |
+
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
| 5329 |
+
"license": "ISC",
|
| 5330 |
+
"dependencies": {
|
| 5331 |
+
"wrappy": "1"
|
| 5332 |
+
}
|
| 5333 |
+
},
|
| 5334 |
"node_modules/optionator": {
|
| 5335 |
"version": "0.9.4",
|
| 5336 |
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
|
|
|
| 5419 |
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
| 5420 |
"license": "MIT"
|
| 5421 |
},
|
| 5422 |
+
"node_modules/parseurl": {
|
| 5423 |
+
"version": "1.3.3",
|
| 5424 |
+
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
| 5425 |
+
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
| 5426 |
+
"license": "MIT",
|
| 5427 |
+
"engines": {
|
| 5428 |
+
"node": ">= 0.8"
|
| 5429 |
+
}
|
| 5430 |
+
},
|
| 5431 |
"node_modules/path-exists": {
|
| 5432 |
"version": "4.0.0",
|
| 5433 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
|
|
| 5448 |
"node": ">=8"
|
| 5449 |
}
|
| 5450 |
},
|
| 5451 |
+
"node_modules/path-to-regexp": {
|
| 5452 |
+
"version": "8.3.0",
|
| 5453 |
+
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
| 5454 |
+
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
| 5455 |
+
"license": "MIT",
|
| 5456 |
+
"funding": {
|
| 5457 |
+
"type": "opencollective",
|
| 5458 |
+
"url": "https://opencollective.com/express"
|
| 5459 |
+
}
|
| 5460 |
+
},
|
| 5461 |
"node_modules/picocolors": {
|
| 5462 |
"version": "1.1.1",
|
| 5463 |
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
|
|
| 5558 |
"node": ">=12.0.0"
|
| 5559 |
}
|
| 5560 |
},
|
| 5561 |
+
"node_modules/proxy-addr": {
|
| 5562 |
+
"version": "2.0.7",
|
| 5563 |
+
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
| 5564 |
+
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
| 5565 |
+
"license": "MIT",
|
| 5566 |
+
"dependencies": {
|
| 5567 |
+
"forwarded": "0.2.0",
|
| 5568 |
+
"ipaddr.js": "1.9.1"
|
| 5569 |
+
},
|
| 5570 |
+
"engines": {
|
| 5571 |
+
"node": ">= 0.10"
|
| 5572 |
+
}
|
| 5573 |
+
},
|
| 5574 |
"node_modules/proxy-from-env": {
|
| 5575 |
"version": "1.1.0",
|
| 5576 |
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
|
|
|
| 5581 |
"version": "2.3.1",
|
| 5582 |
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
| 5583 |
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
|
|
|
| 5584 |
"license": "MIT",
|
| 5585 |
"engines": {
|
| 5586 |
"node": ">=6"
|
| 5587 |
}
|
| 5588 |
},
|
| 5589 |
+
"node_modules/qs": {
|
| 5590 |
+
"version": "6.14.1",
|
| 5591 |
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
| 5592 |
+
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
| 5593 |
+
"license": "BSD-3-Clause",
|
| 5594 |
+
"dependencies": {
|
| 5595 |
+
"side-channel": "^1.1.0"
|
| 5596 |
+
},
|
| 5597 |
+
"engines": {
|
| 5598 |
+
"node": ">=0.6"
|
| 5599 |
+
},
|
| 5600 |
+
"funding": {
|
| 5601 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 5602 |
+
}
|
| 5603 |
+
},
|
| 5604 |
+
"node_modules/range-parser": {
|
| 5605 |
+
"version": "1.2.1",
|
| 5606 |
+
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
| 5607 |
+
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
| 5608 |
+
"license": "MIT",
|
| 5609 |
+
"engines": {
|
| 5610 |
+
"node": ">= 0.6"
|
| 5611 |
+
}
|
| 5612 |
+
},
|
| 5613 |
+
"node_modules/raw-body": {
|
| 5614 |
+
"version": "3.0.2",
|
| 5615 |
+
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
|
| 5616 |
+
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
|
| 5617 |
+
"license": "MIT",
|
| 5618 |
+
"dependencies": {
|
| 5619 |
+
"bytes": "~3.1.2",
|
| 5620 |
+
"http-errors": "~2.0.1",
|
| 5621 |
+
"iconv-lite": "~0.7.0",
|
| 5622 |
+
"unpipe": "~1.0.0"
|
| 5623 |
+
},
|
| 5624 |
+
"engines": {
|
| 5625 |
+
"node": ">= 0.10"
|
| 5626 |
+
}
|
| 5627 |
+
},
|
| 5628 |
"node_modules/react": {
|
| 5629 |
"version": "19.2.3",
|
| 5630 |
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
|
|
|
| 5697 |
"node": ">=0.10.0"
|
| 5698 |
}
|
| 5699 |
},
|
| 5700 |
+
"node_modules/readable-stream": {
|
| 5701 |
+
"version": "3.6.2",
|
| 5702 |
+
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
| 5703 |
+
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
| 5704 |
+
"license": "MIT",
|
| 5705 |
+
"dependencies": {
|
| 5706 |
+
"inherits": "^2.0.3",
|
| 5707 |
+
"string_decoder": "^1.1.1",
|
| 5708 |
+
"util-deprecate": "^1.0.1"
|
| 5709 |
+
},
|
| 5710 |
+
"engines": {
|
| 5711 |
+
"node": ">= 6"
|
| 5712 |
+
}
|
| 5713 |
+
},
|
| 5714 |
"node_modules/remark-parse": {
|
| 5715 |
"version": "11.0.0",
|
| 5716 |
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
|
|
|
|
| 5805 |
"fsevents": "~2.3.2"
|
| 5806 |
}
|
| 5807 |
},
|
| 5808 |
+
"node_modules/router": {
|
| 5809 |
+
"version": "2.2.0",
|
| 5810 |
+
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
| 5811 |
+
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
| 5812 |
+
"license": "MIT",
|
| 5813 |
+
"dependencies": {
|
| 5814 |
+
"debug": "^4.4.0",
|
| 5815 |
+
"depd": "^2.0.0",
|
| 5816 |
+
"is-promise": "^4.0.0",
|
| 5817 |
+
"parseurl": "^1.3.3",
|
| 5818 |
+
"path-to-regexp": "^8.0.0"
|
| 5819 |
+
},
|
| 5820 |
+
"engines": {
|
| 5821 |
+
"node": ">= 18"
|
| 5822 |
+
}
|
| 5823 |
+
},
|
| 5824 |
"node_modules/safe-buffer": {
|
| 5825 |
"version": "5.2.1",
|
| 5826 |
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
|
|
|
| 5841 |
],
|
| 5842 |
"license": "MIT"
|
| 5843 |
},
|
| 5844 |
+
"node_modules/safer-buffer": {
|
| 5845 |
+
"version": "2.1.2",
|
| 5846 |
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
| 5847 |
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
| 5848 |
+
"license": "MIT"
|
| 5849 |
+
},
|
| 5850 |
"node_modules/scheduler": {
|
| 5851 |
"version": "0.27.0",
|
| 5852 |
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
|
|
|
| 5863 |
"semver": "bin/semver.js"
|
| 5864 |
}
|
| 5865 |
},
|
| 5866 |
+
"node_modules/send": {
|
| 5867 |
+
"version": "1.2.1",
|
| 5868 |
+
"resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
|
| 5869 |
+
"integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
|
| 5870 |
+
"license": "MIT",
|
| 5871 |
+
"dependencies": {
|
| 5872 |
+
"debug": "^4.4.3",
|
| 5873 |
+
"encodeurl": "^2.0.0",
|
| 5874 |
+
"escape-html": "^1.0.3",
|
| 5875 |
+
"etag": "^1.8.1",
|
| 5876 |
+
"fresh": "^2.0.0",
|
| 5877 |
+
"http-errors": "^2.0.1",
|
| 5878 |
+
"mime-types": "^3.0.2",
|
| 5879 |
+
"ms": "^2.1.3",
|
| 5880 |
+
"on-finished": "^2.4.1",
|
| 5881 |
+
"range-parser": "^1.2.1",
|
| 5882 |
+
"statuses": "^2.0.2"
|
| 5883 |
+
},
|
| 5884 |
+
"engines": {
|
| 5885 |
+
"node": ">= 18"
|
| 5886 |
+
},
|
| 5887 |
+
"funding": {
|
| 5888 |
+
"type": "opencollective",
|
| 5889 |
+
"url": "https://opencollective.com/express"
|
| 5890 |
+
}
|
| 5891 |
+
},
|
| 5892 |
+
"node_modules/send/node_modules/mime-db": {
|
| 5893 |
+
"version": "1.54.0",
|
| 5894 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 5895 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 5896 |
+
"license": "MIT",
|
| 5897 |
+
"engines": {
|
| 5898 |
+
"node": ">= 0.6"
|
| 5899 |
+
}
|
| 5900 |
+
},
|
| 5901 |
+
"node_modules/send/node_modules/mime-types": {
|
| 5902 |
+
"version": "3.0.2",
|
| 5903 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
| 5904 |
+
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
| 5905 |
+
"license": "MIT",
|
| 5906 |
+
"dependencies": {
|
| 5907 |
+
"mime-db": "^1.54.0"
|
| 5908 |
+
},
|
| 5909 |
+
"engines": {
|
| 5910 |
+
"node": ">=18"
|
| 5911 |
+
},
|
| 5912 |
+
"funding": {
|
| 5913 |
+
"type": "opencollective",
|
| 5914 |
+
"url": "https://opencollective.com/express"
|
| 5915 |
+
}
|
| 5916 |
+
},
|
| 5917 |
+
"node_modules/serve-static": {
|
| 5918 |
+
"version": "2.2.1",
|
| 5919 |
+
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
|
| 5920 |
+
"integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
|
| 5921 |
+
"license": "MIT",
|
| 5922 |
+
"dependencies": {
|
| 5923 |
+
"encodeurl": "^2.0.0",
|
| 5924 |
+
"escape-html": "^1.0.3",
|
| 5925 |
+
"parseurl": "^1.3.3",
|
| 5926 |
+
"send": "^1.2.0"
|
| 5927 |
+
},
|
| 5928 |
+
"engines": {
|
| 5929 |
+
"node": ">= 18"
|
| 5930 |
+
},
|
| 5931 |
+
"funding": {
|
| 5932 |
+
"type": "opencollective",
|
| 5933 |
+
"url": "https://opencollective.com/express"
|
| 5934 |
+
}
|
| 5935 |
+
},
|
| 5936 |
+
"node_modules/setprototypeof": {
|
| 5937 |
+
"version": "1.2.0",
|
| 5938 |
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
| 5939 |
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
| 5940 |
+
"license": "ISC"
|
| 5941 |
+
},
|
| 5942 |
"node_modules/shebang-command": {
|
| 5943 |
"version": "2.0.0",
|
| 5944 |
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
|
|
|
| 5962 |
"node": ">=8"
|
| 5963 |
}
|
| 5964 |
},
|
| 5965 |
+
"node_modules/side-channel": {
|
| 5966 |
+
"version": "1.1.0",
|
| 5967 |
+
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
| 5968 |
+
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
| 5969 |
+
"license": "MIT",
|
| 5970 |
+
"dependencies": {
|
| 5971 |
+
"es-errors": "^1.3.0",
|
| 5972 |
+
"object-inspect": "^1.13.3",
|
| 5973 |
+
"side-channel-list": "^1.0.0",
|
| 5974 |
+
"side-channel-map": "^1.0.1",
|
| 5975 |
+
"side-channel-weakmap": "^1.0.2"
|
| 5976 |
+
},
|
| 5977 |
+
"engines": {
|
| 5978 |
+
"node": ">= 0.4"
|
| 5979 |
+
},
|
| 5980 |
+
"funding": {
|
| 5981 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 5982 |
+
}
|
| 5983 |
+
},
|
| 5984 |
+
"node_modules/side-channel-list": {
|
| 5985 |
+
"version": "1.0.0",
|
| 5986 |
+
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
| 5987 |
+
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
| 5988 |
+
"license": "MIT",
|
| 5989 |
+
"dependencies": {
|
| 5990 |
+
"es-errors": "^1.3.0",
|
| 5991 |
+
"object-inspect": "^1.13.3"
|
| 5992 |
+
},
|
| 5993 |
+
"engines": {
|
| 5994 |
+
"node": ">= 0.4"
|
| 5995 |
+
},
|
| 5996 |
+
"funding": {
|
| 5997 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 5998 |
+
}
|
| 5999 |
+
},
|
| 6000 |
+
"node_modules/side-channel-map": {
|
| 6001 |
+
"version": "1.0.1",
|
| 6002 |
+
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
| 6003 |
+
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
| 6004 |
+
"license": "MIT",
|
| 6005 |
+
"dependencies": {
|
| 6006 |
+
"call-bound": "^1.0.2",
|
| 6007 |
+
"es-errors": "^1.3.0",
|
| 6008 |
+
"get-intrinsic": "^1.2.5",
|
| 6009 |
+
"object-inspect": "^1.13.3"
|
| 6010 |
+
},
|
| 6011 |
+
"engines": {
|
| 6012 |
+
"node": ">= 0.4"
|
| 6013 |
+
},
|
| 6014 |
+
"funding": {
|
| 6015 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 6016 |
+
}
|
| 6017 |
+
},
|
| 6018 |
+
"node_modules/side-channel-weakmap": {
|
| 6019 |
+
"version": "1.0.2",
|
| 6020 |
+
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
| 6021 |
+
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
| 6022 |
+
"license": "MIT",
|
| 6023 |
+
"dependencies": {
|
| 6024 |
+
"call-bound": "^1.0.2",
|
| 6025 |
+
"es-errors": "^1.3.0",
|
| 6026 |
+
"get-intrinsic": "^1.2.5",
|
| 6027 |
+
"object-inspect": "^1.13.3",
|
| 6028 |
+
"side-channel-map": "^1.0.1"
|
| 6029 |
+
},
|
| 6030 |
+
"engines": {
|
| 6031 |
+
"node": ">= 0.4"
|
| 6032 |
+
},
|
| 6033 |
+
"funding": {
|
| 6034 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 6035 |
+
}
|
| 6036 |
+
},
|
| 6037 |
+
"node_modules/sift": {
|
| 6038 |
+
"version": "17.1.3",
|
| 6039 |
+
"resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
|
| 6040 |
+
"integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
|
| 6041 |
+
"license": "MIT"
|
| 6042 |
+
},
|
| 6043 |
+
"node_modules/socket.io": {
|
| 6044 |
+
"version": "4.8.3",
|
| 6045 |
+
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz",
|
| 6046 |
+
"integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==",
|
| 6047 |
+
"license": "MIT",
|
| 6048 |
+
"dependencies": {
|
| 6049 |
+
"accepts": "~1.3.4",
|
| 6050 |
+
"base64id": "~2.0.0",
|
| 6051 |
+
"cors": "~2.8.5",
|
| 6052 |
+
"debug": "~4.4.1",
|
| 6053 |
+
"engine.io": "~6.6.0",
|
| 6054 |
+
"socket.io-adapter": "~2.5.2",
|
| 6055 |
+
"socket.io-parser": "~4.2.4"
|
| 6056 |
+
},
|
| 6057 |
+
"engines": {
|
| 6058 |
+
"node": ">=10.2.0"
|
| 6059 |
+
}
|
| 6060 |
+
},
|
| 6061 |
+
"node_modules/socket.io-adapter": {
|
| 6062 |
+
"version": "2.5.6",
|
| 6063 |
+
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz",
|
| 6064 |
+
"integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==",
|
| 6065 |
+
"license": "MIT",
|
| 6066 |
+
"dependencies": {
|
| 6067 |
+
"debug": "~4.4.1",
|
| 6068 |
+
"ws": "~8.18.3"
|
| 6069 |
+
}
|
| 6070 |
+
},
|
| 6071 |
+
"node_modules/socket.io-client": {
|
| 6072 |
+
"version": "4.8.3",
|
| 6073 |
+
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
|
| 6074 |
+
"integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
|
| 6075 |
+
"license": "MIT",
|
| 6076 |
+
"dependencies": {
|
| 6077 |
+
"@socket.io/component-emitter": "~3.1.0",
|
| 6078 |
+
"debug": "~4.4.1",
|
| 6079 |
+
"engine.io-client": "~6.6.1",
|
| 6080 |
+
"socket.io-parser": "~4.2.4"
|
| 6081 |
+
},
|
| 6082 |
+
"engines": {
|
| 6083 |
+
"node": ">=10.0.0"
|
| 6084 |
+
}
|
| 6085 |
+
},
|
| 6086 |
+
"node_modules/socket.io-parser": {
|
| 6087 |
+
"version": "4.2.5",
|
| 6088 |
+
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz",
|
| 6089 |
+
"integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==",
|
| 6090 |
+
"license": "MIT",
|
| 6091 |
+
"dependencies": {
|
| 6092 |
+
"@socket.io/component-emitter": "~3.1.0",
|
| 6093 |
+
"debug": "~4.4.1"
|
| 6094 |
+
},
|
| 6095 |
+
"engines": {
|
| 6096 |
+
"node": ">=10.0.0"
|
| 6097 |
+
}
|
| 6098 |
+
},
|
| 6099 |
+
"node_modules/socket.io/node_modules/accepts": {
|
| 6100 |
+
"version": "1.3.8",
|
| 6101 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
| 6102 |
+
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
| 6103 |
+
"license": "MIT",
|
| 6104 |
+
"dependencies": {
|
| 6105 |
+
"mime-types": "~2.1.34",
|
| 6106 |
+
"negotiator": "0.6.3"
|
| 6107 |
+
},
|
| 6108 |
+
"engines": {
|
| 6109 |
+
"node": ">= 0.6"
|
| 6110 |
+
}
|
| 6111 |
+
},
|
| 6112 |
+
"node_modules/socket.io/node_modules/negotiator": {
|
| 6113 |
+
"version": "0.6.3",
|
| 6114 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
| 6115 |
+
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
| 6116 |
+
"license": "MIT",
|
| 6117 |
+
"engines": {
|
| 6118 |
+
"node": ">= 0.6"
|
| 6119 |
+
}
|
| 6120 |
+
},
|
| 6121 |
"node_modules/source-map-js": {
|
| 6122 |
"version": "1.2.1",
|
| 6123 |
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
|
|
|
| 6138 |
"url": "https://github.com/sponsors/wooorm"
|
| 6139 |
}
|
| 6140 |
},
|
| 6141 |
+
"node_modules/sparse-bitfield": {
|
| 6142 |
+
"version": "3.0.3",
|
| 6143 |
+
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
| 6144 |
+
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
|
| 6145 |
+
"license": "MIT",
|
| 6146 |
+
"dependencies": {
|
| 6147 |
+
"memory-pager": "^1.0.2"
|
| 6148 |
+
}
|
| 6149 |
+
},
|
| 6150 |
+
"node_modules/statuses": {
|
| 6151 |
+
"version": "2.0.2",
|
| 6152 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
| 6153 |
+
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
| 6154 |
+
"license": "MIT",
|
| 6155 |
+
"engines": {
|
| 6156 |
+
"node": ">= 0.8"
|
| 6157 |
+
}
|
| 6158 |
+
},
|
| 6159 |
+
"node_modules/streamsearch": {
|
| 6160 |
+
"version": "1.1.0",
|
| 6161 |
+
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
| 6162 |
+
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
| 6163 |
+
"engines": {
|
| 6164 |
+
"node": ">=10.0.0"
|
| 6165 |
+
}
|
| 6166 |
+
},
|
| 6167 |
+
"node_modules/string_decoder": {
|
| 6168 |
+
"version": "1.3.0",
|
| 6169 |
+
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
| 6170 |
+
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
| 6171 |
+
"license": "MIT",
|
| 6172 |
+
"dependencies": {
|
| 6173 |
+
"safe-buffer": "~5.2.0"
|
| 6174 |
+
}
|
| 6175 |
+
},
|
| 6176 |
"node_modules/string-width": {
|
| 6177 |
"version": "4.2.3",
|
| 6178 |
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
|
|
| 6281 |
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 6282 |
}
|
| 6283 |
},
|
| 6284 |
+
"node_modules/toidentifier": {
|
| 6285 |
+
"version": "1.0.1",
|
| 6286 |
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
| 6287 |
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
| 6288 |
+
"license": "MIT",
|
| 6289 |
+
"engines": {
|
| 6290 |
+
"node": ">=0.6"
|
| 6291 |
+
}
|
| 6292 |
+
},
|
| 6293 |
+
"node_modules/tr46": {
|
| 6294 |
+
"version": "5.1.1",
|
| 6295 |
+
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
| 6296 |
+
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
| 6297 |
+
"license": "MIT",
|
| 6298 |
+
"dependencies": {
|
| 6299 |
+
"punycode": "^2.3.1"
|
| 6300 |
+
},
|
| 6301 |
+
"engines": {
|
| 6302 |
+
"node": ">=18"
|
| 6303 |
+
}
|
| 6304 |
+
},
|
| 6305 |
"node_modules/trim-lines": {
|
| 6306 |
"version": "3.0.1",
|
| 6307 |
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
|
|
|
| 6341 |
"node": ">= 0.8.0"
|
| 6342 |
}
|
| 6343 |
},
|
| 6344 |
+
"node_modules/type-is": {
|
| 6345 |
+
"version": "2.0.1",
|
| 6346 |
+
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
| 6347 |
+
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
| 6348 |
+
"license": "MIT",
|
| 6349 |
+
"dependencies": {
|
| 6350 |
+
"content-type": "^1.0.5",
|
| 6351 |
+
"media-typer": "^1.1.0",
|
| 6352 |
+
"mime-types": "^3.0.0"
|
| 6353 |
+
},
|
| 6354 |
+
"engines": {
|
| 6355 |
+
"node": ">= 0.6"
|
| 6356 |
+
}
|
| 6357 |
+
},
|
| 6358 |
+
"node_modules/type-is/node_modules/mime-db": {
|
| 6359 |
+
"version": "1.54.0",
|
| 6360 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 6361 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 6362 |
+
"license": "MIT",
|
| 6363 |
+
"engines": {
|
| 6364 |
+
"node": ">= 0.6"
|
| 6365 |
+
}
|
| 6366 |
+
},
|
| 6367 |
+
"node_modules/type-is/node_modules/mime-types": {
|
| 6368 |
+
"version": "3.0.2",
|
| 6369 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
| 6370 |
+
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
| 6371 |
+
"license": "MIT",
|
| 6372 |
+
"dependencies": {
|
| 6373 |
+
"mime-db": "^1.54.0"
|
| 6374 |
+
},
|
| 6375 |
+
"engines": {
|
| 6376 |
+
"node": ">=18"
|
| 6377 |
+
},
|
| 6378 |
+
"funding": {
|
| 6379 |
+
"type": "opencollective",
|
| 6380 |
+
"url": "https://opencollective.com/express"
|
| 6381 |
+
}
|
| 6382 |
+
},
|
| 6383 |
+
"node_modules/typedarray": {
|
| 6384 |
+
"version": "0.0.6",
|
| 6385 |
+
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
| 6386 |
+
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
| 6387 |
+
"license": "MIT"
|
| 6388 |
+
},
|
| 6389 |
"node_modules/undici-types": {
|
| 6390 |
"version": "7.16.0",
|
| 6391 |
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
|
|
|
| 6479 |
"url": "https://opencollective.com/unified"
|
| 6480 |
}
|
| 6481 |
},
|
| 6482 |
+
"node_modules/unpipe": {
|
| 6483 |
+
"version": "1.0.0",
|
| 6484 |
+
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
| 6485 |
+
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
| 6486 |
+
"license": "MIT",
|
| 6487 |
+
"engines": {
|
| 6488 |
+
"node": ">= 0.8"
|
| 6489 |
+
}
|
| 6490 |
+
},
|
| 6491 |
"node_modules/update-browserslist-db": {
|
| 6492 |
"version": "1.2.3",
|
| 6493 |
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
|
|
|
| 6529 |
"punycode": "^2.1.0"
|
| 6530 |
}
|
| 6531 |
},
|
| 6532 |
+
"node_modules/util-deprecate": {
|
| 6533 |
+
"version": "1.0.2",
|
| 6534 |
+
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
| 6535 |
+
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
| 6536 |
+
"license": "MIT"
|
| 6537 |
+
},
|
| 6538 |
+
"node_modules/vary": {
|
| 6539 |
+
"version": "1.1.2",
|
| 6540 |
+
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
| 6541 |
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
| 6542 |
+
"license": "MIT",
|
| 6543 |
+
"engines": {
|
| 6544 |
+
"node": ">= 0.8"
|
| 6545 |
+
}
|
| 6546 |
+
},
|
| 6547 |
"node_modules/vfile": {
|
| 6548 |
"version": "6.0.3",
|
| 6549 |
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
|
|
|
| 6653 |
"integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==",
|
| 6654 |
"license": "Apache-2.0"
|
| 6655 |
},
|
| 6656 |
+
"node_modules/webidl-conversions": {
|
| 6657 |
+
"version": "7.0.0",
|
| 6658 |
+
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
| 6659 |
+
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
| 6660 |
+
"license": "BSD-2-Clause",
|
| 6661 |
+
"engines": {
|
| 6662 |
+
"node": ">=12"
|
| 6663 |
+
}
|
| 6664 |
+
},
|
| 6665 |
"node_modules/websocket-driver": {
|
| 6666 |
"version": "0.7.4",
|
| 6667 |
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
|
|
|
|
| 6685 |
"node": ">=0.8.0"
|
| 6686 |
}
|
| 6687 |
},
|
| 6688 |
+
"node_modules/whatwg-url": {
|
| 6689 |
+
"version": "14.2.0",
|
| 6690 |
+
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
| 6691 |
+
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
| 6692 |
+
"license": "MIT",
|
| 6693 |
+
"dependencies": {
|
| 6694 |
+
"tr46": "^5.1.0",
|
| 6695 |
+
"webidl-conversions": "^7.0.0"
|
| 6696 |
+
},
|
| 6697 |
+
"engines": {
|
| 6698 |
+
"node": ">=18"
|
| 6699 |
+
}
|
| 6700 |
+
},
|
| 6701 |
"node_modules/which": {
|
| 6702 |
"version": "2.0.2",
|
| 6703 |
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
|
|
|
| 6741 |
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
| 6742 |
}
|
| 6743 |
},
|
| 6744 |
+
"node_modules/wrappy": {
|
| 6745 |
+
"version": "1.0.2",
|
| 6746 |
+
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
| 6747 |
+
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
| 6748 |
+
"license": "ISC"
|
| 6749 |
+
},
|
| 6750 |
+
"node_modules/ws": {
|
| 6751 |
+
"version": "8.18.3",
|
| 6752 |
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
| 6753 |
+
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
| 6754 |
+
"license": "MIT",
|
| 6755 |
+
"engines": {
|
| 6756 |
+
"node": ">=10.0.0"
|
| 6757 |
+
},
|
| 6758 |
+
"peerDependencies": {
|
| 6759 |
+
"bufferutil": "^4.0.1",
|
| 6760 |
+
"utf-8-validate": ">=5.0.2"
|
| 6761 |
+
},
|
| 6762 |
+
"peerDependenciesMeta": {
|
| 6763 |
+
"bufferutil": {
|
| 6764 |
+
"optional": true
|
| 6765 |
+
},
|
| 6766 |
+
"utf-8-validate": {
|
| 6767 |
+
"optional": true
|
| 6768 |
+
}
|
| 6769 |
+
}
|
| 6770 |
+
},
|
| 6771 |
+
"node_modules/xmlhttprequest-ssl": {
|
| 6772 |
+
"version": "2.1.2",
|
| 6773 |
+
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
| 6774 |
+
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
| 6775 |
+
"engines": {
|
| 6776 |
+
"node": ">=0.4.0"
|
| 6777 |
+
}
|
| 6778 |
+
},
|
| 6779 |
+
"node_modules/xtend": {
|
| 6780 |
+
"version": "4.0.2",
|
| 6781 |
+
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
| 6782 |
+
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
| 6783 |
+
"license": "MIT",
|
| 6784 |
+
"engines": {
|
| 6785 |
+
"node": ">=0.4"
|
| 6786 |
+
}
|
| 6787 |
+
},
|
| 6788 |
"node_modules/y18n": {
|
| 6789 |
"version": "5.0.8",
|
| 6790 |
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
kibali-ui/package.json
CHANGED
|
@@ -11,13 +11,22 @@
|
|
| 11 |
},
|
| 12 |
"dependencies": {
|
| 13 |
"axios": "^1.13.2",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
"firebase": "^12.7.0",
|
|
|
|
| 15 |
"leaflet": "^1.9.4",
|
| 16 |
"lucide-react": "^0.562.0",
|
|
|
|
|
|
|
| 17 |
"react": "^19.2.0",
|
| 18 |
"react-dom": "^19.2.0",
|
| 19 |
"react-leaflet": "^5.0.0",
|
| 20 |
-
"react-markdown": "^10.1.0"
|
|
|
|
|
|
|
| 21 |
},
|
| 22 |
"devDependencies": {
|
| 23 |
"@eslint/js": "^9.39.1",
|
|
|
|
| 11 |
},
|
| 12 |
"dependencies": {
|
| 13 |
"axios": "^1.13.2",
|
| 14 |
+
"bcrypt": "^6.0.0",
|
| 15 |
+
"cors": "^2.8.5",
|
| 16 |
+
"dotenv": "^17.2.3",
|
| 17 |
+
"express": "^5.2.1",
|
| 18 |
"firebase": "^12.7.0",
|
| 19 |
+
"jsonwebtoken": "^9.0.3",
|
| 20 |
"leaflet": "^1.9.4",
|
| 21 |
"lucide-react": "^0.562.0",
|
| 22 |
+
"mongoose": "^9.1.0",
|
| 23 |
+
"multer": "^2.0.2",
|
| 24 |
"react": "^19.2.0",
|
| 25 |
"react-dom": "^19.2.0",
|
| 26 |
"react-leaflet": "^5.0.0",
|
| 27 |
+
"react-markdown": "^10.1.0",
|
| 28 |
+
"socket.io": "^4.8.3",
|
| 29 |
+
"socket.io-client": "^4.8.3"
|
| 30 |
},
|
| 31 |
"devDependencies": {
|
| 32 |
"@eslint/js": "^9.39.1",
|
kibali-ui/src/App.jsx
CHANGED
|
@@ -2,12 +2,11 @@ import React, { useState, useEffect, useRef } from 'react';
|
|
| 2 |
import axios from 'axios';
|
| 3 |
import {
|
| 4 |
Send, FileText, Upload, Globe, MapPin,
|
| 5 |
-
Loader2, Trash2, Image as ImageIcon, Search, Brain, ChevronDown, ChevronUp, User,
|
|
|
|
| 6 |
} from 'lucide-react';
|
| 7 |
-
import { auth, googleProvider } from './firebase'; // Import Firebase
|
| 8 |
-
import { signInWithPopup, signOut, onAuthStateChanged } from "firebase/auth";
|
| 9 |
|
| 10 |
-
const API_BASE = "http://localhost:8000";
|
| 11 |
const LOGO_PATH = "/kibali_logo.svg";
|
| 12 |
|
| 13 |
function App() {
|
|
@@ -15,129 +14,223 @@ function App() {
|
|
| 15 |
const [input, setInput] = useState("");
|
| 16 |
const [loading, setLoading] = useState(false);
|
| 17 |
const [uploading, setUploading] = useState(false);
|
| 18 |
-
const [status, setStatus] = useState(
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
const scrollRef = useRef(null);
|
|
|
|
| 22 |
|
| 23 |
-
//
|
| 24 |
useEffect(() => {
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
googleId: firebaseUser.uid,
|
| 32 |
-
name: firebaseUser.displayName,
|
| 33 |
-
email: firebaseUser.email,
|
| 34 |
-
photoUrl: firebaseUser.photoURL
|
| 35 |
-
});
|
| 36 |
-
|
| 37 |
-
const user = res.data.user;
|
| 38 |
-
setCurrentUser(user);
|
| 39 |
-
localStorage.setItem("user", JSON.stringify(user)); // backup local
|
| 40 |
-
} catch (err) {
|
| 41 |
-
console.error("Erreur synchronisation avec backend:", err);
|
| 42 |
-
// Option : déconnexion si échec critique
|
| 43 |
-
}
|
| 44 |
-
} else {
|
| 45 |
-
setCurrentUser(null);
|
| 46 |
-
localStorage.removeItem("user");
|
| 47 |
}
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
return () => unsubscribe();
|
| 51 |
}, []);
|
| 52 |
|
| 53 |
-
|
| 54 |
-
useEffect(() => {
|
| 55 |
-
const saved = localStorage.getItem("user");
|
| 56 |
-
if (saved && !currentUser) {
|
| 57 |
-
setCurrentUser(JSON.parse(saved));
|
| 58 |
-
}
|
| 59 |
-
}, []);
|
| 60 |
-
|
| 61 |
-
useEffect(() => {
|
| 62 |
-
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
| 63 |
-
}, [messages, loading]);
|
| 64 |
-
|
| 65 |
-
const handleGoogleLogin = async () => {
|
| 66 |
try {
|
| 67 |
-
await
|
| 68 |
-
|
| 69 |
} catch (error) {
|
| 70 |
-
|
| 71 |
}
|
| 72 |
};
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
// Firebase effacera l'état → onAuthStateChanged mettra currentUser à null
|
| 79 |
-
} catch (error) {
|
| 80 |
-
alert("Erreur déconnexion");
|
| 81 |
-
}
|
| 82 |
-
};
|
| 83 |
|
|
|
|
| 84 |
const handleSend = async () => {
|
| 85 |
-
if (!currentUser) {
|
| 86 |
-
alert("Veuillez vous connecter avec Google pour discuter.");
|
| 87 |
-
return;
|
| 88 |
-
}
|
| 89 |
if (!input.trim() || loading) return;
|
| 90 |
|
| 91 |
-
const userMsg = { role: "user", content: input };
|
| 92 |
setMessages(prev => [...prev, userMsg]);
|
| 93 |
setInput("");
|
| 94 |
setLoading(true);
|
| 95 |
|
| 96 |
try {
|
| 97 |
-
// À adapter selon ton vrai endpoint chat
|
| 98 |
const response = await axios.post(`${API_BASE}/chat`, {
|
| 99 |
messages: [...messages, userMsg],
|
| 100 |
-
latitude:
|
| 101 |
-
longitude:
|
| 102 |
city: "Libreville",
|
| 103 |
thinking_mode: true
|
|
|
|
|
|
|
| 104 |
});
|
| 105 |
|
| 106 |
-
const aiResponse = response.data.response || "Réponse
|
| 107 |
const aiImages = response.data.images || [];
|
|
|
|
| 108 |
|
| 109 |
setMessages(prev => [...prev, {
|
| 110 |
role: "assistant",
|
| 111 |
content: aiResponse,
|
| 112 |
images: aiImages,
|
| 113 |
-
|
|
|
|
| 114 |
}]);
|
|
|
|
|
|
|
|
|
|
| 115 |
} catch (error) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
setMessages(prev => [...prev, {
|
| 117 |
role: "assistant",
|
| 118 |
-
content:
|
|
|
|
|
|
|
| 119 |
}]);
|
| 120 |
} finally {
|
| 121 |
setLoading(false);
|
| 122 |
}
|
| 123 |
};
|
| 124 |
|
|
|
|
| 125 |
const handleFileUpload = async (e) => {
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
};
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
const styles = {
|
| 130 |
container: { display: 'flex', height: '100vh', backgroundColor: '#020617', color: '#f8fafc', fontFamily: 'system-ui, -apple-system, sans-serif' },
|
| 131 |
-
sidebar: { width: '
|
| 132 |
main: { flex: 1, display: 'flex', flexDirection: 'column', position: 'relative', background: 'radial-gradient(circle at 50% 0%, #1e293b 0%, #020617 100%)' },
|
| 133 |
messageArea: { flex: 1, overflowY: 'auto', padding: '2rem 10% 2rem 10%' },
|
| 134 |
userBubble: { backgroundColor: '#059669', color: 'white', padding: '1rem 1.25rem', borderRadius: '1.25rem 1.25rem 0.25rem 1.25rem', boxShadow: '0 4px 15px rgba(5, 150, 105, 0.2)' },
|
| 135 |
aiBubble: { backgroundColor: '#1e293b', border: '1px solid #334155', color: '#f1f5f9', padding: '1rem 1.25rem', borderRadius: '1.25rem 1.25rem 1.25rem 0.25rem', boxShadow: '0 10px 30px rgba(0,0,0,0.5)' },
|
|
|
|
|
|
|
| 136 |
inputContainer: { padding: '2rem', background: 'linear-gradient(to top, #020617 70%, transparent)' },
|
| 137 |
inputWrapper: { display: 'flex', alignItems: 'center', backgroundColor: 'rgba(30, 41, 59, 0.7)', backdropFilter: 'blur(20px)', border: '1px solid #334155', borderRadius: '1.5rem', padding: '0.6rem 1.2rem', maxWidth: '900px', margin: '0 auto' },
|
| 138 |
inputField: { flex: 1, background: 'transparent', border: 'none', color: 'white', padding: '0.8rem', outline: 'none', fontSize: '1rem' },
|
| 139 |
sendBtn: { backgroundColor: '#10b981', color: 'white', border: 'none', borderRadius: '1rem', width: '45px', height: '45px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', transition: 'all 0.2s' },
|
| 140 |
-
toolBadge: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '11px', backgroundColor: '#020617', padding: '6px 12px', borderRadius: '10px', border: '1px solid #1e293b', color: '#94a3b8' }
|
|
|
|
| 141 |
};
|
| 142 |
|
| 143 |
return (
|
|
@@ -146,141 +239,322 @@ function App() {
|
|
| 146 |
<aside style={styles.sidebar}>
|
| 147 |
<div style={{ padding: '2rem', borderBottom: '1px solid #1e293b', display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
| 148 |
<div style={{ width: '45px', height: '45px', backgroundColor: '#1e293b', borderRadius: '12px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1px solid #334155' }}>
|
| 149 |
-
<img src={LOGO_PATH} alt="K" style={{ width: '30px', height: '30px', objectFit: 'contain' }} onError={(e) => e.target.style.display='none'} />
|
| 150 |
</div>
|
| 151 |
<div>
|
| 152 |
-
<h1 style={{ fontSize: '1.3rem', fontWeight: '900', margin: 0, letterSpacing: '-0.5px' }}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
</div>
|
| 154 |
</div>
|
| 155 |
|
| 156 |
-
<div style={{ padding: '2rem', flex: 1, display: 'flex', flexDirection: 'column', gap: '
|
| 157 |
-
{/*
|
| 158 |
-
|
| 159 |
-
<
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
</div>
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
| 193 |
</div>
|
| 194 |
-
<button onClick={handleLogout} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#ef4444' }}>
|
| 195 |
-
<LogOut size={18} />
|
| 196 |
-
</button>
|
| 197 |
</div>
|
| 198 |
-
)}
|
| 199 |
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
|
|
|
|
|
|
| 216 |
</div>
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
</div>
|
| 223 |
</div>
|
| 224 |
</div>
|
| 225 |
|
| 226 |
-
<button
|
| 227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
</button>
|
| 229 |
</aside>
|
| 230 |
|
| 231 |
-
{/* MAIN CONTENT
|
| 232 |
<main style={styles.main}>
|
| 233 |
<div style={styles.messageArea}>
|
| 234 |
-
{messages.length === 0 &&
|
| 235 |
<div style={{ height: '80%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }}>
|
| 236 |
-
<div style={{
|
| 237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
</div>
|
| 239 |
-
<h2 style={{ fontSize: '2.5rem', fontWeight: '900', marginBottom: '0.8rem', letterSpacing: '-1px' }}>
|
|
|
|
|
|
|
| 240 |
<p style={{ color: '#64748b', maxWidth: '450px', lineHeight: '1.7', fontSize: '1.1rem' }}>
|
| 241 |
-
|
| 242 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
</div>
|
| 244 |
)}
|
| 245 |
|
| 246 |
{messages.map((m, i) => (
|
| 247 |
<div key={i} style={{ display: 'flex', justifyContent: m.role === 'user' ? 'flex-end' : 'flex-start', marginBottom: '3rem' }}>
|
| 248 |
<div style={{ display: 'flex', gap: '1.2rem', maxWidth: '85%', flexDirection: m.role === 'user' ? 'row-reverse' : 'row' }}>
|
| 249 |
-
<div style={{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
{m.role === 'user' ? (
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
) : (
|
| 257 |
-
<img src={LOGO_PATH} style={{ width: '28px' }} alt="K" />
|
| 258 |
)}
|
| 259 |
</div>
|
| 260 |
|
| 261 |
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.8rem' }}>
|
| 262 |
-
{
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
</button>
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
<div style={
|
| 271 |
-
|
| 272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
)}
|
| 274 |
</div>
|
| 275 |
)}
|
| 276 |
-
|
| 277 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
</div>
|
|
|
|
|
|
|
| 279 |
{m.images && m.images.length > 0 && (
|
| 280 |
-
<div style={{ display: 'flex', gap: '12px', marginTop: '
|
| 281 |
{m.images.map((img, idx) => (
|
| 282 |
-
<div key={idx} style={{ flexShrink: 0, position: 'relative', borderRadius: '1.2rem', overflow: 'hidden', border: '2px solid #334155' }}>
|
| 283 |
-
<img
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
</div>
|
| 285 |
))}
|
| 286 |
</div>
|
|
@@ -292,12 +566,29 @@ function App() {
|
|
| 292 |
|
| 293 |
{loading && (
|
| 294 |
<div style={{ display: 'flex', gap: '1.2rem', alignItems: 'center' }}>
|
| 295 |
-
<div style={{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
<Loader2 className="animate-spin" color="#10b981" size={24} />
|
| 297 |
</div>
|
| 298 |
-
<div style={{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
</div>
|
| 300 |
)}
|
|
|
|
| 301 |
<div ref={scrollRef} />
|
| 302 |
</div>
|
| 303 |
|
|
@@ -306,23 +597,61 @@ function App() {
|
|
| 306 |
<Search size={22} color="#475569" style={{ marginRight: '10px' }} />
|
| 307 |
<input
|
| 308 |
style={styles.inputField}
|
| 309 |
-
placeholder=
|
| 310 |
value={input}
|
| 311 |
onChange={(e) => setInput(e.target.value)}
|
| 312 |
-
onKeyDown={(e) =>
|
| 313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
/>
|
| 315 |
<button
|
| 316 |
-
style={{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
onClick={handleSend}
|
| 318 |
-
disabled={
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
>
|
| 320 |
-
<Send size={20} />
|
| 321 |
</button>
|
| 322 |
</div>
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
</div>
|
| 327 |
</main>
|
| 328 |
</div>
|
|
|
|
| 2 |
import axios from 'axios';
|
| 3 |
import {
|
| 4 |
Send, FileText, Upload, Globe, MapPin,
|
| 5 |
+
Loader2, Trash2, Image as ImageIcon, Search, Brain, ChevronDown, ChevronUp, User,
|
| 6 |
+
Database, Clock, TrendingUp, AlertCircle, CheckCircle, Zap
|
| 7 |
} from 'lucide-react';
|
|
|
|
|
|
|
| 8 |
|
| 9 |
+
const API_BASE = "http://localhost:8000";
|
| 10 |
const LOGO_PATH = "/kibali_logo.svg";
|
| 11 |
|
| 12 |
function App() {
|
|
|
|
| 14 |
const [input, setInput] = useState("");
|
| 15 |
const [loading, setLoading] = useState(false);
|
| 16 |
const [uploading, setUploading] = useState(false);
|
| 17 |
+
const [status, setStatus] = useState({
|
| 18 |
+
doc_chunks: 0,
|
| 19 |
+
memory_entries: 0,
|
| 20 |
+
current_subject: null,
|
| 21 |
+
subject_message_count: 0
|
| 22 |
+
});
|
| 23 |
+
const [showThinking, setShowThinking] = useState({});
|
| 24 |
+
const [uploadProgress, setUploadProgress] = useState(null);
|
| 25 |
const scrollRef = useRef(null);
|
| 26 |
+
const pollingInterval = useRef(null);
|
| 27 |
|
| 28 |
+
// Récupération du status backend au démarrage et périodiquement
|
| 29 |
useEffect(() => {
|
| 30 |
+
fetchStatus();
|
| 31 |
+
pollingInterval.current = setInterval(fetchStatus, 10000); // Toutes les 10 secondes
|
| 32 |
+
|
| 33 |
+
return () => {
|
| 34 |
+
if (pollingInterval.current) {
|
| 35 |
+
clearInterval(pollingInterval.current);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
}
|
| 37 |
+
};
|
|
|
|
|
|
|
| 38 |
}, []);
|
| 39 |
|
| 40 |
+
const fetchStatus = async () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
try {
|
| 42 |
+
const response = await axios.get(`${API_BASE}/status`, { timeout: 5000 });
|
| 43 |
+
setStatus(response.data);
|
| 44 |
} catch (error) {
|
| 45 |
+
console.error("Erreur récupération status:", error);
|
| 46 |
}
|
| 47 |
};
|
| 48 |
|
| 49 |
+
// Auto-scroll vers le bas
|
| 50 |
+
useEffect(() => {
|
| 51 |
+
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
| 52 |
+
}, [messages, loading]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
+
// Envoi du message
|
| 55 |
const handleSend = async () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
if (!input.trim() || loading) return;
|
| 57 |
|
| 58 |
+
const userMsg = { role: "user", content: input.trim() };
|
| 59 |
setMessages(prev => [...prev, userMsg]);
|
| 60 |
setInput("");
|
| 61 |
setLoading(true);
|
| 62 |
|
| 63 |
try {
|
|
|
|
| 64 |
const response = await axios.post(`${API_BASE}/chat`, {
|
| 65 |
messages: [...messages, userMsg],
|
| 66 |
+
latitude: 0.4061,
|
| 67 |
+
longitude: 9.4673,
|
| 68 |
city: "Libreville",
|
| 69 |
thinking_mode: true
|
| 70 |
+
}, {
|
| 71 |
+
timeout: 120000 // 120 secondes pour les requêtes complexes
|
| 72 |
});
|
| 73 |
|
| 74 |
+
const aiResponse = response.data.response || "Réponse reçue du serveur.";
|
| 75 |
const aiImages = response.data.images || [];
|
| 76 |
+
const contextInfo = response.data.context_info || {};
|
| 77 |
|
| 78 |
setMessages(prev => [...prev, {
|
| 79 |
role: "assistant",
|
| 80 |
content: aiResponse,
|
| 81 |
images: aiImages,
|
| 82 |
+
context_info: contextInfo,
|
| 83 |
+
timestamp: new Date().toISOString()
|
| 84 |
}]);
|
| 85 |
+
|
| 86 |
+
// Mise à jour du status après la réponse
|
| 87 |
+
fetchStatus();
|
| 88 |
} catch (error) {
|
| 89 |
+
console.error("Erreur lors de l'appel au backend:", error);
|
| 90 |
+
let errorMsg = "Erreur : impossible de contacter le serveur IA.";
|
| 91 |
+
|
| 92 |
+
if (error.code === 'ERR_NETWORK') {
|
| 93 |
+
errorMsg = "⚠️ Serveur injoignable. Vérifiez que votre backend est lancé sur http://localhost:8000";
|
| 94 |
+
} else if (error.code === 'ECONNABORTED') {
|
| 95 |
+
errorMsg = "⏱️ Timeout : la requête a pris trop de temps. Le modèle est peut-être surchargé.";
|
| 96 |
+
} else if (error.response?.status === 400) {
|
| 97 |
+
errorMsg = `❌ Erreur de requête : ${error.response.data.detail || 'Format invalide'}`;
|
| 98 |
+
} else if (error.response?.status === 500) {
|
| 99 |
+
errorMsg = "🔥 Erreur serveur interne. Vérifiez les logs du backend.";
|
| 100 |
+
} else if (error.response?.data?.detail) {
|
| 101 |
+
errorMsg = `Erreur serveur : ${error.response.data.detail}`;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
setMessages(prev => [...prev, {
|
| 105 |
role: "assistant",
|
| 106 |
+
content: errorMsg,
|
| 107 |
+
error: true,
|
| 108 |
+
timestamp: new Date().toISOString()
|
| 109 |
}]);
|
| 110 |
} finally {
|
| 111 |
setLoading(false);
|
| 112 |
}
|
| 113 |
};
|
| 114 |
|
| 115 |
+
// Upload de fichiers PDF avec feedback de progression
|
| 116 |
const handleFileUpload = async (e) => {
|
| 117 |
+
const files = Array.from(e.target.files);
|
| 118 |
+
if (!files.length) return;
|
| 119 |
+
|
| 120 |
+
setUploading(true);
|
| 121 |
+
setUploadProgress({ current: 0, total: files.length });
|
| 122 |
+
|
| 123 |
+
const formData = new FormData();
|
| 124 |
+
files.forEach(file => formData.append('files', file));
|
| 125 |
+
|
| 126 |
+
try {
|
| 127 |
+
const res = await axios.post(`${API_BASE}/upload`, formData, {
|
| 128 |
+
headers: { 'Content-Type': 'multipart/form-data' },
|
| 129 |
+
timeout: 180000, // 3 minutes pour les gros PDFs
|
| 130 |
+
onUploadProgress: (progressEvent) => {
|
| 131 |
+
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
| 132 |
+
setUploadProgress(prev => ({ ...prev, percent: percentCompleted }));
|
| 133 |
+
}
|
| 134 |
+
});
|
| 135 |
+
|
| 136 |
+
const chunksAdded = res.data.chunks_added || 0;
|
| 137 |
+
const filesProcessed = res.data.files_processed || 0;
|
| 138 |
+
const totalChunks = res.data.total_doc_chunks || 0;
|
| 139 |
+
|
| 140 |
+
// Mise à jour du status immédiate
|
| 141 |
+
setStatus(prev => ({ ...prev, doc_chunks: totalChunks }));
|
| 142 |
+
|
| 143 |
+
setMessages(prev => [...prev, {
|
| 144 |
+
role: "assistant",
|
| 145 |
+
content: `✅ **Import réussi !**\n\n📄 ${filesProcessed} document(s) traité(s)\n📊 ${chunksAdded} nouveaux segments ajoutés\n💾 Total en base : **${totalChunks} chunks**\n\nVous pouvez maintenant poser des questions sur ces documents !`,
|
| 146 |
+
success: true,
|
| 147 |
+
timestamp: new Date().toISOString()
|
| 148 |
+
}]);
|
| 149 |
+
|
| 150 |
+
// Refresh complet du status
|
| 151 |
+
fetchStatus();
|
| 152 |
+
} catch (err) {
|
| 153 |
+
console.error("Erreur upload:", err);
|
| 154 |
+
let errorMsg = "❌ Échec de l'import des documents.";
|
| 155 |
+
|
| 156 |
+
if (err.code === 'ECONNABORTED') {
|
| 157 |
+
errorMsg += " Timeout : les fichiers sont peut-être trop volumineux.";
|
| 158 |
+
} else if (err.response?.data?.detail) {
|
| 159 |
+
errorMsg += ` Détails : ${err.response.data.detail}`;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
setMessages(prev => [...prev, {
|
| 163 |
+
role: "assistant",
|
| 164 |
+
content: errorMsg,
|
| 165 |
+
error: true,
|
| 166 |
+
timestamp: new Date().toISOString()
|
| 167 |
+
}]);
|
| 168 |
+
} finally {
|
| 169 |
+
setUploading(false);
|
| 170 |
+
setUploadProgress(null);
|
| 171 |
+
e.target.value = '';
|
| 172 |
+
}
|
| 173 |
};
|
| 174 |
|
| 175 |
+
// Réinitialisation du chat ET de la mémoire
|
| 176 |
+
const handleReset = async () => {
|
| 177 |
+
if (!window.confirm("Voulez-vous réinitialiser la conversation ET effacer la mémoire du modèle ?")) {
|
| 178 |
+
return;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
try {
|
| 182 |
+
// Appeler l'endpoint de clear memory
|
| 183 |
+
await axios.post(`${API_BASE}/clear-memory`, {}, { timeout: 5000 });
|
| 184 |
+
setMessages([]);
|
| 185 |
+
fetchStatus();
|
| 186 |
+
|
| 187 |
+
setMessages([{
|
| 188 |
+
role: "assistant",
|
| 189 |
+
content: "✅ Conversation et mémoire réinitialisées avec succès.",
|
| 190 |
+
success: true,
|
| 191 |
+
timestamp: new Date().toISOString()
|
| 192 |
+
}]);
|
| 193 |
+
} catch (error) {
|
| 194 |
+
console.error("Erreur lors de la réinitialisation:", error);
|
| 195 |
+
setMessages([{
|
| 196 |
+
role: "assistant",
|
| 197 |
+
content: "⚠️ Chat réinitialisé, mais impossible de contacter le serveur pour effacer la mémoire.",
|
| 198 |
+
error: true
|
| 199 |
+
}]);
|
| 200 |
+
}
|
| 201 |
+
};
|
| 202 |
+
|
| 203 |
+
// Toggle thinking pour chaque message
|
| 204 |
+
const toggleThinking = (msgIndex) => {
|
| 205 |
+
setShowThinking(prev => ({ ...prev, [msgIndex]: !prev[msgIndex] }));
|
| 206 |
+
};
|
| 207 |
+
|
| 208 |
+
// Formatage du temps relatif
|
| 209 |
+
const formatTimeAgo = (timestamp) => {
|
| 210 |
+
if (!timestamp) return "";
|
| 211 |
+
const seconds = Math.floor((new Date() - new Date(timestamp)) / 1000);
|
| 212 |
+
if (seconds < 60) return "à l'instant";
|
| 213 |
+
if (seconds < 3600) return `il y a ${Math.floor(seconds / 60)}m`;
|
| 214 |
+
if (seconds < 86400) return `il y a ${Math.floor(seconds / 3600)}h`;
|
| 215 |
+
return `il y a ${Math.floor(seconds / 86400)}j`;
|
| 216 |
+
};
|
| 217 |
+
|
| 218 |
+
// Styles centralisés
|
| 219 |
const styles = {
|
| 220 |
container: { display: 'flex', height: '100vh', backgroundColor: '#020617', color: '#f8fafc', fontFamily: 'system-ui, -apple-system, sans-serif' },
|
| 221 |
+
sidebar: { width: '340px', backgroundColor: '#0f172a', borderRight: '1px solid #1e293b', display: 'flex', flexDirection: 'column', overflowY: 'auto' },
|
| 222 |
main: { flex: 1, display: 'flex', flexDirection: 'column', position: 'relative', background: 'radial-gradient(circle at 50% 0%, #1e293b 0%, #020617 100%)' },
|
| 223 |
messageArea: { flex: 1, overflowY: 'auto', padding: '2rem 10% 2rem 10%' },
|
| 224 |
userBubble: { backgroundColor: '#059669', color: 'white', padding: '1rem 1.25rem', borderRadius: '1.25rem 1.25rem 0.25rem 1.25rem', boxShadow: '0 4px 15px rgba(5, 150, 105, 0.2)' },
|
| 225 |
aiBubble: { backgroundColor: '#1e293b', border: '1px solid #334155', color: '#f1f5f9', padding: '1rem 1.25rem', borderRadius: '1.25rem 1.25rem 1.25rem 0.25rem', boxShadow: '0 10px 30px rgba(0,0,0,0.5)' },
|
| 226 |
+
successBubble: { backgroundColor: '#064e3b', border: '1px solid #059669', color: '#d1fae5' },
|
| 227 |
+
errorBubble: { backgroundColor: '#7f1d1d', border: '1px solid #991b1b', color: '#fca5a5' },
|
| 228 |
inputContainer: { padding: '2rem', background: 'linear-gradient(to top, #020617 70%, transparent)' },
|
| 229 |
inputWrapper: { display: 'flex', alignItems: 'center', backgroundColor: 'rgba(30, 41, 59, 0.7)', backdropFilter: 'blur(20px)', border: '1px solid #334155', borderRadius: '1.5rem', padding: '0.6rem 1.2rem', maxWidth: '900px', margin: '0 auto' },
|
| 230 |
inputField: { flex: 1, background: 'transparent', border: 'none', color: 'white', padding: '0.8rem', outline: 'none', fontSize: '1rem' },
|
| 231 |
sendBtn: { backgroundColor: '#10b981', color: 'white', border: 'none', borderRadius: '1rem', width: '45px', height: '45px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', transition: 'all 0.2s' },
|
| 232 |
+
toolBadge: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '11px', backgroundColor: '#020617', padding: '6px 12px', borderRadius: '10px', border: '1px solid #1e293b', color: '#94a3b8' },
|
| 233 |
+
statCard: { backgroundColor: 'rgba(2, 6, 23, 0.5)', borderRadius: '1rem', padding: '1rem', border: '1px solid #1e293b', marginBottom: '0.8rem' }
|
| 234 |
};
|
| 235 |
|
| 236 |
return (
|
|
|
|
| 239 |
<aside style={styles.sidebar}>
|
| 240 |
<div style={{ padding: '2rem', borderBottom: '1px solid #1e293b', display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
| 241 |
<div style={{ width: '45px', height: '45px', backgroundColor: '#1e293b', borderRadius: '12px', display: 'flex', alignItems: 'center', justifyContent: 'center', border: '1px solid #334155' }}>
|
| 242 |
+
<img src={LOGO_PATH} alt="K" style={{ width: '30px', height: '30px', objectFit: 'contain' }} onError={(e) => e.target.style.display = 'none'} />
|
| 243 |
</div>
|
| 244 |
<div>
|
| 245 |
+
<h1 style={{ fontSize: '1.3rem', fontWeight: '900', margin: 0, letterSpacing: '-0.5px' }}>
|
| 246 |
+
Kibali <span style={{ color: '#10b981' }}>AI</span>
|
| 247 |
+
</h1>
|
| 248 |
+
<p style={{ fontSize: '10px', color: '#475569', margin: 0, marginTop: '4px', fontWeight: '700' }}>
|
| 249 |
+
v1.0 • {status.status === 'ready' ? '🟢 ONLINE' : '🔴 OFFLINE'}
|
| 250 |
+
</p>
|
| 251 |
</div>
|
| 252 |
</div>
|
| 253 |
|
| 254 |
+
<div style={{ padding: '2rem', flex: 1, display: 'flex', flexDirection: 'column', gap: '2rem' }}>
|
| 255 |
+
{/* Upload de documents */}
|
| 256 |
+
<div>
|
| 257 |
+
<h2 style={{ fontSize: '11px', color: '#475569', textTransform: 'uppercase', letterSpacing: '1.5px', marginBottom: '1rem', fontWeight: '800' }}>
|
| 258 |
+
📚 Documents
|
| 259 |
+
</h2>
|
| 260 |
+
<label style={{
|
| 261 |
+
display: 'flex',
|
| 262 |
+
flexDirection: 'column',
|
| 263 |
+
alignItems: 'center',
|
| 264 |
+
justifyContent: 'center',
|
| 265 |
+
height: '130px',
|
| 266 |
+
border: uploading ? '2px solid #10b981' : '2px dashed #1e293b',
|
| 267 |
+
borderRadius: '1.5rem',
|
| 268 |
+
cursor: uploading ? 'not-allowed' : 'pointer',
|
| 269 |
+
transition: 'all 0.3s',
|
| 270 |
+
backgroundColor: uploading ? 'rgba(16, 185, 129, 0.05)' : '#020617',
|
| 271 |
+
position: 'relative',
|
| 272 |
+
overflow: 'hidden'
|
| 273 |
+
}}>
|
| 274 |
+
{uploading ? (
|
| 275 |
+
<>
|
| 276 |
+
<Loader2 className="animate-spin" color="#10b981" size={32} />
|
| 277 |
+
<span style={{ fontSize: '12px', marginTop: '12px', color: '#10b981', fontWeight: '600' }}>
|
| 278 |
+
{uploadProgress?.percent ? `${uploadProgress.percent}%` : 'Traitement...'}
|
| 279 |
+
</span>
|
| 280 |
+
{uploadProgress?.total && (
|
| 281 |
+
<span style={{ fontSize: '10px', color: '#64748b', marginTop: '4px' }}>
|
| 282 |
+
{uploadProgress.current}/{uploadProgress.total} fichiers
|
| 283 |
+
</span>
|
| 284 |
+
)}
|
| 285 |
+
</>
|
| 286 |
+
) : (
|
| 287 |
+
<>
|
| 288 |
+
<Upload color="#10b981" size={32} />
|
| 289 |
+
<span style={{ fontSize: '12px', marginTop: '12px', color: '#64748b', fontWeight: '500' }}>
|
| 290 |
+
Importer rapports PDF
|
| 291 |
+
</span>
|
| 292 |
+
<span style={{ fontSize: '10px', color: '#475569', marginTop: '4px' }}>
|
| 293 |
+
Plusieurs fichiers acceptés
|
| 294 |
+
</span>
|
| 295 |
+
</>
|
| 296 |
+
)}
|
| 297 |
+
<input type="file" hidden multiple accept=".pdf" onChange={handleFileUpload} disabled={uploading} />
|
| 298 |
+
</label>
|
| 299 |
+
</div>
|
| 300 |
+
|
| 301 |
+
{/* Statistiques enrichies */}
|
| 302 |
+
<div>
|
| 303 |
+
<h3 style={{ fontSize: '11px', color: '#475569', textTransform: 'uppercase', marginBottom: '1rem', fontWeight: '800', letterSpacing: '1.5px' }}>
|
| 304 |
+
📊 Statistiques
|
| 305 |
+
</h3>
|
| 306 |
+
|
| 307 |
+
{/* Base de connaissances */}
|
| 308 |
+
<div style={styles.statCard}>
|
| 309 |
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
| 310 |
+
<span style={{ color: '#64748b', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '6px' }}>
|
| 311 |
+
<Database size={14} color="#10b981" /> Base documentaire
|
| 312 |
+
</span>
|
| 313 |
+
<span style={{ color: '#10b981', fontWeight: 'bold', fontSize: '15px' }}>
|
| 314 |
+
{status.doc_chunks || 0}
|
| 315 |
+
</span>
|
| 316 |
+
</div>
|
| 317 |
+
<div style={{ fontSize: '10px', color: '#475569', marginTop: '6px' }}>
|
| 318 |
+
segments vectorisés
|
| 319 |
+
</div>
|
| 320 |
</div>
|
| 321 |
+
|
| 322 |
+
{/* Mémoire conversationnelle */}
|
| 323 |
+
<div style={styles.statCard}>
|
| 324 |
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
| 325 |
+
<span style={{ color: '#64748b', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '6px' }}>
|
| 326 |
+
<Brain size={14} color="#3b82f6" /> Mémoire active
|
| 327 |
+
</span>
|
| 328 |
+
<span style={{ color: '#3b82f6', fontWeight: 'bold', fontSize: '15px' }}>
|
| 329 |
+
{status.memory_entries || 0}
|
| 330 |
+
</span>
|
| 331 |
+
</div>
|
| 332 |
+
<div style={{ fontSize: '10px', color: '#475569', marginTop: '6px' }}>
|
| 333 |
+
échanges mémorisés
|
| 334 |
</div>
|
|
|
|
|
|
|
|
|
|
| 335 |
</div>
|
|
|
|
| 336 |
|
| 337 |
+
{/* Contexte conversationnel */}
|
| 338 |
+
{status.current_subject && (
|
| 339 |
+
<div style={styles.statCard}>
|
| 340 |
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
| 341 |
+
<span style={{ color: '#64748b', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '6px' }}>
|
| 342 |
+
<TrendingUp size={14} color="#f59e0b" /> Sujet actuel
|
| 343 |
+
</span>
|
| 344 |
+
<span style={{ color: '#f59e0b', fontWeight: 'bold', fontSize: '15px' }}>
|
| 345 |
+
{status.subject_message_count}
|
| 346 |
+
</span>
|
| 347 |
+
</div>
|
| 348 |
+
<div style={{ fontSize: '10px', color: '#475569', marginTop: '6px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
| 349 |
+
{status.current_subject}
|
| 350 |
+
</div>
|
| 351 |
+
</div>
|
| 352 |
+
)}
|
| 353 |
|
| 354 |
+
{/* Localisation */}
|
| 355 |
+
<div style={styles.statCard}>
|
| 356 |
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
| 357 |
+
<span style={{ color: '#64748b', fontSize: '12px' }}>Position</span>
|
| 358 |
+
<span style={{ color: '#f1f5f9', display: 'flex', alignItems: 'center', gap: '6px', fontWeight: '600', fontSize: '13px' }}>
|
| 359 |
+
<MapPin size={14} color="#ef4444" /> Libreville
|
| 360 |
+
</span>
|
| 361 |
+
</div>
|
| 362 |
</div>
|
| 363 |
+
|
| 364 |
+
{/* Santé du système */}
|
| 365 |
+
<div style={styles.statCard}>
|
| 366 |
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
| 367 |
+
<span style={{ color: '#64748b', fontSize: '12px', display: 'flex', alignItems: 'center', gap: '6px' }}>
|
| 368 |
+
<Zap size={14} color={status.torch_cuda_available ? '#10b981' : '#f59e0b'} /> GPU
|
| 369 |
+
</span>
|
| 370 |
+
<span style={{ color: status.torch_cuda_available ? '#10b981' : '#f59e0b', fontWeight: 'bold', fontSize: '11px' }}>
|
| 371 |
+
{status.torch_cuda_available ? 'ACTIVÉ' : 'CPU ONLY'}
|
| 372 |
+
</span>
|
| 373 |
+
</div>
|
| 374 |
</div>
|
| 375 |
</div>
|
| 376 |
</div>
|
| 377 |
|
| 378 |
+
<button
|
| 379 |
+
onClick={handleReset}
|
| 380 |
+
style={{
|
| 381 |
+
margin: '2rem',
|
| 382 |
+
background: '#020617',
|
| 383 |
+
border: '1px solid #1e293b',
|
| 384 |
+
color: '#475569',
|
| 385 |
+
padding: '14px',
|
| 386 |
+
borderRadius: '14px',
|
| 387 |
+
cursor: 'pointer',
|
| 388 |
+
display: 'flex',
|
| 389 |
+
alignItems: 'center',
|
| 390 |
+
justifyContent: 'center',
|
| 391 |
+
gap: '10px',
|
| 392 |
+
fontWeight: '700',
|
| 393 |
+
fontSize: '12px',
|
| 394 |
+
transition: 'all 0.2s'
|
| 395 |
+
}}
|
| 396 |
+
onMouseEnter={(e) => {
|
| 397 |
+
e.target.style.borderColor = '#ef4444';
|
| 398 |
+
e.target.style.color = '#ef4444';
|
| 399 |
+
}}
|
| 400 |
+
onMouseLeave={(e) => {
|
| 401 |
+
e.target.style.borderColor = '#1e293b';
|
| 402 |
+
e.target.style.color = '#475569';
|
| 403 |
+
}}
|
| 404 |
+
>
|
| 405 |
+
<Trash2 size={16} /> RÉINITIALISER TOUT
|
| 406 |
</button>
|
| 407 |
</aside>
|
| 408 |
|
| 409 |
+
{/* MAIN CONTENT */}
|
| 410 |
<main style={styles.main}>
|
| 411 |
<div style={styles.messageArea}>
|
| 412 |
+
{messages.length === 0 && (
|
| 413 |
<div style={{ height: '80%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }}>
|
| 414 |
+
<div style={{
|
| 415 |
+
width: '100px',
|
| 416 |
+
height: '100px',
|
| 417 |
+
backgroundColor: '#0f172a',
|
| 418 |
+
borderRadius: '30px',
|
| 419 |
+
display: 'flex',
|
| 420 |
+
alignItems: 'center',
|
| 421 |
+
justifyContent: 'center',
|
| 422 |
+
marginBottom: '2rem',
|
| 423 |
+
border: '1px solid #334155',
|
| 424 |
+
boxShadow: '0 20px 50px rgba(0,0,0,0.5)'
|
| 425 |
+
}}>
|
| 426 |
+
<img src={LOGO_PATH} alt="Kibali" style={{ width: '60px' }} onError={(e) => e.target.style.display = 'none'} />
|
| 427 |
</div>
|
| 428 |
+
<h2 style={{ fontSize: '2.5rem', fontWeight: '900', marginBottom: '0.8rem', letterSpacing: '-1px' }}>
|
| 429 |
+
Bienvenue sur Kibali AI
|
| 430 |
+
</h2>
|
| 431 |
<p style={{ color: '#64748b', maxWidth: '450px', lineHeight: '1.7', fontSize: '1.1rem' }}>
|
| 432 |
+
Assistant IA expert du Gabon avec mémoire contextuelle et analyse documentaire avancée.
|
| 433 |
</p>
|
| 434 |
+
<div style={{ display: 'flex', gap: '1rem', marginTop: '2rem' }}>
|
| 435 |
+
<div style={{ textAlign: 'center' }}>
|
| 436 |
+
<div style={{ fontSize: '2rem', fontWeight: 'bold', color: '#10b981' }}>{status.doc_chunks}</div>
|
| 437 |
+
<div style={{ fontSize: '12px', color: '#64748b' }}>Chunks PDF</div>
|
| 438 |
+
</div>
|
| 439 |
+
<div style={{ textAlign: 'center' }}>
|
| 440 |
+
<div style={{ fontSize: '2rem', fontWeight: 'bold', color: '#3b82f6' }}>{status.memory_entries}</div>
|
| 441 |
+
<div style={{ fontSize: '12px', color: '#64748b' }}>Mémoires</div>
|
| 442 |
+
</div>
|
| 443 |
+
</div>
|
| 444 |
</div>
|
| 445 |
)}
|
| 446 |
|
| 447 |
{messages.map((m, i) => (
|
| 448 |
<div key={i} style={{ display: 'flex', justifyContent: m.role === 'user' ? 'flex-end' : 'flex-start', marginBottom: '3rem' }}>
|
| 449 |
<div style={{ display: 'flex', gap: '1.2rem', maxWidth: '85%', flexDirection: m.role === 'user' ? 'row-reverse' : 'row' }}>
|
| 450 |
+
<div style={{
|
| 451 |
+
width: '45px',
|
| 452 |
+
height: '45px',
|
| 453 |
+
borderRadius: '14px',
|
| 454 |
+
display: 'flex',
|
| 455 |
+
alignItems: 'center',
|
| 456 |
+
justifyContent: 'center',
|
| 457 |
+
backgroundColor: m.role === 'user' ? '#059669' : m.error ? '#7f1d1d' : m.success ? '#064e3b' : '#1e293b',
|
| 458 |
+
flexShrink: 0,
|
| 459 |
+
border: '1px solid #334155'
|
| 460 |
+
}}>
|
| 461 |
{m.role === 'user' ? (
|
| 462 |
+
<User size={24} color="white" />
|
| 463 |
+
) : m.error ? (
|
| 464 |
+
<AlertCircle size={24} color="#ef4444" />
|
| 465 |
+
) : m.success ? (
|
| 466 |
+
<CheckCircle size={24} color="#10b981" />
|
| 467 |
) : (
|
| 468 |
+
<img src={LOGO_PATH} style={{ width: '28px' }} alt="K" onError={(e) => e.target.style.display = 'none'} />
|
| 469 |
)}
|
| 470 |
</div>
|
| 471 |
|
| 472 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.8rem', width: '100%' }}>
|
| 473 |
+
{/* Informations contextuelles */}
|
| 474 |
+
{m.role === 'assistant' && !m.error && !m.success && m.context_info && (
|
| 475 |
+
<div style={{ backgroundColor: 'rgba(30, 41, 59, 0.3)', borderRadius: '12px', padding: '10px 14px', border: '1px solid #1e293b' }}>
|
| 476 |
+
<button
|
| 477 |
+
onClick={() => toggleThinking(i)}
|
| 478 |
+
style={{ background: 'none', border: 'none', color: '#64748b', fontSize: '11px', display: 'flex', alignItems: 'center', gap: '6px', cursor: 'pointer', fontWeight: '700', textTransform: 'uppercase', width: '100%', justifyContent: 'space-between' }}
|
| 479 |
+
>
|
| 480 |
+
<span style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
| 481 |
+
<Brain size={14} color="#10b981" /> Intelligence contextuelle
|
| 482 |
+
</span>
|
| 483 |
+
{showThinking[i] ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
|
| 484 |
</button>
|
| 485 |
+
|
| 486 |
+
{showThinking[i] && (
|
| 487 |
+
<>
|
| 488 |
+
<div style={{ display: 'flex', gap: '8px', marginTop: '12px', flexWrap: 'wrap' }}>
|
| 489 |
+
<div style={styles.toolBadge}>
|
| 490 |
+
<Globe size={13} color="#3b82f6" /> Web Search
|
| 491 |
+
{m.context_info.web_results > 0 && <span style={{ color: '#10b981' }}>• {m.context_info.web_results}</span>}
|
| 492 |
+
</div>
|
| 493 |
+
<div style={styles.toolBadge}>
|
| 494 |
+
<FileText size={13} color="#10b981" /> PDF Vault
|
| 495 |
+
{m.context_info.rag_sources?.length > 0 && <span style={{ color: '#10b981' }}>• {m.context_info.rag_sources.length}</span>}
|
| 496 |
+
</div>
|
| 497 |
+
<div style={styles.toolBadge}>
|
| 498 |
+
<Brain size={13} color="#f59e0b" /> Mémoire
|
| 499 |
+
{m.context_info.memory_used > 0 && <span style={{ color: '#10b981' }}>• {m.context_info.memory_used}</span>}
|
| 500 |
+
</div>
|
| 501 |
+
<div style={styles.toolBadge}>
|
| 502 |
+
<MapPin size={13} color="#ef4444" /> Geo-Context
|
| 503 |
+
</div>
|
| 504 |
+
</div>
|
| 505 |
+
|
| 506 |
+
{/* Détails du contexte */}
|
| 507 |
+
<div style={{ marginTop: '12px', fontSize: '11px', color: '#64748b', padding: '10px', backgroundColor: '#020617', borderRadius: '8px' }}>
|
| 508 |
+
{m.context_info.subject_keywords?.length > 0 && (
|
| 509 |
+
<div style={{ marginBottom: '8px' }}>
|
| 510 |
+
<strong style={{ color: '#94a3b8' }}>Mots-clés du sujet :</strong> {m.context_info.subject_keywords.join(', ')}
|
| 511 |
+
</div>
|
| 512 |
+
)}
|
| 513 |
+
{m.context_info.message_count > 0 && (
|
| 514 |
+
<div style={{ marginBottom: '8px' }}>
|
| 515 |
+
<strong style={{ color: '#94a3b8' }}>Messages sur ce sujet :</strong> {m.context_info.message_count}
|
| 516 |
+
</div>
|
| 517 |
+
)}
|
| 518 |
+
{m.context_info.rag_sources?.length > 0 && (
|
| 519 |
+
<div>
|
| 520 |
+
<strong style={{ color: '#94a3b8' }}>Sources documentaires :</strong> {m.context_info.rag_sources.join(', ')}
|
| 521 |
+
</div>
|
| 522 |
+
)}
|
| 523 |
+
</div>
|
| 524 |
+
</>
|
| 525 |
)}
|
| 526 |
</div>
|
| 527 |
)}
|
| 528 |
+
|
| 529 |
+
{/* Bulle de message */}
|
| 530 |
+
<div style={{
|
| 531 |
+
...(m.role === 'user' ? styles.userBubble : m.error ? styles.errorBubble : m.success ? styles.successBubble : styles.aiBubble),
|
| 532 |
+
position: 'relative'
|
| 533 |
+
}}>
|
| 534 |
+
<p style={{ margin: 0, fontSize: '1.05rem', lineHeight: '1.6', fontWeight: '400', whiteSpace: 'pre-wrap' }}>
|
| 535 |
+
{m.content}
|
| 536 |
+
</p>
|
| 537 |
+
{m.timestamp && (
|
| 538 |
+
<div style={{ fontSize: '10px', color: 'rgba(255,255,255,0.4)', marginTop: '8px', display: 'flex', alignItems: 'center', gap: '4px' }}>
|
| 539 |
+
<Clock size={10} /> {formatTimeAgo(m.timestamp)}
|
| 540 |
+
</div>
|
| 541 |
+
)}
|
| 542 |
</div>
|
| 543 |
+
|
| 544 |
+
{/* Images */}
|
| 545 |
{m.images && m.images.length > 0 && (
|
| 546 |
+
<div style={{ display: 'flex', gap: '12px', marginTop: '8px', overflowX: 'auto', paddingBottom: '10px' }}>
|
| 547 |
{m.images.map((img, idx) => (
|
| 548 |
+
<div key={idx} style={{ flexShrink: 0, position: 'relative', borderRadius: '1.2rem', overflow: 'hidden', border: '2px solid #334155', backgroundColor: '#0f172a' }}>
|
| 549 |
+
<img
|
| 550 |
+
src={img}
|
| 551 |
+
alt={`Image ${idx + 1}`}
|
| 552 |
+
style={{ height: '180px', width: '280px', objectFit: 'cover' }}
|
| 553 |
+
onError={(e) => {
|
| 554 |
+
e.target.style.display = 'none';
|
| 555 |
+
e.target.parentElement.innerHTML = '<div style="height:180px;width:280px;display:flex;align-items:center;justify-content:center;color:#64748b;font-size:12px;"><ImageIcon size={24} /> Image indisponible</div>';
|
| 556 |
+
}}
|
| 557 |
+
/>
|
| 558 |
</div>
|
| 559 |
))}
|
| 560 |
</div>
|
|
|
|
| 566 |
|
| 567 |
{loading && (
|
| 568 |
<div style={{ display: 'flex', gap: '1.2rem', alignItems: 'center' }}>
|
| 569 |
+
<div style={{
|
| 570 |
+
width: '45px',
|
| 571 |
+
height: '45px',
|
| 572 |
+
borderRadius: '14px',
|
| 573 |
+
backgroundColor: '#1e293b',
|
| 574 |
+
display: 'flex',
|
| 575 |
+
alignItems: 'center',
|
| 576 |
+
justifyContent: 'center',
|
| 577 |
+
border: '1px solid #334155'
|
| 578 |
+
}}>
|
| 579 |
<Loader2 className="animate-spin" color="#10b981" size={24} />
|
| 580 |
</div>
|
| 581 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
|
| 582 |
+
<div style={{ color: '#f1f5f9', fontSize: '15px', fontWeight: '600' }}>
|
| 583 |
+
Kibali analyse votre demande...
|
| 584 |
+
</div>
|
| 585 |
+
<div style={{ color: '#64748b', fontSize: '12px' }}>
|
| 586 |
+
Recherche multi-sources • Traitement vectoriel • Génération contextuelle
|
| 587 |
+
</div>
|
| 588 |
+
</div>
|
| 589 |
</div>
|
| 590 |
)}
|
| 591 |
+
|
| 592 |
<div ref={scrollRef} />
|
| 593 |
</div>
|
| 594 |
|
|
|
|
| 597 |
<Search size={22} color="#475569" style={{ marginRight: '10px' }} />
|
| 598 |
<input
|
| 599 |
style={styles.inputField}
|
| 600 |
+
placeholder="Posez votre question sur le Gabon, vos documents ou l'actualité..."
|
| 601 |
value={input}
|
| 602 |
onChange={(e) => setInput(e.target.value)}
|
| 603 |
+
onKeyDown={(e) => {
|
| 604 |
+
if (e.key === 'Enter' && !e.shiftKey) {
|
| 605 |
+
e.preventDefault();
|
| 606 |
+
handleSend();
|
| 607 |
+
}
|
| 608 |
+
}}
|
| 609 |
+
disabled={loading}
|
| 610 |
/>
|
| 611 |
<button
|
| 612 |
+
style={{
|
| 613 |
+
...styles.sendBtn,
|
| 614 |
+
opacity: input.trim() && !loading ? 1 : 0.4,
|
| 615 |
+
cursor: input.trim() && !loading ? 'pointer' : 'not-allowed',
|
| 616 |
+
transform: input.trim() && !loading ? 'scale(1)' : 'scale(0.95)'
|
| 617 |
+
}}
|
| 618 |
onClick={handleSend}
|
| 619 |
+
disabled={loading || !input.trim()}
|
| 620 |
+
onMouseEnter={(e) => {
|
| 621 |
+
if (input.trim() && !loading) {
|
| 622 |
+
e.target.style.backgroundColor = '#059669';
|
| 623 |
+
e.target.style.transform = 'scale(1.05)';
|
| 624 |
+
}
|
| 625 |
+
}}
|
| 626 |
+
onMouseLeave={(e) => {
|
| 627 |
+
e.target.style.backgroundColor = '#10b981';
|
| 628 |
+
e.target.style.transform = 'scale(1)';
|
| 629 |
+
}}
|
| 630 |
>
|
| 631 |
+
{loading ? <Loader2 className="animate-spin" size={20} /> : <Send size={20} />}
|
| 632 |
</button>
|
| 633 |
</div>
|
| 634 |
+
|
| 635 |
+
<div style={{ textAlign: 'center', marginTop: '1.5rem', display: 'flex', flexDirection: 'column', gap: '0.5rem', alignItems: 'center' }}>
|
| 636 |
+
<p style={{
|
| 637 |
+
color: '#334155',
|
| 638 |
+
fontSize: '10px',
|
| 639 |
+
fontWeight: '800',
|
| 640 |
+
letterSpacing: '2px',
|
| 641 |
+
margin: 0
|
| 642 |
+
}}>
|
| 643 |
+
GABONESE SOVEREIGN ARTIFICIAL INTELLIGENCE
|
| 644 |
+
</p>
|
| 645 |
+
<div style={{ display: 'flex', gap: '1rem', fontSize: '9px', color: '#1e293b', fontWeight: '700' }}>
|
| 646 |
+
<span>SYSTEM KIBALI-1</span>
|
| 647 |
+
<span>•</span>
|
| 648 |
+
<span>SETRAF-GABON</span>
|
| 649 |
+
<span>•</span>
|
| 650 |
+
<span style={{ color: status.torch_cuda_available ? '#10b981' : '#f59e0b' }}>
|
| 651 |
+
{status.torch_cuda_available ? '⚡ GPU ACCELERATED' : '⚙️ CPU MODE'}
|
| 652 |
+
</span>
|
| 653 |
+
</div>
|
| 654 |
+
</div>
|
| 655 |
</div>
|
| 656 |
</main>
|
| 657 |
</div>
|
main.py
CHANGED
|
@@ -12,6 +12,9 @@ from threading import Thread
|
|
| 12 |
import os
|
| 13 |
from io import BytesIO
|
| 14 |
import logging
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
# --- CONFIGURATION LOGGING ---
|
| 17 |
logging.basicConfig(level=logging.INFO)
|
|
@@ -30,30 +33,13 @@ except ImportError:
|
|
| 30 |
raise ImportError("Installe pypdf ou PyPDF2 : pip install pypdf")
|
| 31 |
|
| 32 |
# --- OUTILS PERSONNALISÉS ---
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
logger.warning("Module tools.web non trouvé, utilisation d'une fonction stub")
|
| 37 |
-
def web_search(query):
|
| 38 |
-
return {"results": [], "images": []}
|
| 39 |
-
|
| 40 |
-
try:
|
| 41 |
-
from tools.todo import execute_reflection_plan
|
| 42 |
-
except ImportError:
|
| 43 |
-
logger.warning("Module tools.todo non trouvé")
|
| 44 |
-
def execute_reflection_plan(*args, **kwargs):
|
| 45 |
-
return None
|
| 46 |
-
|
| 47 |
-
try:
|
| 48 |
-
from tools.geo import get_geo_context
|
| 49 |
-
except ImportError:
|
| 50 |
-
logger.warning("Module tools.geo non trouvé")
|
| 51 |
-
def get_geo_context(*args, **kwargs):
|
| 52 |
-
return {}
|
| 53 |
|
| 54 |
app = FastAPI(title="Kibali AI API", version="1.0")
|
| 55 |
|
| 56 |
-
# --- SERVEUR STATIQUE
|
| 57 |
script_dir = os.path.dirname(os.path.abspath(__file__))
|
| 58 |
static_dir = os.path.join(script_dir, "static")
|
| 59 |
os.makedirs(static_dir, exist_ok=True)
|
|
@@ -68,58 +54,99 @@ app.add_middleware(
|
|
| 68 |
allow_headers=["*"],
|
| 69 |
)
|
| 70 |
|
| 71 |
-
# --- CHARGEMENT DES MODÈLES
|
| 72 |
-
|
| 73 |
-
|
| 74 |
logger.info("Chargement du modèle d'embedding...")
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
try:
|
| 94 |
-
bnb_config = BitsAndBytesConfig(
|
| 95 |
-
load_in_4bit=True,
|
| 96 |
-
bnb_4bit_use_double_quant=True,
|
| 97 |
-
bnb_4bit_quant_type="nf4",
|
| 98 |
-
bnb_4bit_compute_dtype=torch.float16
|
| 99 |
-
)
|
| 100 |
-
|
| 101 |
-
model = AutoModelForCausalLM.from_pretrained(
|
| 102 |
-
MODEL_REPO,
|
| 103 |
-
quantization_config=bnb_config,
|
| 104 |
-
device_map="auto",
|
| 105 |
-
torch_dtype=torch.float16,
|
| 106 |
-
trust_remote_code=True,
|
| 107 |
-
low_cpu_mem_usage=True
|
| 108 |
-
)
|
| 109 |
-
logger.info(f"Modèle LLM chargé avec succès sur {model.device}")
|
| 110 |
-
except Exception as e:
|
| 111 |
-
logger.error(f"Erreur chargement modèle LLM : {e}")
|
| 112 |
-
raise
|
| 113 |
-
|
| 114 |
-
# --- BASES VECTORIELLES (séparées) ---
|
| 115 |
dimension = 384
|
| 116 |
-
# Index pour les documents PDF
|
| 117 |
doc_index = faiss.IndexFlatL2(dimension)
|
| 118 |
doc_chunks: List[str] = []
|
|
|
|
| 119 |
|
| 120 |
-
# Index séparé pour la mémoire conversationnelle
|
| 121 |
memory_index = faiss.IndexFlatL2(dimension)
|
| 122 |
memory_texts: List[str] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
# --- MODÈLES PYDANTIC ---
|
| 125 |
class Message(BaseModel):
|
|
@@ -136,10 +163,10 @@ class ChatRequest(BaseModel):
|
|
| 136 |
class ChatResponse(BaseModel):
|
| 137 |
response: str
|
| 138 |
images: List[str] = []
|
|
|
|
| 139 |
|
| 140 |
# --- UTILITAIRES ---
|
| 141 |
def extract_text_from_pdf(pdf_bytes: bytes) -> str:
|
| 142 |
-
"""Extrait le texte d'un PDF depuis des bytes"""
|
| 143 |
text = ""
|
| 144 |
try:
|
| 145 |
pdf_file = BytesIO(pdf_bytes)
|
|
@@ -148,41 +175,105 @@ def extract_text_from_pdf(pdf_bytes: bytes) -> str:
|
|
| 148 |
page_text = page.extract_text()
|
| 149 |
if page_text:
|
| 150 |
text += page_text + "\n"
|
|
|
|
| 151 |
except Exception as e:
|
| 152 |
logger.error(f"Erreur extraction PDF : {e}")
|
| 153 |
-
|
| 154 |
|
| 155 |
def chunk_text(text: str, chunk_size: int = 400, overlap: int = 50) -> List[str]:
|
| 156 |
-
|
|
|
|
| 157 |
words = text.split()
|
| 158 |
chunks = []
|
| 159 |
i = 0
|
| 160 |
while i < len(words):
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
| 163 |
i += chunk_size - overlap
|
| 164 |
-
if i >= len(words) and len(
|
| 165 |
break
|
| 166 |
return chunks
|
| 167 |
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
|
|
|
| 183 |
@app.get("/status")
|
| 184 |
async def status():
|
| 185 |
-
"""Statut de l'API et des ressources chargées"""
|
| 186 |
return {
|
| 187 |
"status": "ready",
|
| 188 |
"doc_chunks": len(doc_chunks),
|
|
@@ -190,188 +281,215 @@ async def status():
|
|
| 190 |
"pdf_library": PDF_READER,
|
| 191 |
"model_device": str(model.device),
|
| 192 |
"torch_cuda_available": torch.cuda.is_available(),
|
| 193 |
-
"
|
|
|
|
| 194 |
}
|
| 195 |
|
| 196 |
@app.post("/chat", response_model=ChatResponse)
|
| 197 |
async def chat(request: ChatRequest):
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
if
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
|
| 242 |
-
|
| 243 |
-
|
| 244 |
Réponds toujours en français, de façon naturelle, concise et factuelle.
|
| 245 |
-
Utilise uniquement les informations fournies dans les contextes ci-dessous ou ta connaissance vérifiée.
|
| 246 |
-
Si tu n'es pas sûr d'une information, dis-le clairement plutôt que d'inventer.
|
| 247 |
-
Ne répète jamais une réponse précédente et n'ajoute pas d'informations inutiles."""
|
| 248 |
|
| 249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
{system_prompt}
|
| 251 |
|
| 252 |
-
### CONTEXTE DOCUMENTS (
|
| 253 |
-
{rag_context}
|
| 254 |
|
| 255 |
-
### HISTORIQUE
|
| 256 |
-
{memory_context}
|
| 257 |
|
| 258 |
-
### INFORMATIONS
|
| 259 |
-
{web_context}
|
| 260 |
|
| 261 |
-
### QUESTION
|
| 262 |
{user_message}
|
| 263 |
|
| 264 |
-
### RÉPONSE (en français uniquement
|
| 265 |
"""
|
| 266 |
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True, timeout=120.0)
|
| 270 |
-
|
| 271 |
-
def generate_stream():
|
| 272 |
-
try:
|
| 273 |
-
model.generate(
|
| 274 |
-
**inputs,
|
| 275 |
-
streamer=streamer,
|
| 276 |
-
max_new_tokens=1024,
|
| 277 |
-
temperature=0.6,
|
| 278 |
-
do_sample=True,
|
| 279 |
-
top_p=0.85,
|
| 280 |
-
top_k=50,
|
| 281 |
-
repetition_penalty=1.2,
|
| 282 |
-
length_penalty=0.8
|
| 283 |
-
)
|
| 284 |
-
except Exception as e:
|
| 285 |
-
logger.error(f"Erreur génération : {e}")
|
| 286 |
-
|
| 287 |
-
thread = Thread(target=generate_stream)
|
| 288 |
-
thread.start()
|
| 289 |
-
|
| 290 |
-
response_text = ""
|
| 291 |
-
try:
|
| 292 |
-
for new_text in streamer:
|
| 293 |
-
if new_text is None:
|
| 294 |
-
break
|
| 295 |
-
response_text += new_text
|
| 296 |
-
except Exception as e:
|
| 297 |
-
logger.error(f"Erreur streaming : {e}")
|
| 298 |
-
response_text = "Désolé, une erreur est survenue pendant la génération."
|
| 299 |
|
| 300 |
-
|
| 301 |
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
|
| 312 |
-
|
| 313 |
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
raise HTTPException(status_code=500, detail=f"Erreur serveur : {str(e)}")
|
| 319 |
|
| 320 |
-
@app.post("/upload-pdfs")
|
| 321 |
-
async def upload_pdfs(files: List[UploadFile] = File(...)):
|
| 322 |
-
"""Upload et indexation de fichiers PDF"""
|
| 323 |
-
added = 0
|
| 324 |
-
errors = []
|
| 325 |
-
|
| 326 |
for file in files:
|
| 327 |
if not file.filename.lower().endswith(".pdf"):
|
| 328 |
-
errors.append(f"{file.filename} : n'est pas un PDF")
|
| 329 |
continue
|
| 330 |
-
|
| 331 |
try:
|
| 332 |
content = await file.read()
|
| 333 |
text = extract_text_from_pdf(content)
|
| 334 |
-
|
| 335 |
-
if text
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
except Exception as e:
|
| 347 |
-
logger.error(f"Erreur
|
| 348 |
-
errors.append(f"{file.filename} : {str(e)}")
|
| 349 |
-
|
| 350 |
-
return {
|
| 351 |
-
"status": "success" if added > 0 else "partial" if errors else "failed",
|
| 352 |
-
"chunks_added": added,
|
| 353 |
-
"total_doc_chunks": len(doc_chunks),
|
| 354 |
-
"errors": errors if errors else None
|
| 355 |
-
}
|
| 356 |
|
| 357 |
-
@app.get("/health")
|
| 358 |
-
async def health():
|
| 359 |
-
"""Health check pour monitoring"""
|
| 360 |
return {
|
| 361 |
-
"status": "
|
| 362 |
-
"
|
| 363 |
-
"
|
|
|
|
| 364 |
}
|
| 365 |
|
| 366 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
@app.on_event("startup")
|
| 368 |
async def startup_event():
|
| 369 |
logger.info("🚀 Kibali AI API démarrée avec succès !")
|
| 370 |
-
logger.info(f"
|
| 371 |
-
logger.info(f"
|
| 372 |
-
logger.info(f"CUDA disponible : {torch.cuda.is_available()}")
|
| 373 |
-
logger.info("Accès : http://localhost:8000 | Docs : http://localhost:8000/docs")
|
| 374 |
-
|
| 375 |
-
@app.on_event("shutdown")
|
| 376 |
-
async def shutdown_event():
|
| 377 |
-
logger.info("👋 Arrêt de Kibali AI API")
|
|
|
|
| 12 |
import os
|
| 13 |
from io import BytesIO
|
| 14 |
import logging
|
| 15 |
+
from datetime import datetime
|
| 16 |
+
import json
|
| 17 |
+
import hashlib
|
| 18 |
|
| 19 |
# --- CONFIGURATION LOGGING ---
|
| 20 |
logging.basicConfig(level=logging.INFO)
|
|
|
|
| 33 |
raise ImportError("Installe pypdf ou PyPDF2 : pip install pypdf")
|
| 34 |
|
| 35 |
# --- OUTILS PERSONNALISÉS ---
|
| 36 |
+
from tools.web import web_search
|
| 37 |
+
from tools.todo import execute_reflection_plan
|
| 38 |
+
from tools.geo import get_geo_context
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
app = FastAPI(title="Kibali AI API", version="1.0")
|
| 41 |
|
| 42 |
+
# --- SERVEUR STATIQUE ---
|
| 43 |
script_dir = os.path.dirname(os.path.abspath(__file__))
|
| 44 |
static_dir = os.path.join(script_dir, "static")
|
| 45 |
os.makedirs(static_dir, exist_ok=True)
|
|
|
|
| 54 |
allow_headers=["*"],
|
| 55 |
)
|
| 56 |
|
| 57 |
+
# --- CHARGEMENT DES MODÈLES ---
|
| 58 |
+
MODEL_PATH = "/home/belikan/geoscan/agent_kibali/model_cache"
|
|
|
|
| 59 |
logger.info("Chargement du modèle d'embedding...")
|
| 60 |
+
embed_model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
|
| 61 |
+
logger.info("Chargement du tokenizer et du modèle LLM...")
|
| 62 |
+
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, local_files_only=True)
|
| 63 |
+
if tokenizer.pad_token is None:
|
| 64 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 65 |
+
|
| 66 |
+
bnb_config = BitsAndBytesConfig(
|
| 67 |
+
load_in_4bit=True,
|
| 68 |
+
bnb_4bit_use_double_quant=True,
|
| 69 |
+
bnb_4bit_quant_type="nf4",
|
| 70 |
+
bnb_4bit_compute_dtype=torch.float16
|
| 71 |
+
)
|
| 72 |
|
| 73 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 74 |
+
MODEL_PATH,
|
| 75 |
+
quantization_config=bnb_config,
|
| 76 |
+
device_map="auto",
|
| 77 |
+
torch_dtype=torch.float16,
|
| 78 |
+
trust_remote_code=True,
|
| 79 |
+
low_cpu_mem_usage=True
|
| 80 |
+
)
|
| 81 |
+
logger.info(f"Modèle chargé sur {model.device}")
|
| 82 |
+
|
| 83 |
+
# --- BASES VECTORIELLES GLOBALES ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
dimension = 384
|
|
|
|
| 85 |
doc_index = faiss.IndexFlatL2(dimension)
|
| 86 |
doc_chunks: List[str] = []
|
| 87 |
+
doc_metadata: List[dict] = [] # Métadonnées des chunks (source, timestamp, etc.)
|
| 88 |
|
|
|
|
| 89 |
memory_index = faiss.IndexFlatL2(dimension)
|
| 90 |
memory_texts: List[str] = []
|
| 91 |
+
memory_metadata: List[dict] = [] # Métadonnées des mémoires (timestamp, sujet, score)
|
| 92 |
+
|
| 93 |
+
# --- GESTION DU CONTEXTE CONVERSATIONNEL ---
|
| 94 |
+
class ConversationContext:
|
| 95 |
+
def __init__(self):
|
| 96 |
+
self.current_subject = None
|
| 97 |
+
self.subject_embedding = None
|
| 98 |
+
self.subject_start_time = None
|
| 99 |
+
self.message_count = 0
|
| 100 |
+
self.subject_keywords = []
|
| 101 |
+
|
| 102 |
+
def update_subject(self, message: str, embedding: np.ndarray):
|
| 103 |
+
"""Détecte et met à jour le sujet actuel de la conversation"""
|
| 104 |
+
keywords = self._extract_keywords(message)
|
| 105 |
+
|
| 106 |
+
# Détection de changement de sujet
|
| 107 |
+
if self.subject_embedding is not None:
|
| 108 |
+
similarity = np.dot(embedding.flatten(), self.subject_embedding.flatten())
|
| 109 |
+
if similarity < 0.6: # Seuil de changement de sujet
|
| 110 |
+
logger.info(f"Changement de sujet détecté (similarité: {similarity:.2f})")
|
| 111 |
+
self._archive_current_subject()
|
| 112 |
+
self.current_subject = message
|
| 113 |
+
self.subject_embedding = embedding
|
| 114 |
+
self.subject_start_time = datetime.now()
|
| 115 |
+
self.message_count = 1
|
| 116 |
+
self.subject_keywords = keywords
|
| 117 |
+
else:
|
| 118 |
+
self.message_count += 1
|
| 119 |
+
self.subject_keywords.extend(keywords)
|
| 120 |
+
self.subject_keywords = list(set(self.subject_keywords))[:10] # Top 10
|
| 121 |
+
else:
|
| 122 |
+
self.current_subject = message
|
| 123 |
+
self.subject_embedding = embedding
|
| 124 |
+
self.subject_start_time = datetime.now()
|
| 125 |
+
self.message_count = 1
|
| 126 |
+
self.subject_keywords = keywords
|
| 127 |
+
|
| 128 |
+
def _extract_keywords(self, text: str) -> List[str]:
|
| 129 |
+
"""Extrait les mots-clés importants du texte"""
|
| 130 |
+
stopwords = {'le', 'la', 'les', 'un', 'une', 'des', 'de', 'du', 'et', 'ou',
|
| 131 |
+
'est', 'sont', 'à', 'au', 'en', 'pour', 'dans', 'sur', 'avec'}
|
| 132 |
+
words = text.lower().split()
|
| 133 |
+
keywords = [w for w in words if len(w) > 3 and w not in stopwords]
|
| 134 |
+
return keywords[:5]
|
| 135 |
+
|
| 136 |
+
def _archive_current_subject(self):
|
| 137 |
+
"""Archive le sujet actuel avant de passer au suivant"""
|
| 138 |
+
if self.current_subject and memory_index.ntotal > 0:
|
| 139 |
+
# Créer un résumé du sujet archivé
|
| 140 |
+
summary = {
|
| 141 |
+
"subject": self.current_subject[:200],
|
| 142 |
+
"keywords": self.subject_keywords,
|
| 143 |
+
"message_count": self.message_count,
|
| 144 |
+
"duration": (datetime.now() - self.subject_start_time).seconds,
|
| 145 |
+
"archived_at": datetime.now().isoformat()
|
| 146 |
+
}
|
| 147 |
+
logger.info(f"Sujet archivé: {summary['keywords']}")
|
| 148 |
+
|
| 149 |
+
conversation_ctx = ConversationContext()
|
| 150 |
|
| 151 |
# --- MODÈLES PYDANTIC ---
|
| 152 |
class Message(BaseModel):
|
|
|
|
| 163 |
class ChatResponse(BaseModel):
|
| 164 |
response: str
|
| 165 |
images: List[str] = []
|
| 166 |
+
context_info: Optional[dict] = None
|
| 167 |
|
| 168 |
# --- UTILITAIRES ---
|
| 169 |
def extract_text_from_pdf(pdf_bytes: bytes) -> str:
|
|
|
|
| 170 |
text = ""
|
| 171 |
try:
|
| 172 |
pdf_file = BytesIO(pdf_bytes)
|
|
|
|
| 175 |
page_text = page.extract_text()
|
| 176 |
if page_text:
|
| 177 |
text += page_text + "\n"
|
| 178 |
+
return text.strip()
|
| 179 |
except Exception as e:
|
| 180 |
logger.error(f"Erreur extraction PDF : {e}")
|
| 181 |
+
return ""
|
| 182 |
|
| 183 |
def chunk_text(text: str, chunk_size: int = 400, overlap: int = 50) -> List[str]:
|
| 184 |
+
if not text.strip():
|
| 185 |
+
return []
|
| 186 |
words = text.split()
|
| 187 |
chunks = []
|
| 188 |
i = 0
|
| 189 |
while i < len(words):
|
| 190 |
+
chunk_words = words[i:i + chunk_size]
|
| 191 |
+
chunk = " ".join(chunk_words)
|
| 192 |
+
if chunk.strip():
|
| 193 |
+
chunks.append(chunk.strip())
|
| 194 |
i += chunk_size - overlap
|
| 195 |
+
if i >= len(words) and len(chunk_words) < overlap:
|
| 196 |
break
|
| 197 |
return chunks
|
| 198 |
|
| 199 |
+
def add_to_memory_realtime(user_msg: str, ai_response: str, subject_keywords: List[str]):
|
| 200 |
+
"""Ajoute une entrée mémoire en temps réel avec métadonnées enrichies"""
|
| 201 |
+
timestamp = datetime.now().isoformat()
|
| 202 |
+
|
| 203 |
+
# Créer une entrée mémoire enrichie
|
| 204 |
+
memory_entry = f"""[{timestamp}]
|
| 205 |
+
Sujet: {', '.join(subject_keywords)}
|
| 206 |
+
Utilisateur: {user_msg}
|
| 207 |
+
Kibali: {ai_response}"""
|
| 208 |
+
|
| 209 |
+
# Métadonnées
|
| 210 |
+
metadata = {
|
| 211 |
+
"timestamp": timestamp,
|
| 212 |
+
"subject_keywords": subject_keywords,
|
| 213 |
+
"user_length": len(user_msg),
|
| 214 |
+
"ai_length": len(ai_response),
|
| 215 |
+
"hash": hashlib.md5(memory_entry.encode()).hexdigest()
|
| 216 |
}
|
| 217 |
+
|
| 218 |
+
# Éviter les doublons
|
| 219 |
+
if metadata["hash"] not in [m.get("hash") for m in memory_metadata]:
|
| 220 |
+
memory_texts.append(memory_entry)
|
| 221 |
+
memory_metadata.append(metadata)
|
| 222 |
+
|
| 223 |
+
# Ajout vectoriel
|
| 224 |
+
mem_emb = embed_model.encode([memory_entry], normalize_embeddings=True).astype('float32')
|
| 225 |
+
memory_index.add(mem_emb)
|
| 226 |
+
|
| 227 |
+
logger.info(f"Mémoire ajoutée en temps réel: {subject_keywords} (total: {len(memory_texts)})")
|
| 228 |
+
return True
|
| 229 |
+
return False
|
| 230 |
+
|
| 231 |
+
def retrieve_adaptive_memory(query: str, k: int = 5) -> tuple:
|
| 232 |
+
"""Récupère la mémoire de façon adaptative selon le contexte"""
|
| 233 |
+
if memory_index.ntotal == 0:
|
| 234 |
+
return [], []
|
| 235 |
+
|
| 236 |
+
query_emb = embed_model.encode([query], normalize_embeddings=True).astype('float32')
|
| 237 |
+
|
| 238 |
+
# Recherche de base
|
| 239 |
+
k_search = min(k * 2, memory_index.ntotal) # Chercher plus pour filtrer ensuite
|
| 240 |
+
D, I = memory_index.search(query_emb, k=k_search)
|
| 241 |
+
|
| 242 |
+
# Filtrage intelligent avec scoring
|
| 243 |
+
results = []
|
| 244 |
+
for dist, idx in zip(D[0], I[0]):
|
| 245 |
+
if 0 <= idx < len(memory_texts):
|
| 246 |
+
metadata = memory_metadata[idx] if idx < len(memory_metadata) else {}
|
| 247 |
+
|
| 248 |
+
# Score de pertinence
|
| 249 |
+
recency_score = 1.0 / (1 + (datetime.now() - datetime.fromisoformat(metadata.get("timestamp", datetime.now().isoformat()))).seconds / 3600)
|
| 250 |
+
similarity_score = 1.0 / (1 + dist)
|
| 251 |
+
|
| 252 |
+
# Bonus si les mots-clés du sujet actuel correspondent
|
| 253 |
+
keyword_bonus = 0
|
| 254 |
+
if conversation_ctx.subject_keywords:
|
| 255 |
+
text_lower = memory_texts[idx].lower()
|
| 256 |
+
keyword_bonus = sum(1 for kw in conversation_ctx.subject_keywords if kw in text_lower) * 0.1
|
| 257 |
+
|
| 258 |
+
total_score = similarity_score * 0.6 + recency_score * 0.3 + keyword_bonus
|
| 259 |
+
|
| 260 |
+
results.append({
|
| 261 |
+
"text": memory_texts[idx],
|
| 262 |
+
"score": total_score,
|
| 263 |
+
"metadata": metadata
|
| 264 |
+
})
|
| 265 |
+
|
| 266 |
+
# Trier par score et prendre les top k
|
| 267 |
+
results = sorted(results, key=lambda x: x["score"], reverse=True)[:k]
|
| 268 |
+
|
| 269 |
+
texts = [r["text"] for r in results]
|
| 270 |
+
scores = [r["score"] for r in results]
|
| 271 |
+
|
| 272 |
+
return texts, scores
|
| 273 |
|
| 274 |
+
# --- ROUTES ---
|
| 275 |
@app.get("/status")
|
| 276 |
async def status():
|
|
|
|
| 277 |
return {
|
| 278 |
"status": "ready",
|
| 279 |
"doc_chunks": len(doc_chunks),
|
|
|
|
| 281 |
"pdf_library": PDF_READER,
|
| 282 |
"model_device": str(model.device),
|
| 283 |
"torch_cuda_available": torch.cuda.is_available(),
|
| 284 |
+
"current_subject": conversation_ctx.current_subject[:100] if conversation_ctx.current_subject else None,
|
| 285 |
+
"subject_message_count": conversation_ctx.message_count
|
| 286 |
}
|
| 287 |
|
| 288 |
@app.post("/chat", response_model=ChatResponse)
|
| 289 |
async def chat(request: ChatRequest):
|
| 290 |
+
user_message = request.messages[-1].content.strip()
|
| 291 |
+
if not user_message:
|
| 292 |
+
raise HTTPException(status_code=400, detail="Message vide")
|
| 293 |
+
|
| 294 |
+
geo = {
|
| 295 |
+
"latitude": request.latitude,
|
| 296 |
+
"longitude": request.longitude,
|
| 297 |
+
"city": request.city or "Libreville"
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
# Mise à jour du contexte conversationnel en temps réel
|
| 301 |
+
user_emb = embed_model.encode([user_message], normalize_embeddings=True).astype('float32')
|
| 302 |
+
conversation_ctx.update_subject(user_message, user_emb)
|
| 303 |
+
|
| 304 |
+
# 1. RAG Documents PDF
|
| 305 |
+
rag_context = ""
|
| 306 |
+
rag_sources = []
|
| 307 |
+
if doc_index.ntotal > 0 and len(doc_chunks) > 0:
|
| 308 |
+
D, I = doc_index.search(user_emb, k=5)
|
| 309 |
+
relevant_chunks = []
|
| 310 |
+
for idx in I[0]:
|
| 311 |
+
if 0 <= idx < len(doc_chunks):
|
| 312 |
+
relevant_chunks.append(doc_chunks[idx][:1000])
|
| 313 |
+
if idx < len(doc_metadata):
|
| 314 |
+
rag_sources.append(doc_metadata[idx].get("source", "PDF"))
|
| 315 |
+
if relevant_chunks:
|
| 316 |
+
rag_context = "\n\n".join([f"Document : {chunk}" for chunk in relevant_chunks])
|
| 317 |
+
|
| 318 |
+
# 2. Mémoire conversationnelle adaptative
|
| 319 |
+
memory_context = ""
|
| 320 |
+
memory_texts_filtered, memory_scores = retrieve_adaptive_memory(user_message, k=5)
|
| 321 |
+
if memory_texts_filtered:
|
| 322 |
+
memory_context = "\n\n".join([f"Mémoire (score: {score:.2f}): {text}"
|
| 323 |
+
for text, score in zip(memory_texts_filtered, memory_scores)])
|
| 324 |
+
logger.info(f"Mémoire récupérée: {len(memory_texts_filtered)} entrées (scores: {[f'{s:.2f}' for s in memory_scores]})")
|
| 325 |
+
|
| 326 |
+
# 3. Réflexion stratégique
|
| 327 |
+
if request.thinking_mode:
|
| 328 |
+
execute_reflection_plan(
|
| 329 |
+
user_message,
|
| 330 |
+
geo_info=geo,
|
| 331 |
+
messages=request.messages,
|
| 332 |
+
current_subject=conversation_ctx.current_subject,
|
| 333 |
+
subject_keywords=conversation_ctx.subject_keywords
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
# 4. Recherche Web enrichie
|
| 337 |
+
search_query = user_message
|
| 338 |
+
if conversation_ctx.subject_keywords:
|
| 339 |
+
search_query = f"{user_message} {' '.join(conversation_ctx.subject_keywords[:3])} Gabon"
|
| 340 |
+
|
| 341 |
+
search_results = web_search(search_query)
|
| 342 |
+
web_context = "\n".join([f"- {r['content'][:500]}" for r in search_results.get("results", [])[:6]])
|
| 343 |
+
web_images = search_results.get("images", [])[:4]
|
| 344 |
|
| 345 |
+
# 5. Prompt enrichi avec verrouillage contextuel
|
| 346 |
+
system_prompt = f"""Tu es Kibali, un assistant IA chaleureux, précis et expert du Gabon, basé à {geo['city']}.
|
| 347 |
Réponds toujours en français, de façon naturelle, concise et factuelle.
|
|
|
|
|
|
|
|
|
|
| 348 |
|
| 349 |
+
CONTEXTE CONVERSATIONNEL ACTUEL:
|
| 350 |
+
- Sujet en cours: {', '.join(conversation_ctx.subject_keywords) if conversation_ctx.subject_keywords else 'Nouveau sujet'}
|
| 351 |
+
- Nombre de messages sur ce sujet: {conversation_ctx.message_count}
|
| 352 |
+
|
| 353 |
+
PRIORITÉ DES SOURCES:
|
| 354 |
+
1. Documents uploadés (PDF Vault) - Source la plus fiable
|
| 355 |
+
2. Mémoire conversationnelle récente et pertinente
|
| 356 |
+
3. Informations Web actualisées
|
| 357 |
+
|
| 358 |
+
Si une information vient d'un document uploadé, mentionne-le brièvement.
|
| 359 |
+
Adapte-toi aux changements brusques de sujet en restant cohérent."""
|
| 360 |
+
|
| 361 |
+
full_prompt = f"""### INSTRUCTIONS STRICTES :
|
| 362 |
{system_prompt}
|
| 363 |
|
| 364 |
+
### CONTEXTE DOCUMENTS (PDF Vault) :
|
| 365 |
+
{rag_context if rag_context else "Aucun document pertinent trouvé."}
|
| 366 |
|
| 367 |
+
### HISTORIQUE PERTINENT (Mémoire adaptative) :
|
| 368 |
+
{memory_context if memory_context else "Pas d'historique pertinent."}
|
| 369 |
|
| 370 |
+
### INFORMATIONS WEB RÉCENTES :
|
| 371 |
+
{web_context if web_context else "Pas d'informations web disponibles."}
|
| 372 |
|
| 373 |
+
### QUESTION :
|
| 374 |
{user_message}
|
| 375 |
|
| 376 |
+
### RÉPONSE (en français uniquement) :
|
| 377 |
"""
|
| 378 |
|
| 379 |
+
inputs = tokenizer(full_prompt, return_tensors="pt", truncation=True, max_length=8192).to(model.device)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 380 |
|
| 381 |
+
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True, timeout=120.0)
|
| 382 |
|
| 383 |
+
def generate_stream():
|
| 384 |
+
try:
|
| 385 |
+
model.generate(
|
| 386 |
+
**inputs,
|
| 387 |
+
streamer=streamer,
|
| 388 |
+
max_new_tokens=1024,
|
| 389 |
+
temperature=0.6,
|
| 390 |
+
do_sample=True,
|
| 391 |
+
top_p=0.85,
|
| 392 |
+
top_k=50,
|
| 393 |
+
repetition_penalty=1.2,
|
| 394 |
+
length_penalty=0.8
|
| 395 |
+
)
|
| 396 |
+
except Exception as e:
|
| 397 |
+
logger.error(f"Erreur génération : {e}")
|
| 398 |
+
|
| 399 |
+
thread = Thread(target=generate_stream)
|
| 400 |
+
thread.start()
|
| 401 |
+
|
| 402 |
+
response_text = ""
|
| 403 |
+
for new_text in streamer:
|
| 404 |
+
if new_text is not None:
|
| 405 |
+
response_text += new_text
|
| 406 |
+
response_text = response_text.strip()
|
| 407 |
+
|
| 408 |
+
# Ajout en temps réel à la mémoire
|
| 409 |
+
if response_text:
|
| 410 |
+
add_to_memory_realtime(
|
| 411 |
+
user_message,
|
| 412 |
+
response_text,
|
| 413 |
+
conversation_ctx.subject_keywords
|
| 414 |
+
)
|
| 415 |
+
|
| 416 |
+
# Informations contextuelles
|
| 417 |
+
context_info = {
|
| 418 |
+
"subject_keywords": conversation_ctx.subject_keywords,
|
| 419 |
+
"message_count": conversation_ctx.message_count,
|
| 420 |
+
"memory_used": len(memory_texts_filtered),
|
| 421 |
+
"rag_sources": list(set(rag_sources)),
|
| 422 |
+
"web_results": len(search_results.get("results", []))
|
| 423 |
+
}
|
| 424 |
|
| 425 |
+
return ChatResponse(response=response_text, images=web_images, context_info=context_info)
|
| 426 |
|
| 427 |
+
@app.post("/upload")
|
| 428 |
+
async def upload(files: List[UploadFile] = File(...)):
|
| 429 |
+
total_added = 0
|
| 430 |
+
processed_files = 0
|
|
|
|
| 431 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
for file in files:
|
| 433 |
if not file.filename.lower().endswith(".pdf"):
|
|
|
|
| 434 |
continue
|
| 435 |
+
|
| 436 |
try:
|
| 437 |
content = await file.read()
|
| 438 |
text = extract_text_from_pdf(content)
|
| 439 |
+
|
| 440 |
+
if not text:
|
| 441 |
+
logger.warning(f"Aucun texte extrait de {file.filename}")
|
| 442 |
+
continue
|
| 443 |
+
|
| 444 |
+
chunks = chunk_text(text)
|
| 445 |
+
if not chunks:
|
| 446 |
+
continue
|
| 447 |
+
|
| 448 |
+
# Métadonnées pour chaque chunk
|
| 449 |
+
timestamp = datetime.now().isoformat()
|
| 450 |
+
for chunk in chunks:
|
| 451 |
+
doc_metadata.append({
|
| 452 |
+
"source": file.filename,
|
| 453 |
+
"timestamp": timestamp,
|
| 454 |
+
"length": len(chunk)
|
| 455 |
+
})
|
| 456 |
+
|
| 457 |
+
embeddings = embed_model.encode(chunks, normalize_embeddings=True).astype('float32')
|
| 458 |
+
doc_index.add(embeddings)
|
| 459 |
+
doc_chunks.extend(chunks)
|
| 460 |
+
|
| 461 |
+
total_added += len(chunks)
|
| 462 |
+
processed_files += 1
|
| 463 |
+
|
| 464 |
+
logger.info(f"Upload réussi : {file.filename} → {len(chunks)} chunks ajoutés")
|
| 465 |
+
|
| 466 |
except Exception as e:
|
| 467 |
+
logger.error(f"Erreur lors du traitement de {file.filename} : {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
|
|
|
|
|
|
|
|
|
|
| 469 |
return {
|
| 470 |
+
"status": "success",
|
| 471 |
+
"files_processed": processed_files,
|
| 472 |
+
"chunks_added": total_added,
|
| 473 |
+
"total_doc_chunks": len(doc_chunks)
|
| 474 |
}
|
| 475 |
|
| 476 |
+
@app.post("/upload-pdfs")
|
| 477 |
+
async def upload_pdfs(files: List[UploadFile] = File(...)):
|
| 478 |
+
return await upload(files)
|
| 479 |
+
|
| 480 |
+
@app.post("/clear-memory")
|
| 481 |
+
async def clear_memory():
|
| 482 |
+
"""Efface la mémoire conversationnelle"""
|
| 483 |
+
global memory_index, memory_texts, memory_metadata
|
| 484 |
+
memory_index = faiss.IndexFlatL2(dimension)
|
| 485 |
+
memory_texts = []
|
| 486 |
+
memory_metadata = []
|
| 487 |
+
conversation_ctx.__init__()
|
| 488 |
+
return {"status": "memory_cleared", "message": "Mémoire conversationnelle effacée"}
|
| 489 |
+
|
| 490 |
+
# --- DEMARRAGE ---
|
| 491 |
@app.on_event("startup")
|
| 492 |
async def startup_event():
|
| 493 |
logger.info("🚀 Kibali AI API démarrée avec succès !")
|
| 494 |
+
logger.info(f"Accès : http://localhost:8000 | Docs : http://localhost:8000/docs")
|
| 495 |
+
logger.info(f"Mémoire adaptative et réflexion contextuelle activées ✓")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -1,21 +1,22 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
triton==3.6.0+git9844da95
|
| 5 |
transformers==4.41.2
|
| 6 |
bitsandbytes>=0.41.0
|
| 7 |
accelerate
|
| 8 |
sentence-transformers
|
| 9 |
faiss-gpu
|
|
|
|
|
|
|
| 10 |
fastapi
|
| 11 |
uvicorn[standard]
|
| 12 |
pydantic
|
| 13 |
python-multipart
|
| 14 |
-
|
| 15 |
-
|
| 16 |
pypdf>=3.0.0
|
| 17 |
-
numpy<2
|
| 18 |
folium
|
| 19 |
duckduckgo-search
|
| 20 |
huggingface_hub==0.23.4
|
| 21 |
-
spaces
|
|
|
|
| 1 |
+
# --- Core IA (Versions Stables) ---
|
| 2 |
+
# Note: On ne met pas de version figée pour torch ici,
|
| 3 |
+
# car on l'installe via l'URL spécifique dans le Dockerfile.
|
|
|
|
| 4 |
transformers==4.41.2
|
| 5 |
bitsandbytes>=0.41.0
|
| 6 |
accelerate
|
| 7 |
sentence-transformers
|
| 8 |
faiss-gpu
|
| 9 |
+
|
| 10 |
+
# --- Serveur & API ---
|
| 11 |
fastapi
|
| 12 |
uvicorn[standard]
|
| 13 |
pydantic
|
| 14 |
python-multipart
|
| 15 |
+
|
| 16 |
+
# --- Outils & Data ---
|
| 17 |
pypdf>=3.0.0
|
| 18 |
+
numpy<2.0.0
|
| 19 |
folium
|
| 20 |
duckduckgo-search
|
| 21 |
huggingface_hub==0.23.4
|
| 22 |
+
spaces
|
tools/todo.py
CHANGED
|
@@ -1,41 +1,253 @@
|
|
| 1 |
-
# tools/todo.py
|
| 2 |
import streamlit as st
|
| 3 |
import time
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
def
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
|
| 12 |
-
# Si le prompt est court, on récupère le sujet du dernier message
|
| 13 |
-
subject = prompt
|
| 14 |
-
is_continuation = len(prompt.split()) < 5 or any(x in prompt.lower() for x in ["ils", "elles", "donc", "alors", "ceux-là"])
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
-
|
| 37 |
-
st.write(f"
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import time
|
| 3 |
+
from typing import List, Optional
|
| 4 |
+
import re
|
| 5 |
|
| 6 |
+
def analyze_query_type(prompt: str) -> dict:
|
| 7 |
+
"""Analyse le type de requête pour adapter la stratégie de réflexion"""
|
| 8 |
+
prompt_lower = prompt.lower()
|
| 9 |
+
|
| 10 |
+
analysis = {
|
| 11 |
+
"type": "general",
|
| 12 |
+
"needs_web": False,
|
| 13 |
+
"needs_memory": False,
|
| 14 |
+
"needs_docs": False,
|
| 15 |
+
"complexity": "simple",
|
| 16 |
+
"temporal": False,
|
| 17 |
+
"geographical": False
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
# Détection de questions temporelles
|
| 21 |
+
temporal_keywords = ["aujourd'hui", "maintenant", "récent", "actuel", "dernier", "2024", "2025"]
|
| 22 |
+
if any(kw in prompt_lower for kw in temporal_keywords):
|
| 23 |
+
analysis["temporal"] = True
|
| 24 |
+
analysis["needs_web"] = True
|
| 25 |
+
|
| 26 |
+
# Détection géographique
|
| 27 |
+
geo_keywords = ["gabon", "libreville", "port-gentil", "franceville", "oyem", "où", "localisation"]
|
| 28 |
+
if any(kw in prompt_lower for kw in geo_keywords):
|
| 29 |
+
analysis["geographical"] = True
|
| 30 |
+
|
| 31 |
+
# Détection de questions sur documents
|
| 32 |
+
doc_keywords = ["selon le document", "d'après le pdf", "dans le fichier", "uploadé"]
|
| 33 |
+
if any(kw in prompt_lower for kw in doc_keywords):
|
| 34 |
+
analysis["needs_docs"] = True
|
| 35 |
+
analysis["type"] = "document_query"
|
| 36 |
+
|
| 37 |
+
# Détection de continuation de conversation
|
| 38 |
+
continuation_keywords = ["ils", "elles", "lui", "leur", "donc", "alors", "ensuite", "aussi", "également"]
|
| 39 |
+
if any(kw in prompt_lower for kw in continuation_keywords) or len(prompt.split()) < 5:
|
| 40 |
+
analysis["needs_memory"] = True
|
| 41 |
+
analysis["type"] = "continuation"
|
| 42 |
+
|
| 43 |
+
# Détection de complexité
|
| 44 |
+
if len(prompt.split()) > 15 or "?" in prompt and prompt.count("?") > 1:
|
| 45 |
+
analysis["complexity"] = "complex"
|
| 46 |
+
elif "pourquoi" in prompt_lower or "comment" in prompt_lower or "expliquer" in prompt_lower:
|
| 47 |
+
analysis["complexity"] = "medium"
|
| 48 |
+
|
| 49 |
+
# Questions nécessitant le web
|
| 50 |
+
web_keywords = ["actualité", "news", "prix", "cours", "météo", "horaire"]
|
| 51 |
+
if any(kw in prompt_lower for kw in web_keywords):
|
| 52 |
+
analysis["needs_web"] = True
|
| 53 |
+
analysis["type"] = "real_time"
|
| 54 |
+
|
| 55 |
+
return analysis
|
| 56 |
+
|
| 57 |
+
def detect_subject_shift(prompt: str, current_subject: str, subject_keywords: List[str]) -> dict:
|
| 58 |
+
"""Détecte un changement de sujet et évalue la force du changement"""
|
| 59 |
+
if not current_subject or not subject_keywords:
|
| 60 |
+
return {
|
| 61 |
+
"shift_detected": False,
|
| 62 |
+
"shift_strength": 0.0,
|
| 63 |
+
"new_subject_detected": True,
|
| 64 |
+
"reason": "Premier message ou pas de sujet actuel"
|
| 65 |
+
}
|
| 66 |
|
| 67 |
+
prompt_lower = prompt.lower()
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
+
# Calcul de l'overlap des mots-clés
|
| 70 |
+
prompt_words = set(re.findall(r'\b\w{4,}\b', prompt_lower))
|
| 71 |
+
keyword_overlap = len(prompt_words.intersection(set(subject_keywords)))
|
| 72 |
+
overlap_ratio = keyword_overlap / max(len(subject_keywords), 1)
|
| 73 |
+
|
| 74 |
+
# Détection de marqueurs de changement de sujet
|
| 75 |
+
shift_markers = ["maintenant", "sinon", "autre chose", "parlons de", "passons à", "nouveau sujet"]
|
| 76 |
+
has_shift_marker = any(marker in prompt_lower for marker in shift_markers)
|
| 77 |
+
|
| 78 |
+
# Calcul de la force du changement
|
| 79 |
+
shift_strength = 0.0
|
| 80 |
+
if overlap_ratio < 0.2:
|
| 81 |
+
shift_strength += 0.5
|
| 82 |
+
if has_shift_marker:
|
| 83 |
+
shift_strength += 0.3
|
| 84 |
+
if len(prompt_words) > 5 and keyword_overlap == 0:
|
| 85 |
+
shift_strength += 0.2
|
| 86 |
+
|
| 87 |
+
shift_detected = shift_strength > 0.4
|
| 88 |
+
|
| 89 |
+
return {
|
| 90 |
+
"shift_detected": shift_detected,
|
| 91 |
+
"shift_strength": shift_strength,
|
| 92 |
+
"new_subject_detected": shift_strength > 0.6,
|
| 93 |
+
"keyword_overlap": keyword_overlap,
|
| 94 |
+
"overlap_ratio": overlap_ratio,
|
| 95 |
+
"reason": f"Overlap: {overlap_ratio:.1%}, Marqueurs: {has_shift_marker}"
|
| 96 |
+
}
|
| 97 |
|
| 98 |
+
def generate_search_strategy(analysis: dict, subject_keywords: List[str], geo_info: dict) -> dict:
|
| 99 |
+
"""Génère une stratégie de recherche optimisée"""
|
| 100 |
+
strategy = {
|
| 101 |
+
"use_rag": analysis["needs_docs"],
|
| 102 |
+
"use_memory": analysis["needs_memory"] or analysis["type"] == "continuation",
|
| 103 |
+
"use_web": analysis["needs_web"] or analysis["temporal"],
|
| 104 |
+
"memory_k": 5,
|
| 105 |
+
"rag_k": 3,
|
| 106 |
+
"web_enhanced": False,
|
| 107 |
+
"search_query_suffix": ""
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
# Ajustement selon la complexité
|
| 111 |
+
if analysis["complexity"] == "complex":
|
| 112 |
+
strategy["memory_k"] = 8
|
| 113 |
+
strategy["rag_k"] = 5
|
| 114 |
+
elif analysis["complexity"] == "simple":
|
| 115 |
+
strategy["memory_k"] = 3
|
| 116 |
+
strategy["rag_k"] = 2
|
| 117 |
+
|
| 118 |
+
# Enrichissement de la recherche web
|
| 119 |
+
if analysis["needs_web"]:
|
| 120 |
+
strategy["web_enhanced"] = True
|
| 121 |
+
if subject_keywords:
|
| 122 |
+
strategy["search_query_suffix"] = f" {' '.join(subject_keywords[:3])}"
|
| 123 |
+
if analysis["geographical"]:
|
| 124 |
+
strategy["search_query_suffix"] += f" {geo_info.get('city', 'Gabon')}"
|
| 125 |
+
|
| 126 |
+
return strategy
|
| 127 |
|
| 128 |
+
def execute_reflection_plan(
|
| 129 |
+
prompt: str,
|
| 130 |
+
geo_info: Optional[dict] = None,
|
| 131 |
+
messages: Optional[List] = None,
|
| 132 |
+
current_subject: Optional[str] = None,
|
| 133 |
+
subject_keywords: Optional[List[str]] = None
|
| 134 |
+
):
|
| 135 |
+
"""
|
| 136 |
+
Phase de réflexion structurée avec analyse contextuelle avancée et adaptation dynamique.
|
| 137 |
+
"""
|
| 138 |
+
if geo_info is None:
|
| 139 |
+
geo_info = {}
|
| 140 |
+
if messages is None:
|
| 141 |
+
messages = []
|
| 142 |
+
if subject_keywords is None:
|
| 143 |
+
subject_keywords = []
|
| 144 |
+
|
| 145 |
+
# ÉTAPE 1: Analyse du type de requête
|
| 146 |
+
query_analysis = analyze_query_type(prompt)
|
| 147 |
+
|
| 148 |
+
# ÉTAPE 2: Détection de changement de sujet
|
| 149 |
+
subject_shift = detect_subject_shift(prompt, current_subject, subject_keywords)
|
| 150 |
+
|
| 151 |
+
# ÉTAPE 3: Génération de la stratégie de recherche
|
| 152 |
+
search_strategy = generate_search_strategy(query_analysis, subject_keywords, geo_info)
|
| 153 |
+
|
| 154 |
+
# ÉTAPE 4: Affichage de la réflexion (si Streamlit disponible)
|
| 155 |
+
try:
|
| 156 |
+
location = f"{geo_info.get('city', 'Libreville')}, {geo_info.get('country', 'Gabon')}"
|
| 157 |
|
| 158 |
+
with st.status("🧠 Kibali Thinking Engine", expanded=True) as status:
|
| 159 |
+
st.write(f"🌍 **Localisation active :** {location}")
|
| 160 |
+
st.write("")
|
| 161 |
+
|
| 162 |
+
# Analyse du type de requête
|
| 163 |
+
st.write("### 📊 Analyse de la requête")
|
| 164 |
+
st.write(f"- **Type :** {query_analysis['type'].replace('_', ' ').title()}")
|
| 165 |
+
st.write(f"- **Complexité :** {query_analysis['complexity'].title()}")
|
| 166 |
+
|
| 167 |
+
if query_analysis['temporal']:
|
| 168 |
+
st.write("- ⏰ **Dimension temporelle détectée** → Recherche web activée")
|
| 169 |
+
if query_analysis['geographical']:
|
| 170 |
+
st.write(f"- 🗺️ **Contexte géographique :** {location}")
|
| 171 |
+
|
| 172 |
+
time.sleep(0.2)
|
| 173 |
+
st.write("")
|
| 174 |
+
|
| 175 |
+
# Détection de changement de sujet
|
| 176 |
+
st.write("### 🔄 Analyse du contexte conversationnel")
|
| 177 |
+
if subject_shift['shift_detected']:
|
| 178 |
+
if subject_shift['new_subject_detected']:
|
| 179 |
+
st.write("- 🆕 **Nouveau sujet détecté** → Rafraîchissement du contexte")
|
| 180 |
+
else:
|
| 181 |
+
st.write(f"- ⚠️ **Changement partiel** (force: {subject_shift['shift_strength']:.0%})")
|
| 182 |
+
st.write(f" *Raison : {subject_shift['reason']}*")
|
| 183 |
+
else:
|
| 184 |
+
st.write("- ✅ **Continuité du sujet actuel**")
|
| 185 |
+
if subject_keywords:
|
| 186 |
+
st.write(f" *Mots-clés actifs : {', '.join(subject_keywords[:5])}*")
|
| 187 |
+
st.write(f" *Overlap : {subject_shift['keyword_overlap']}/{len(subject_keywords)} mots-clés*")
|
| 188 |
+
|
| 189 |
+
time.sleep(0.2)
|
| 190 |
+
st.write("")
|
| 191 |
+
|
| 192 |
+
# Stratégie de recherche
|
| 193 |
+
st.write("### 🎯 Stratégie de réponse")
|
| 194 |
+
sources = []
|
| 195 |
+
if search_strategy['use_rag']:
|
| 196 |
+
sources.append(f"📚 Documents PDF (top {search_strategy['rag_k']})")
|
| 197 |
+
if search_strategy['use_memory']:
|
| 198 |
+
sources.append(f"🧠 Mémoire conversationnelle (top {search_strategy['memory_k']})")
|
| 199 |
+
if search_strategy['use_web']:
|
| 200 |
+
web_label = "🌐 Recherche web"
|
| 201 |
+
if search_strategy['web_enhanced']:
|
| 202 |
+
web_label += " (enrichie avec contexte)"
|
| 203 |
+
sources.append(web_label)
|
| 204 |
+
|
| 205 |
+
if not sources:
|
| 206 |
+
sources.append("💭 Connaissance générale du modèle")
|
| 207 |
|
| 208 |
+
for i, source in enumerate(sources, 1):
|
| 209 |
+
st.write(f"{i}. {source}")
|
| 210 |
+
time.sleep(0.15)
|
| 211 |
+
|
| 212 |
+
st.write("")
|
| 213 |
+
|
| 214 |
+
# Plan d'action détaillé
|
| 215 |
+
st.write("### ⚙️ Plan d'exécution")
|
| 216 |
+
steps = []
|
| 217 |
+
|
| 218 |
+
if search_strategy['use_rag']:
|
| 219 |
+
steps.append("Extraction des chunks pertinents depuis la base vectorielle PDF")
|
| 220 |
+
|
| 221 |
+
if search_strategy['use_memory']:
|
| 222 |
+
steps.append("Récupération des échanges similaires avec scoring de pertinence")
|
| 223 |
+
|
| 224 |
+
if search_strategy['use_web']:
|
| 225 |
+
query_suffix = search_strategy['search_query_suffix']
|
| 226 |
+
steps.append(f"Requête web : '{prompt[:50]}...{query_suffix}'")
|
| 227 |
+
|
| 228 |
+
steps.append("Synthèse des sources avec priorisation hiérarchique")
|
| 229 |
+
steps.append("Génération de la réponse avec verrouillage contextuel")
|
| 230 |
+
|
| 231 |
+
for i, step in enumerate(steps, 1):
|
| 232 |
+
st.write(f"{i}. {step}")
|
| 233 |
+
time.sleep(0.15)
|
| 234 |
+
|
| 235 |
+
time.sleep(0.3)
|
| 236 |
+
status.update(
|
| 237 |
+
label="✅ Stratégie validée - Génération en cours",
|
| 238 |
+
state="complete",
|
| 239 |
+
expanded=False
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
except Exception as e:
|
| 243 |
+
# Fallback si Streamlit n'est pas disponible
|
| 244 |
+
print(f"[Kibali Thinking] Type: {query_analysis['type']}, Complexité: {query_analysis['complexity']}")
|
| 245 |
+
print(f"[Kibali Thinking] Changement de sujet: {subject_shift['shift_detected']} (force: {subject_shift['shift_strength']:.0%})")
|
| 246 |
+
print(f"[Kibali Thinking] Sources: RAG={search_strategy['use_rag']}, Memory={search_strategy['use_memory']}, Web={search_strategy['use_web']}")
|
| 247 |
+
|
| 248 |
+
return {
|
| 249 |
+
"analysis": query_analysis,
|
| 250 |
+
"subject_shift": subject_shift,
|
| 251 |
+
"strategy": search_strategy,
|
| 252 |
+
"execution_plan_ready": True
|
| 253 |
+
}
|