@imenam/mcp-messenger 1.0.6 → 1.0.7
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/.env.example +6 -3
- package/README.md +40 -13
- package/dist/src/cli/setup-mcp.js +1 -1
- package/dist/src/cli/watch.js +2 -2
- package/dist/src/client/messenger-client.d.ts +2 -3
- package/dist/src/client/messenger-client.js +3 -3
- package/dist/src/config.d.ts +3 -2
- package/dist/src/config.js +2 -2
- package/dist/src/core/store.d.ts +23 -8
- package/dist/src/core/store.js +65 -42
- package/dist/src/core/types.d.ts +19 -9
- package/dist/src/core/types.js +2 -2
- package/dist/src/mcp/tools.js +102 -61
- package/dist/src/server/app.js +18 -14
- package/dist/src/server/ui/app.js +320 -318
- package/dist/src/server/ui/styles.css +5 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
package/dist/src/mcp/tools.js
CHANGED
|
@@ -25,24 +25,37 @@ export const TOOLS = [
|
|
|
25
25
|
},
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
name: "
|
|
29
|
-
description: "Give a plain-language name to this mailbox, or
|
|
28
|
+
name: "rename_mailbox",
|
|
29
|
+
description: "Give a plain-language name to this mailbox, or change it: what this session is working on, e.g. 'Développement des tests unitaires du parser'. Addresses are opaque: the name is what makes this session recognisable later, both for the other agents (list_agents) and for the user reading the web console. It does not touch the agent name, which says where the agent comes from (its project) and is set by the configuration.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
name: { type: "string", description: "Short name, one line. Say what the work is about, not who does it." },
|
|
34
|
+
description: { type: "string", description: "Optional longer explanation of what this mailbox is for." },
|
|
35
|
+
},
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "rename_thread",
|
|
41
|
+
description: "Give a plain-language name to a thread, or change it, e.g. 'Le test échoue sur un bug applicatif'. A thread has a single shared name: renaming it changes it for every participant, and the last caller wins. Use it when a thread was opened without a name, or when the discussion has drifted away from its original name.",
|
|
30
42
|
inputSchema: {
|
|
31
43
|
type: "object",
|
|
32
44
|
properties: {
|
|
33
|
-
label: { type: "string", description: "Short name, one line. Say what the work is about, not who does it." },
|
|
34
|
-
description: { type: "string", description: "Optional longer explanation of the mailbox or the thread." },
|
|
35
45
|
thread_id: {
|
|
36
46
|
type: "string",
|
|
37
|
-
description: "Thread to
|
|
47
|
+
description: "Thread to rename. You must be a participant of the thread.",
|
|
38
48
|
},
|
|
49
|
+
name: { type: "string", description: "Short name, one line. Say what the thread is about." },
|
|
50
|
+
description: { type: "string", description: "Optional longer explanation of the thread." },
|
|
39
51
|
},
|
|
52
|
+
required: ["thread_id"],
|
|
40
53
|
additionalProperties: false,
|
|
41
54
|
},
|
|
42
55
|
},
|
|
43
56
|
{
|
|
44
57
|
name: "list_threads",
|
|
45
|
-
description: "List the conversations visible from this mailbox, most recently active first, with their name, participants and unread count. This is the readable map of the ongoing work: prefer it over list_messages when you want to know what is being discussed rather than what arrived last. A thread with no name falls back to the subject of its opening message — name it with
|
|
58
|
+
description: "List the conversations visible from this mailbox, most recently active first, with their name, participants and unread count. This is the readable map of the ongoing work: prefer it over list_messages when you want to know what is being discussed rather than what arrived last. A thread with no name falls back to the subject of its opening message — name it with rename_thread.",
|
|
46
59
|
inputSchema: {
|
|
47
60
|
type: "object",
|
|
48
61
|
properties: {
|
|
@@ -55,29 +68,23 @@ export const TOOLS = [
|
|
|
55
68
|
},
|
|
56
69
|
{
|
|
57
70
|
name: "list_agents",
|
|
58
|
-
description: "List the agents known to the mailbox server, most recently active first, with their name and description. Use it to find the address of the agent you want to write to.",
|
|
71
|
+
description: "List the other agents known to the mailbox server, most recently active first, with their name and description. Your own mailbox is never listed — you cannot write to yourself. Use it to find the address of the agent you want to write to.",
|
|
59
72
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
60
73
|
},
|
|
61
74
|
{
|
|
62
75
|
name: "send_message",
|
|
63
|
-
description: "Send a message to one
|
|
76
|
+
description: "Send a message to one agent, identified by its mailbox address. A message has a single recipient: to reach several agents, send each one its own message. Set reply_to to answer an existing message and keep the thread together.",
|
|
64
77
|
inputSchema: {
|
|
65
78
|
type: "object",
|
|
66
79
|
properties: {
|
|
67
|
-
to: {
|
|
68
|
-
type: "array",
|
|
69
|
-
items: { type: "string" },
|
|
70
|
-
minItems: 1,
|
|
71
|
-
description: "Recipient agent ids (mailbox addresses).",
|
|
72
|
-
},
|
|
73
|
-
cc: { type: "array", items: { type: "string" }, description: "Agents to keep in copy." },
|
|
80
|
+
to: { type: "string", description: "Recipient agent id (mailbox address). Exactly one recipient." },
|
|
74
81
|
subject: { type: "string", description: "Short subject line." },
|
|
75
82
|
body: { type: "string", description: "Message body, plain text or markdown." },
|
|
76
83
|
priority: { type: "string", enum: [...PRIORITIES], description: "Defaults to normal." },
|
|
77
84
|
reply_to: { type: "string", description: "Id of the message being answered; the reply joins its thread." },
|
|
78
|
-
|
|
85
|
+
thread_name: {
|
|
79
86
|
type: "string",
|
|
80
|
-
description: "Plain-language name of the conversation, e.g. 'Résolution de l'échec du test de parsing'. Name the thread when you open it — you already know what it is about. On a reply it renames the thread for everyone
|
|
87
|
+
description: "Plain-language name of the conversation, e.g. 'Résolution de l'échec du test de parsing'. Name the thread when you open it — you already know what it is about. On a reply it renames the thread for everyone (same effect as rename_thread).",
|
|
81
88
|
},
|
|
82
89
|
thread_description: { type: "string", description: "Optional longer description of the thread." },
|
|
83
90
|
},
|
|
@@ -210,13 +217,12 @@ export const TOOLS = [
|
|
|
210
217
|
];
|
|
211
218
|
const schemas = {
|
|
212
219
|
send_message: z.object({
|
|
213
|
-
to: z.
|
|
214
|
-
cc: z.union([z.string(), z.array(z.string())]).optional(),
|
|
220
|
+
to: z.string().min(1),
|
|
215
221
|
subject: z.string().min(1),
|
|
216
222
|
body: z.string().min(1),
|
|
217
223
|
priority: z.enum(PRIORITIES).optional(),
|
|
218
224
|
reply_to: z.string().optional(),
|
|
219
|
-
|
|
225
|
+
thread_name: z.string().trim().min(1).max(200).optional(),
|
|
220
226
|
thread_description: z.string().trim().min(1).max(2000).optional(),
|
|
221
227
|
}),
|
|
222
228
|
list_messages: z.object({
|
|
@@ -238,6 +244,24 @@ const schemas = {
|
|
|
238
244
|
move_messages: z.object({ message_ids: z.array(z.string().min(1)).min(1), folder: z.string().min(1) }),
|
|
239
245
|
delete_messages: z.object({ message_ids: z.array(z.string().min(1)).min(1), permanent: z.boolean().optional() }),
|
|
240
246
|
attach_to_mailbox: z.object({ mailbox_id: z.string().trim().min(1).optional() }),
|
|
247
|
+
rename_mailbox: z
|
|
248
|
+
.object({
|
|
249
|
+
name: z.string().trim().min(1).max(200).optional(),
|
|
250
|
+
description: z.string().trim().min(1).max(2000).optional(),
|
|
251
|
+
})
|
|
252
|
+
.refine((v) => v.name !== undefined || v.description !== undefined, {
|
|
253
|
+
message: "provide `name` and/or `description`",
|
|
254
|
+
}),
|
|
255
|
+
rename_thread: z
|
|
256
|
+
.object({
|
|
257
|
+
thread_id: z.string().trim().min(1),
|
|
258
|
+
name: z.string().trim().min(1).max(200).optional(),
|
|
259
|
+
description: z.string().trim().min(1).max(2000).optional(),
|
|
260
|
+
})
|
|
261
|
+
.refine((v) => v.name !== undefined || v.description !== undefined, {
|
|
262
|
+
message: "provide `name` and/or `description`",
|
|
263
|
+
}),
|
|
264
|
+
// Ancien outil `set_label` : une seule entrée pour la boîte et le fil, `label` au lieu de `name`.
|
|
241
265
|
set_label: z
|
|
242
266
|
.object({
|
|
243
267
|
label: z.string().trim().min(1).max(200).optional(),
|
|
@@ -273,9 +297,10 @@ export async function callTool(client, name, rawArgs) {
|
|
|
273
297
|
const lines = [
|
|
274
298
|
`Mailbox address: ${client.agentId}`,
|
|
275
299
|
" ↳ give this address to another agent so it can write to you.",
|
|
300
|
+
`Agent: ${self?.agent_name ?? "unnamed (set MESSENGER_AGENT_NAME in the MCP configuration)"}`,
|
|
276
301
|
self?.display_name
|
|
277
302
|
? `Mailbox name: ${self.display_name}`
|
|
278
|
-
: "Mailbox name: none — call
|
|
303
|
+
: "Mailbox name: none — call rename_mailbox so the user can tell this session apart from the others.",
|
|
279
304
|
];
|
|
280
305
|
if (client.isAttachedElsewhere) {
|
|
281
306
|
lines.push(`Attached to another mailbox. This session's own mailbox is ${client.sessionAgentId}.`);
|
|
@@ -303,8 +328,9 @@ export async function callTool(client, name, rawArgs) {
|
|
|
303
328
|
client.setAgentId(target.id);
|
|
304
329
|
const attachedStats = await client.stats();
|
|
305
330
|
return [
|
|
306
|
-
`Attached to mailbox ${target.id}${target
|
|
307
|
-
target.
|
|
331
|
+
`Attached to mailbox ${target.id}${formatAgentNames(target) ? ` (${formatAgentNames(target)})` : ""}.`,
|
|
332
|
+
target.agent_description ? `Agent: ${target.agent_description}` : null,
|
|
333
|
+
target.description ? `Mailbox: ${target.description}` : null,
|
|
308
334
|
`Unread (excluding trash): ${attachedStats.unread_total}`,
|
|
309
335
|
client.sessionAgentId && client.sessionAgentId !== target.id
|
|
310
336
|
? `This session's own mailbox remains ${client.sessionAgentId} — attach_to_mailbox with no argument goes back to it.`
|
|
@@ -313,29 +339,24 @@ export async function callTool(client, name, rawArgs) {
|
|
|
313
339
|
.filter(Boolean)
|
|
314
340
|
.join("\n");
|
|
315
341
|
}
|
|
316
|
-
|
|
317
|
-
|
|
342
|
+
case "rename_mailbox": {
|
|
343
|
+
const input = schemas.rename_mailbox.parse(args);
|
|
344
|
+
return renameMailbox(client, input.name ?? null, input.description ?? null);
|
|
345
|
+
}
|
|
346
|
+
case "rename_thread": {
|
|
347
|
+
const input = schemas.rename_thread.parse(args);
|
|
348
|
+
return renameThread(client, input.thread_id, input.name ?? null, input.description ?? null);
|
|
349
|
+
}
|
|
350
|
+
// `set_label` et `set_mailbox_label` ne sont plus publiés, mais restent acceptés : une
|
|
351
|
+
// session en cours peut les avoir appris avant la mise à jour du serveur. Le mot « label »
|
|
352
|
+
// faisait penser à une étiquette, alors qu'il s'agissait du nom.
|
|
318
353
|
case "set_mailbox_label":
|
|
319
354
|
case "set_label": {
|
|
320
355
|
const input = schemas.set_label.parse(args);
|
|
321
356
|
if (input.thread_id) {
|
|
322
|
-
|
|
323
|
-
title: input.label ?? null,
|
|
324
|
-
description: input.description ?? null,
|
|
325
|
-
});
|
|
326
|
-
return [
|
|
327
|
-
`Thread ${thread.thread_id} is now named "${thread.title}".`,
|
|
328
|
-
thread.description ? `Description: ${thread.description}` : null,
|
|
329
|
-
"Every participant of the thread sees this name.",
|
|
330
|
-
]
|
|
331
|
-
.filter(Boolean)
|
|
332
|
-
.join("\n");
|
|
357
|
+
return renameThread(client, input.thread_id, input.label ?? null, input.description ?? null);
|
|
333
358
|
}
|
|
334
|
-
|
|
335
|
-
display_name: input.label ?? null,
|
|
336
|
-
description: input.description ?? null,
|
|
337
|
-
});
|
|
338
|
-
return `Mailbox ${agent.id} named "${agent.display_name}".`;
|
|
359
|
+
return renameMailbox(client, input.label ?? null, input.description ?? null);
|
|
339
360
|
}
|
|
340
361
|
case "list_threads": {
|
|
341
362
|
const input = schemas.list_threads.parse(args);
|
|
@@ -347,41 +368,44 @@ export async function callTool(client, name, rawArgs) {
|
|
|
347
368
|
case "list_agents": {
|
|
348
369
|
const agents = await client.listAgents();
|
|
349
370
|
if (agents.length === 0)
|
|
350
|
-
return "No agent
|
|
371
|
+
return "No other agent on this server yet.";
|
|
351
372
|
return agents
|
|
352
373
|
.map((agent) => {
|
|
353
|
-
const flags =
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const title = agent.display_name ? `${agent.id} — ${agent.display_name}` : agent.id;
|
|
374
|
+
const flags = agent.registered ? "" : "never connected";
|
|
375
|
+
const names = formatAgentNames(agent);
|
|
376
|
+
const title = names ? `${agent.id} — ${names}` : agent.id;
|
|
357
377
|
// L'annuaire est classé par cette date : l'afficher rend l'ordre lisible.
|
|
358
378
|
const seen = agent.last_activity_at ? `last active ${agent.last_activity_at}` : "never active";
|
|
359
|
-
return [
|
|
379
|
+
return [
|
|
380
|
+
`• ${title}${flags ? ` (${flags})` : ""}`,
|
|
381
|
+
` ${agent.agent_description ?? agent.description ?? "no description"}`,
|
|
382
|
+
` ${seen}`,
|
|
383
|
+
].join("\n");
|
|
360
384
|
})
|
|
361
385
|
.join("\n");
|
|
362
386
|
}
|
|
363
387
|
case "send_message": {
|
|
364
388
|
const input = schemas.send_message.parse(args);
|
|
365
389
|
const result = await client.sendMessage({
|
|
366
|
-
to:
|
|
367
|
-
cc: asArray(input.cc),
|
|
390
|
+
to: input.to,
|
|
368
391
|
subject: input.subject,
|
|
369
392
|
body: input.body,
|
|
370
393
|
priority: input.priority,
|
|
371
394
|
reply_to: input.reply_to ?? null,
|
|
372
|
-
|
|
395
|
+
// Le protocole HTTP et le stockage gardent `thread_title` ; seul l'outil dit `thread_name`.
|
|
396
|
+
thread_title: input.thread_name ?? null,
|
|
373
397
|
thread_description: input.thread_description ?? null,
|
|
374
398
|
});
|
|
375
399
|
const lines = [
|
|
376
400
|
`Message sent: ${result.message_id}`,
|
|
377
401
|
`Thread: ${formatThreadRef(result.thread_title, result.thread_id)}`,
|
|
378
|
-
`Delivered to: ${result.delivered_to
|
|
402
|
+
`Delivered to: ${result.delivered_to}`,
|
|
379
403
|
];
|
|
380
404
|
if (!result.thread_title && !input.reply_to) {
|
|
381
|
-
lines.push("This thread has no name yet —
|
|
405
|
+
lines.push("This thread has no name yet — rename_thread with its thread_id makes it readable for everyone.");
|
|
382
406
|
}
|
|
383
|
-
if (result.
|
|
384
|
-
lines.push(`Warning — never connected to this server, the message will wait in their mailbox
|
|
407
|
+
if (result.unknown_recipient) {
|
|
408
|
+
lines.push(`Warning — ${result.delivered_to} has never connected to this server, the message will wait in their mailbox.`);
|
|
385
409
|
}
|
|
386
410
|
return lines.join("\n");
|
|
387
411
|
}
|
|
@@ -497,10 +521,28 @@ export async function callTool(client, name, rawArgs) {
|
|
|
497
521
|
throw new Error(`Unknown tool: ${name}`);
|
|
498
522
|
}
|
|
499
523
|
}
|
|
500
|
-
function
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
524
|
+
async function renameMailbox(client, name, description) {
|
|
525
|
+
const agent = await client.updateProfile({ display_name: name, description });
|
|
526
|
+
return [
|
|
527
|
+
`Mailbox ${agent.id} named "${agent.display_name}".`,
|
|
528
|
+
agent.agent_name ? `The agent name stays "${agent.agent_name}"; both are shown together.` : null,
|
|
529
|
+
]
|
|
530
|
+
.filter(Boolean)
|
|
531
|
+
.join("\n");
|
|
532
|
+
}
|
|
533
|
+
/** `Agent · Boîte` : l'agent dit d'où l'on écrit, la boîte de quoi il est question. */
|
|
534
|
+
function formatAgentNames(agent) {
|
|
535
|
+
return [agent.agent_name, agent.display_name].filter(Boolean).join(" · ");
|
|
536
|
+
}
|
|
537
|
+
async function renameThread(client, threadId, name, description) {
|
|
538
|
+
const thread = await client.setThreadLabel(threadId, { title: name, description });
|
|
539
|
+
return [
|
|
540
|
+
`Thread ${thread.thread_id} is now named "${thread.title}".`,
|
|
541
|
+
thread.description ? `Description: ${thread.description}` : null,
|
|
542
|
+
"Every participant of the thread sees this name.",
|
|
543
|
+
]
|
|
544
|
+
.filter(Boolean)
|
|
545
|
+
.join("\n");
|
|
504
546
|
}
|
|
505
547
|
/** `titre (id)`, ou l'id seul tant que le fil n'a pas été nommé. */
|
|
506
548
|
function formatThreadRef(title, threadId) {
|
|
@@ -523,7 +565,7 @@ function formatSummary(message) {
|
|
|
523
565
|
const priority = message.priority === "normal" ? "" : ` [${message.priority}]`;
|
|
524
566
|
return [
|
|
525
567
|
`${flags} ${message.subject}${priority}`,
|
|
526
|
-
` from ${message.from} to ${message.to
|
|
568
|
+
` from ${message.from} to ${message.to || "—"} · ${message.created_at}`,
|
|
527
569
|
` ${message.preview}`,
|
|
528
570
|
` id ${message.message_id} · thread ${formatThreadRef(message.thread_title, message.thread_id)}`,
|
|
529
571
|
].join("\n");
|
|
@@ -532,8 +574,7 @@ function formatMessage(message) {
|
|
|
532
574
|
const header = [
|
|
533
575
|
`Subject: ${message.subject}`,
|
|
534
576
|
`From: ${message.from}`,
|
|
535
|
-
`To: ${message.to
|
|
536
|
-
message.cc.length ? `Cc: ${message.cc.join(", ")}` : null,
|
|
577
|
+
`To: ${message.to || "—"}`,
|
|
537
578
|
`Date: ${message.created_at}`,
|
|
538
579
|
`Priority: ${message.priority}`,
|
|
539
580
|
`Folder: ${message.folder}${message.read ? "" : " (unread)"}${message.starred ? " ★" : ""}`,
|
package/dist/src/server/app.js
CHANGED
|
@@ -14,15 +14,20 @@ import { logger } from "../logger.js";
|
|
|
14
14
|
const UI_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "ui");
|
|
15
15
|
const agentIdSchema = z.string().trim().min(1).max(128);
|
|
16
16
|
const idListSchema = z.array(z.string().trim().min(1)).min(1).max(200);
|
|
17
|
-
|
|
17
|
+
/** Nom et description de la boîte : ce que l'agent pose lui-même sur sa session. */
|
|
18
|
+
const mailboxProfileSchema = z.object({
|
|
18
19
|
display_name: z.string().trim().max(200).nullish(),
|
|
19
20
|
description: z.string().trim().max(2000).nullish(),
|
|
21
|
+
});
|
|
22
|
+
const registerSchema = mailboxProfileSchema.extend({
|
|
23
|
+
/** Identité de l'agent (le projet, typiquement) : vient de la configuration, pas de l'agent. */
|
|
24
|
+
agent_name: z.string().trim().max(200).nullish(),
|
|
25
|
+
agent_description: z.string().trim().max(2000).nullish(),
|
|
20
26
|
/** true = l'adresse doit être inconnue du serveur (allocation d'une boîte neuve). */
|
|
21
27
|
fresh: z.boolean().optional(),
|
|
22
28
|
});
|
|
23
29
|
const sendSchema = z.object({
|
|
24
|
-
to:
|
|
25
|
-
cc: z.union([agentIdSchema, z.array(agentIdSchema)]).optional(),
|
|
30
|
+
to: agentIdSchema,
|
|
26
31
|
subject: z.string().min(1).max(500),
|
|
27
32
|
body: z.string().min(1).max(200_000),
|
|
28
33
|
priority: z.enum(PRIORITIES).optional(),
|
|
@@ -191,16 +196,21 @@ export function createApp({ store, config }) {
|
|
|
191
196
|
}
|
|
192
197
|
const agent = store.ensureAgent(req.agentId, {
|
|
193
198
|
registered: true,
|
|
199
|
+
agent_name: input.agent_name ?? null,
|
|
200
|
+
agent_description: input.agent_description ?? null,
|
|
201
|
+
// Un client antérieur à la séparation agent / boîte n'envoie que ces deux-là.
|
|
194
202
|
display_name: input.display_name ?? null,
|
|
195
203
|
description: input.description ?? null,
|
|
196
204
|
});
|
|
197
205
|
res.json({ agent });
|
|
198
206
|
});
|
|
199
|
-
|
|
200
|
-
|
|
207
|
+
// L'agent ne se voit pas dans son propre annuaire : sa boîte est la seule interdite d'envoi.
|
|
208
|
+
api.get("/agents", (req, res) => {
|
|
209
|
+
res.json({ agents: store.listAgents(req.agentId) });
|
|
201
210
|
});
|
|
211
|
+
// Ne touche qu'à la boîte : l'identité de l'agent ne se renomme pas depuis une session.
|
|
202
212
|
api.patch("/agents/me", (req, res) => {
|
|
203
|
-
const input =
|
|
213
|
+
const input = mailboxProfileSchema.parse(req.body ?? {});
|
|
204
214
|
res.json({
|
|
205
215
|
agent: store.ensureAgent(req.agentId, {
|
|
206
216
|
registered: true,
|
|
@@ -222,8 +232,7 @@ export function createApp({ store, config }) {
|
|
|
222
232
|
const input = sendSchema.parse(req.body ?? {});
|
|
223
233
|
const result = store.sendMessage({
|
|
224
234
|
from: req.agentId,
|
|
225
|
-
to:
|
|
226
|
-
cc: toArray(input.cc),
|
|
235
|
+
to: input.to,
|
|
227
236
|
subject: input.subject,
|
|
228
237
|
body: input.body,
|
|
229
238
|
priority: input.priority,
|
|
@@ -231,7 +240,7 @@ export function createApp({ store, config }) {
|
|
|
231
240
|
thread_title: input.thread_title ?? null,
|
|
232
241
|
thread_description: input.thread_description ?? null,
|
|
233
242
|
});
|
|
234
|
-
logger.info(`message ${result.message_id}: ${req.agentId} -> ${result.delivered_to
|
|
243
|
+
logger.info(`message ${result.message_id}: ${req.agentId} -> ${result.delivered_to}`);
|
|
235
244
|
res.status(201).json(result);
|
|
236
245
|
});
|
|
237
246
|
api.get("/messages", (req, res) => {
|
|
@@ -337,11 +346,6 @@ export function createApp({ store, config }) {
|
|
|
337
346
|
});
|
|
338
347
|
return app;
|
|
339
348
|
}
|
|
340
|
-
function toArray(value) {
|
|
341
|
-
if (value === undefined)
|
|
342
|
-
return [];
|
|
343
|
-
return Array.isArray(value) ? value : [value];
|
|
344
|
-
}
|
|
345
349
|
/**
|
|
346
350
|
* Exige un jeton connu du serveur sur toute requête entrante.
|
|
347
351
|
*
|