@imenam/mcp-messenger 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -8
- package/dist/src/cli/watch.js +1 -1
- package/dist/src/client/messenger-client.d.ts +8 -1
- package/dist/src/client/messenger-client.js +7 -2
- package/dist/src/core/types.d.ts +15 -0
- package/dist/src/mcp/tools.js +15 -1
- package/dist/src/server/app.js +13 -2
- package/dist/src/server/watchers.d.ts +10 -0
- package/dist/src/server/watchers.js +69 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -143,11 +143,25 @@ n'hérite pas du bloc `env` du `.mcp.json`, donc ni de `MESSENGER_SERVER_URL` ni
|
|
|
143
143
|
Le fichier est nommé par hachage de l'adresse, supprimé à la fin de la session, et purgé au
|
|
144
144
|
bout de sept jours si la session s'est arrêtée brutalement.
|
|
145
145
|
|
|
146
|
+
**Savoir si c'est vraiment armé.** L'armement dépend de l'agent : rien ne peut le déclencher
|
|
147
|
+
à sa place, et un agent qui saute l'étape se croirait joignable sans l'être. Le serveur suit
|
|
148
|
+
donc les attentes tenues par un veilleur, et `whoami` le dit :
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
Notifications: armed — incoming messages will interrupt you (watcher since 2026-09-07T13:31:00.203Z).
|
|
152
|
+
Notifications: NOT armed — no incoming message will reach you. Call enable_notifications.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
C'est une présence de connexion, pas une donnée stockée : elle vit en mémoire du serveur et
|
|
156
|
+
disparaît avec lui. Un `wait_for_message` ordinaire ne compte pas — il s'arrête au premier
|
|
157
|
+
message, un veilleur reste. Entre deux attentes longues, un délai de grâce de deux minutes
|
|
158
|
+
évite que le signalement clignote. `enable_notifications` s'en sert aussi : si un veilleur est
|
|
159
|
+
déjà en place, il le dit et n'en fait pas armer un second, qui doublerait chaque notification.
|
|
160
|
+
|
|
146
161
|
**Ce que ça ne couvre pas.** Le veilleur n'annonce que les messages arrivés **après** son
|
|
147
162
|
démarrage : `enable_notifications` signale le nombre de non-lus déjà en boîte, à traiter avec
|
|
148
|
-
`list_messages`. Et
|
|
149
|
-
|
|
150
|
-
un rappel dans le `CLAUDE.md` du projet.
|
|
163
|
+
`list_messages`. Et `--claude-setup-mcp` écrit un rappel d'armement dans le `CLAUDE.md` du
|
|
164
|
+
projet, pour que l'étape ne soit pas oubliée en début de session.
|
|
151
165
|
|
|
152
166
|
**Sur un autre hôte que Claude Code.** `--watch` est une commande ordinaire : n'importe quel
|
|
153
167
|
superviseur capable de lire les lignes d'un processus peut la brancher. Sans relais, elle lit
|
|
@@ -159,7 +173,7 @@ superviseur capable de lire les lignes d'un processus peut la brancher. Sans rel
|
|
|
159
173
|
|
|
160
174
|
| Outil | Rôle |
|
|
161
175
|
|---|---|
|
|
162
|
-
| `whoami` | Adresse de cette session, serveur connecté, compteurs par dossier |
|
|
176
|
+
| `whoami` | Adresse de cette session, serveur connecté, veilleur armé ou non, compteurs par dossier |
|
|
163
177
|
| `attach_to_mailbox` | Se brancher sur une autre boîte via son adresse ; sans argument, retour à la sienne |
|
|
164
178
|
| `set_mailbox_label` | Nommer sa boîte, pour la reconnaître dans l'annuaire et la console |
|
|
165
179
|
| `list_agents` | Annuaire : qui est joignable, avec quelle description |
|
|
@@ -298,7 +312,7 @@ cet agent lorsque `MESSENGER_AGENT_TOKENS` est utilisé.
|
|
|
298
312
|
| `GET` | `/api/agents` | Annuaire des boîtes |
|
|
299
313
|
| `GET` | `/api/agents/:id` | Une boîte, ou 404 |
|
|
300
314
|
| `PATCH` | `/api/agents/me` | Renommer la boîte courante |
|
|
301
|
-
| `GET` | `/api/mailbox` | Compteurs de la boîte |
|
|
315
|
+
| `GET` | `/api/mailbox` | Compteurs de la boîte, et présence d'un veilleur (`watcher`) |
|
|
302
316
|
| `POST` | `/api/messages` | Envoyer un message |
|
|
303
317
|
| `GET` | `/api/messages` | Lister (`folder`, `unread_only`, `from`, `thread_id`, `search`, `limit`, `offset`) |
|
|
304
318
|
| `GET` | `/api/messages/:id` | Lire (`?mark_read=false` pour ne pas marquer) |
|
|
@@ -307,7 +321,7 @@ cet agent lorsque `MESSENGER_AGENT_TOKENS` est utilisé.
|
|
|
307
321
|
| `POST` | `/api/messages/delete` | Corbeille ou suppression définitive |
|
|
308
322
|
| `GET` | `/api/threads/:id` | Fil complet |
|
|
309
323
|
| `GET` `POST` `DELETE` | `/api/folders[/:name]` | Gérer les dossiers |
|
|
310
|
-
| `GET` | `/api/wait` | Attente longue (`timeout_seconds`, `folder`) |
|
|
324
|
+
| `GET` | `/api/wait` | Attente longue (`timeout_seconds`, `folder`, `watcher`) |
|
|
311
325
|
| `GET` | `/admin/overview` · `/admin/messages[/:id]` · `/admin/threads/:id` | Supervision, lecture seule |
|
|
312
326
|
|
|
313
327
|
---
|
|
@@ -355,6 +369,7 @@ src/
|
|
|
355
369
|
logger.ts # journalisation sur stderr (stdout est réservé au JSON-RPC)
|
|
356
370
|
core/store.ts # SQLite : agents, messages, livraisons, dossiers
|
|
357
371
|
server/app.ts # API HTTP, authentification, supervision
|
|
372
|
+
server/watchers.ts # présence des veilleurs, en mémoire : état de connexion
|
|
358
373
|
server/start.ts # démarrage, arrêt propre, purge de la corbeille
|
|
359
374
|
server/ui/ # console web (HTML/CSS/JS sans dépendance)
|
|
360
375
|
cli/watch.ts # mode veilleur : une ligne sur stdout par message entrant
|
|
@@ -366,7 +381,7 @@ test/ # store, config, API HTTP, outils MCP, binaire réel
|
|
|
366
381
|
|
|
367
382
|
### Tests
|
|
368
383
|
|
|
369
|
-
|
|
384
|
+
112 tests couvrant : le stockage et l'isolation des boîtes, la validation de configuration,
|
|
370
385
|
l'API HTTP et son authentification (refus systématique sans jeton, jeton partagé, jetons par
|
|
371
386
|
agent, jeton de console distinct), les outils MCP de bout en bout (deux agents qui
|
|
372
387
|
conversent à travers un vrai serveur), l'allocation des boîtes par session — deux processus
|
|
@@ -376,7 +391,8 @@ troisième session reprend la boîte de l'une d'elles — et le binaire publié
|
|
|
376
391
|
La notification passive est couverte de bout en bout : le relais vers le veilleur (isolation
|
|
377
392
|
entre boîtes, adresse jamais transformée en chemin, nettoyage), le format d'évènement (une
|
|
378
393
|
ligne, quel que soit le sujet), et un veilleur réellement lancé comme processus séparé, sans
|
|
379
|
-
jeton dans son environnement, qui émet sa ligne à l'arrivée d'un message
|
|
394
|
+
jeton dans son environnement, qui émet sa ligne à l'arrivée d'un message — et `whoami` qui
|
|
395
|
+
passe de « NOT armed » à « armed » quand ce veilleur prend son poste.
|
|
380
396
|
|
|
381
397
|
---
|
|
382
398
|
|
package/dist/src/cli/watch.js
CHANGED
|
@@ -63,7 +63,7 @@ export async function runWatchMode(mailbox) {
|
|
|
63
63
|
let backoff = BACKOFF_MIN_MS;
|
|
64
64
|
for (;;) {
|
|
65
65
|
try {
|
|
66
|
-
const result = await client.waitForMessage(POLL_SECONDS);
|
|
66
|
+
const result = await client.waitForMessage(POLL_SECONDS, undefined, { watcher: true });
|
|
67
67
|
backoff = BACKOFF_MIN_MS;
|
|
68
68
|
if (result.timed_out || !result.message)
|
|
69
69
|
continue;
|
|
@@ -80,6 +80,13 @@ export declare class MessengerClient {
|
|
|
80
80
|
listFolders(): Promise<FolderInfo[]>;
|
|
81
81
|
createFolder(name: string): Promise<FolderInfo>;
|
|
82
82
|
deleteFolder(name: string): Promise<number>;
|
|
83
|
-
|
|
83
|
+
/**
|
|
84
|
+
* `watcher` signale une attente tenue par un veilleur `--watch`, et non par un
|
|
85
|
+
* `wait_for_message` ponctuel : c'est ce qui permet au serveur de dire si la boîte est
|
|
86
|
+
* réellement surveillée.
|
|
87
|
+
*/
|
|
88
|
+
waitForMessage(timeoutSeconds: number, folder?: string, options?: {
|
|
89
|
+
watcher?: boolean;
|
|
90
|
+
}): Promise<WaitResult>;
|
|
84
91
|
private request;
|
|
85
92
|
}
|
|
@@ -139,10 +139,15 @@ export class MessengerClient {
|
|
|
139
139
|
const { moved } = await this.request("DELETE", `/api/folders/${encodeURIComponent(name)}`);
|
|
140
140
|
return moved;
|
|
141
141
|
}
|
|
142
|
-
|
|
142
|
+
/**
|
|
143
|
+
* `watcher` signale une attente tenue par un veilleur `--watch`, et non par un
|
|
144
|
+
* `wait_for_message` ponctuel : c'est ce qui permet au serveur de dire si la boîte est
|
|
145
|
+
* réellement surveillée.
|
|
146
|
+
*/
|
|
147
|
+
async waitForMessage(timeoutSeconds, folder, options = {}) {
|
|
143
148
|
// L'attente longue peut dépasser le délai HTTP par défaut : on l'étend d'une marge.
|
|
144
149
|
return this.request("GET", "/api/wait", {
|
|
145
|
-
query: { timeout_seconds: timeoutSeconds, folder },
|
|
150
|
+
query: { timeout_seconds: timeoutSeconds, folder, watcher: options.watcher ? "true" : undefined },
|
|
146
151
|
timeoutMs: timeoutSeconds * 1000 + this.config.timeoutMs,
|
|
147
152
|
});
|
|
148
153
|
}
|
package/dist/src/core/types.d.ts
CHANGED
|
@@ -67,8 +67,23 @@ export interface ListMessagesOptions {
|
|
|
67
67
|
limit?: number;
|
|
68
68
|
offset?: number;
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Présence d'un veilleur (`--watch`) sur une boîte, telle que la voit le serveur.
|
|
72
|
+
*
|
|
73
|
+
* `armed: false` veut dire que personne n'écoute : les messages arriveront en boîte,
|
|
74
|
+
* mais aucun agent n'en sera averti tant qu'il ne regarde pas de lui-même.
|
|
75
|
+
*/
|
|
76
|
+
export interface WatcherStatus {
|
|
77
|
+
armed: boolean;
|
|
78
|
+
/** Depuis quand ce veilleur est en place, ou null s'il n'y en a pas. */
|
|
79
|
+
since: string | null;
|
|
80
|
+
/** Dernière attente observée, y compris pour un veilleur qui vient de disparaître. */
|
|
81
|
+
last_seen: string | null;
|
|
82
|
+
}
|
|
70
83
|
export interface MailboxStats {
|
|
71
84
|
agent_id: string;
|
|
72
85
|
unread_total: number;
|
|
73
86
|
folders: FolderInfo[];
|
|
87
|
+
/** Absent des réponses du store : ajouté par le serveur, qui seul voit les connexions. */
|
|
88
|
+
watcher?: WatcherStatus;
|
|
74
89
|
}
|
package/dist/src/mcp/tools.js
CHANGED
|
@@ -240,7 +240,12 @@ export async function callTool(client, name, rawArgs) {
|
|
|
240
240
|
if (client.isAttachedElsewhere) {
|
|
241
241
|
lines.push(`Attached to another mailbox. This session's own mailbox is ${client.sessionAgentId}.`);
|
|
242
242
|
}
|
|
243
|
-
lines.push(`Mailbox server: ${client.serverUrl}${health ? ` (v${health.version}, ${health.agents} mailbox(es))` : " (unreachable health endpoint)"}`,
|
|
243
|
+
lines.push(`Mailbox server: ${client.serverUrl}${health ? ` (v${health.version}, ${health.agents} mailbox(es))` : " (unreachable health endpoint)"}`,
|
|
244
|
+
// Sans veilleur, un message arrive en boîte sans que personne n'en soit averti.
|
|
245
|
+
// Le dire ici est le seul moyen pour l'agent de s'en apercevoir.
|
|
246
|
+
stats.watcher?.armed
|
|
247
|
+
? `Notifications: armed — incoming messages will interrupt you (watcher since ${stats.watcher.since}).`
|
|
248
|
+
: "Notifications: NOT armed — no incoming message will reach you. Call enable_notifications.", `Unread (excluding trash): ${stats.unread_total}`, "Folders:", ...folders);
|
|
244
249
|
return lines.join("\n");
|
|
245
250
|
}
|
|
246
251
|
case "attach_to_mailbox": {
|
|
@@ -371,6 +376,15 @@ export async function callTool(client, name, rawArgs) {
|
|
|
371
376
|
writeHandoff({ mailbox, serverUrl: client.serverUrl, token: client.tokenForHandoff });
|
|
372
377
|
const stats = await client.stats().catch(() => null);
|
|
373
378
|
const pending = stats?.folders.find((f) => f.name === "inbox")?.unread ?? 0;
|
|
379
|
+
// Un veilleur déjà en place suffit : en armer un second doublerait chaque
|
|
380
|
+
// notification sans rien apporter.
|
|
381
|
+
if (stats?.watcher?.armed) {
|
|
382
|
+
return [
|
|
383
|
+
`Notifications are already live on mailbox \`${mailbox}\` (watcher armed since ${stats.watcher.since}).`,
|
|
384
|
+
"Nothing to do: keep working, incoming messages will interrupt you on their own.",
|
|
385
|
+
"Do not arm a second watcher — you would get every message twice.",
|
|
386
|
+
].join("\n");
|
|
387
|
+
}
|
|
374
388
|
const lines = [
|
|
375
389
|
`Notifications enabled for mailbox \`${mailbox}\`.`,
|
|
376
390
|
"",
|
package/dist/src/server/app.js
CHANGED
|
@@ -8,6 +8,7 @@ import { rateLimit } from "express-rate-limit";
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { MessengerError, normalizeAgentId } from "../core/store.js";
|
|
10
10
|
import { PRIORITIES } from "../core/types.js";
|
|
11
|
+
import { WatcherRegistry } from "./watchers.js";
|
|
11
12
|
import { APP_VERSION } from "../version.js";
|
|
12
13
|
import { logger } from "../logger.js";
|
|
13
14
|
const UI_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "ui");
|
|
@@ -51,6 +52,9 @@ const folderSchema = z.object({ name: z.string().trim().min(1).max(40) });
|
|
|
51
52
|
const waitQuerySchema = z.object({
|
|
52
53
|
timeout_seconds: z.coerce.number().int().min(1).optional(),
|
|
53
54
|
folder: z.string().trim().min(1).optional(),
|
|
55
|
+
// Distingue le veilleur d'un `wait_for_message` ordinaire : les deux tiennent la même
|
|
56
|
+
// attente longue, mais seul le premier prouve que l'agent est joignable en continu.
|
|
57
|
+
watcher: z.enum(["true", "1"]).optional(),
|
|
54
58
|
});
|
|
55
59
|
const adminListSchema = z.object({
|
|
56
60
|
search: z.string().trim().min(1).optional(),
|
|
@@ -61,6 +65,8 @@ const adminListSchema = z.object({
|
|
|
61
65
|
});
|
|
62
66
|
export function createApp({ store, config }) {
|
|
63
67
|
const app = express();
|
|
68
|
+
// Présence des veilleurs : état de connexion, donc en mémoire du processus.
|
|
69
|
+
const watchers = new WatcherRegistry();
|
|
64
70
|
app.disable("x-powered-by");
|
|
65
71
|
app.set("trust proxy", false);
|
|
66
72
|
app.use(helmet({
|
|
@@ -136,7 +142,7 @@ export function createApp({ store, config }) {
|
|
|
136
142
|
res.json({ agent });
|
|
137
143
|
});
|
|
138
144
|
api.get("/mailbox", (req, res) => {
|
|
139
|
-
res.json(store.stats(req.agentId));
|
|
145
|
+
res.json({ ...store.stats(req.agentId), watcher: watchers.status(req.agentId) });
|
|
140
146
|
});
|
|
141
147
|
api.post("/messages", (req, res) => {
|
|
142
148
|
const input = sendSchema.parse(req.body ?? {});
|
|
@@ -190,16 +196,20 @@ export function createApp({ store, config }) {
|
|
|
190
196
|
// l'appelant ; les non-lus déjà présents sont ignorés (sinon l'outil rendrait la main
|
|
191
197
|
// immédiatement tant que la boîte n'est pas vidée).
|
|
192
198
|
api.get("/wait", (req, res) => {
|
|
193
|
-
const { timeout_seconds, folder } = waitQuerySchema.parse(req.query);
|
|
199
|
+
const { timeout_seconds, folder, watcher } = waitQuerySchema.parse(req.query);
|
|
194
200
|
const agentId = req.agentId;
|
|
195
201
|
const timeoutSeconds = Math.min(timeout_seconds ?? 30, config.maxWaitSeconds);
|
|
196
202
|
if (folder && !store.listFolders(agentId).some((f) => f.name === folder)) {
|
|
197
203
|
throw new MessengerError(`unknown folder: ${folder}`, 404);
|
|
198
204
|
}
|
|
205
|
+
// Le relâchement doit couvrir les trois sorties possibles : message, expiration,
|
|
206
|
+
// client parti. Un compteur qui ne redescend pas ferait croire à un veilleur éternel.
|
|
207
|
+
const releaseWatcher = watcher ? watchers.begin(agentId) : () => { };
|
|
199
208
|
const done = (payload) => {
|
|
200
209
|
clearTimeout(timer);
|
|
201
210
|
store.off("message", onMessage);
|
|
202
211
|
req.off("close", onClose);
|
|
212
|
+
releaseWatcher();
|
|
203
213
|
if (!res.headersSent)
|
|
204
214
|
res.json(payload);
|
|
205
215
|
};
|
|
@@ -215,6 +225,7 @@ export function createApp({ store, config }) {
|
|
|
215
225
|
const onClose = () => {
|
|
216
226
|
clearTimeout(timer);
|
|
217
227
|
store.off("message", onMessage);
|
|
228
|
+
releaseWatcher();
|
|
218
229
|
};
|
|
219
230
|
const timer = setTimeout(() => done({ timed_out: true, message: null }), timeoutSeconds * 1000);
|
|
220
231
|
store.on("message", onMessage);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WatcherStatus } from "../core/types.js";
|
|
2
|
+
export declare class WatcherRegistry {
|
|
3
|
+
private readonly now;
|
|
4
|
+
private readonly presence;
|
|
5
|
+
/** L'horloge est injectable : le délai de grâce se teste sans faire attendre la suite. */
|
|
6
|
+
constructor(now?: () => number);
|
|
7
|
+
/** Déclare une attente de veilleur. La fonction rendue la clôt, et doit toujours être appelée. */
|
|
8
|
+
begin(agentId: string): () => void;
|
|
9
|
+
status(agentId: string): WatcherStatus;
|
|
10
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Présence des veilleurs, par boîte.
|
|
3
|
+
*
|
|
4
|
+
* L'armement du veilleur dépend de l'agent : rien ne peut le déclencher à sa place, et
|
|
5
|
+
* un agent qui saute l'étape se croit joignable sans l'être. Ce registre rend le fait
|
|
6
|
+
* observable — `whoami` peut alors répondre « armé » ou « pas armé » plutôt que rien.
|
|
7
|
+
*
|
|
8
|
+
* État de connexion, donc volatile : il vit en mémoire du processus serveur et disparaît
|
|
9
|
+
* avec lui. Le mettre en base ferait croire à un veilleur qui n'existe plus.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Un veilleur enchaîne des attentes longues ; entre deux, il n'est plus connecté pendant
|
|
13
|
+
* quelques millisecondes. Ce délai de grâce évite de le déclarer absent dans cet
|
|
14
|
+
* intervalle, ou juste après qu'un message a mis fin à son attente.
|
|
15
|
+
*/
|
|
16
|
+
const GRACE_MS = 2 * 60 * 1000;
|
|
17
|
+
export class WatcherRegistry {
|
|
18
|
+
now;
|
|
19
|
+
presence = new Map();
|
|
20
|
+
/** L'horloge est injectable : le délai de grâce se teste sans faire attendre la suite. */
|
|
21
|
+
constructor(now = Date.now) {
|
|
22
|
+
this.now = now;
|
|
23
|
+
}
|
|
24
|
+
/** Déclare une attente de veilleur. La fonction rendue la clôt, et doit toujours être appelée. */
|
|
25
|
+
begin(agentId) {
|
|
26
|
+
const now = this.now();
|
|
27
|
+
const current = this.presence.get(agentId);
|
|
28
|
+
// Une absence dépassant le délai de grâce commence une nouvelle série : `since`
|
|
29
|
+
// désigne le moment où ce veilleur-ci s'est armé, pas un ancêtre oublié.
|
|
30
|
+
const continuing = current && now - current.lastSeen <= GRACE_MS;
|
|
31
|
+
const entry = continuing
|
|
32
|
+
? { ...current, inflight: current.inflight + 1, lastSeen: now }
|
|
33
|
+
: { inflight: 1, since: now, lastSeen: now };
|
|
34
|
+
this.presence.set(agentId, entry);
|
|
35
|
+
let released = false;
|
|
36
|
+
return () => {
|
|
37
|
+
if (released)
|
|
38
|
+
return;
|
|
39
|
+
released = true;
|
|
40
|
+
const active = this.presence.get(agentId);
|
|
41
|
+
if (!active)
|
|
42
|
+
return;
|
|
43
|
+
// On garde l'entrée après la dernière attente : c'est elle qui porte le délai de
|
|
44
|
+
// grâce, le temps que le veilleur relance son tour suivant.
|
|
45
|
+
this.presence.set(agentId, {
|
|
46
|
+
...active,
|
|
47
|
+
inflight: Math.max(0, active.inflight - 1),
|
|
48
|
+
lastSeen: this.now(),
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
status(agentId) {
|
|
53
|
+
const now = this.now();
|
|
54
|
+
const entry = this.presence.get(agentId);
|
|
55
|
+
if (!entry)
|
|
56
|
+
return { armed: false, since: null, last_seen: null };
|
|
57
|
+
const armed = entry.inflight > 0 || now - entry.lastSeen <= GRACE_MS;
|
|
58
|
+
if (!armed) {
|
|
59
|
+
// Purge à la lecture : sans veilleur pour cette boîte, l'entrée n'a plus d'objet.
|
|
60
|
+
this.presence.delete(agentId);
|
|
61
|
+
return { armed: false, since: null, last_seen: new Date(entry.lastSeen).toISOString() };
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
armed: true,
|
|
65
|
+
since: new Date(entry.since).toISOString(),
|
|
66
|
+
last_seen: new Date(entry.lastSeen).toISOString(),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const APP_VERSION = "1.0.
|
|
1
|
+
export declare const APP_VERSION = "1.0.4";
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Généré par scripts/generate-version.js — ne pas modifier à la main.
|
|
2
|
-
export const APP_VERSION = "1.0.
|
|
2
|
+
export const APP_VERSION = "1.0.4";
|