@imenam/mcp-organizer 0.0.0-stage → 1.0.1
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 +529 -2
- package/dist/shared/logic/first-account.d.ts +13 -0
- package/dist/shared/logic/first-account.js +15 -0
- package/dist/shared/logic/labels.d.ts +5 -0
- package/dist/shared/logic/labels.js +17 -0
- package/dist/shared/logic/positions.d.ts +9 -0
- package/dist/shared/logic/positions.js +19 -0
- package/dist/shared/logic/spaces.d.ts +13 -0
- package/dist/shared/logic/spaces.js +48 -0
- package/dist/shared/logic/test-summary.d.ts +28 -0
- package/dist/shared/logic/test-summary.js +35 -0
- package/dist/shared/schemas/agent-api.schema.d.ts +268 -0
- package/dist/shared/schemas/agent-api.schema.js +94 -0
- package/dist/shared/schemas/agent-tools.schema.d.ts +724 -0
- package/dist/shared/schemas/agent-tools.schema.js +165 -0
- package/dist/shared/schemas/api.schema.d.ts +249 -0
- package/dist/shared/schemas/api.schema.js +116 -0
- package/dist/shared/schemas/entities.schema.d.ts +472 -0
- package/dist/shared/schemas/entities.schema.js +262 -0
- package/dist/shared/schemas/enums.schema.d.ts +135 -0
- package/dist/shared/schemas/enums.schema.js +58 -0
- package/dist/shared/schemas/gui-api.schema.d.ts +53 -0
- package/dist/shared/schemas/gui-api.schema.js +20 -0
- package/dist/shared/schemas/ideas-api.schema.d.ts +81 -0
- package/dist/shared/schemas/ideas-api.schema.js +40 -0
- package/dist/shared/schemas/notifications-api.schema.d.ts +87 -0
- package/dist/shared/schemas/notifications-api.schema.js +58 -0
- package/dist/shared/schemas/tasks-api.schema.d.ts +675 -0
- package/dist/shared/schemas/tasks-api.schema.js +225 -0
- package/dist/shared/schemas/tokens-api.schema.d.ts +106 -0
- package/dist/shared/schemas/tokens-api.schema.js +42 -0
- package/dist/shared/schemas/transcription-api.schema.d.ts +15 -0
- package/dist/shared/schemas/transcription-api.schema.js +12 -0
- package/dist/src/agent-api/checklist.d.ts +3 -0
- package/dist/src/agent-api/checklist.js +127 -0
- package/dist/src/agent-api/ideas.d.ts +3 -0
- package/dist/src/agent-api/ideas.js +93 -0
- package/dist/src/agent-api/requests.d.ts +3 -0
- package/dist/src/agent-api/requests.js +60 -0
- package/dist/src/agent-api/router.d.ts +3 -0
- package/dist/src/agent-api/router.js +40 -0
- package/dist/src/agent-api/tasks.d.ts +2 -0
- package/dist/src/agent-api/tasks.js +290 -0
- package/dist/src/agent-api/types.d.ts +41 -0
- package/dist/src/agent-api/types.js +14 -0
- package/dist/src/cli/backup.d.ts +12 -0
- package/dist/src/cli/backup.js +126 -0
- package/dist/src/cli/prompt.d.ts +7 -0
- package/dist/src/cli/prompt.js +40 -0
- package/dist/src/cli/set-user.d.ts +2 -0
- package/dist/src/cli/set-user.js +114 -0
- package/dist/src/cli/setup-mcp.d.ts +10 -0
- package/dist/src/cli/setup-mcp.js +110 -0
- package/dist/src/cli/setup.d.ts +39 -0
- package/dist/src/cli/setup.js +626 -0
- package/dist/src/core/accounts.d.ts +129 -0
- package/dist/src/core/accounts.js +284 -0
- package/dist/src/core/agent-tokens.d.ts +31 -0
- package/dist/src/core/agent-tokens.js +120 -0
- package/dist/src/core/agent.d.ts +71 -0
- package/dist/src/core/agent.js +358 -0
- package/dist/src/core/attachments.d.ts +21 -0
- package/dist/src/core/attachments.js +44 -0
- package/dist/src/core/data-dir.d.ts +5 -0
- package/dist/src/core/data-dir.js +11 -0
- package/dist/src/core/db.d.ts +13 -0
- package/dist/src/core/db.js +38 -0
- package/dist/src/core/env-file.d.ts +11 -0
- package/dist/src/core/env-file.js +36 -0
- package/dist/src/core/errors.d.ts +24 -0
- package/dist/src/core/errors.js +42 -0
- package/dist/src/core/history.d.ts +34 -0
- package/dist/src/core/history.js +279 -0
- package/dist/src/core/ideas.d.ts +16 -0
- package/dist/src/core/ideas.js +100 -0
- package/dist/src/core/ids.d.ts +2 -0
- package/dist/src/core/ids.js +5 -0
- package/dist/src/core/log-dir.d.ts +2 -0
- package/dist/src/core/log-dir.js +9 -0
- package/dist/src/core/logger.d.ts +14 -0
- package/dist/src/core/logger.js +37 -0
- package/dist/src/core/mail.d.ts +43 -0
- package/dist/src/core/mail.js +44 -0
- package/dist/src/core/mcp-entry.d.ts +20 -0
- package/dist/src/core/mcp-entry.js +15 -0
- package/dist/src/core/migrations.d.ts +18 -0
- package/dist/src/core/migrations.js +404 -0
- package/dist/src/core/native-sqlite.d.ts +2 -0
- package/dist/src/core/native-sqlite.js +23 -0
- package/dist/src/core/notifications.d.ts +46 -0
- package/dist/src/core/notifications.js +173 -0
- package/dist/src/core/project-root.d.ts +2 -0
- package/dist/src/core/project-root.js +13 -0
- package/dist/src/core/requests.d.ts +49 -0
- package/dist/src/core/requests.js +239 -0
- package/dist/src/core/snapshots.d.ts +32 -0
- package/dist/src/core/snapshots.js +35 -0
- package/dist/src/core/spaces.d.ts +66 -0
- package/dist/src/core/spaces.js +252 -0
- package/dist/src/core/store.d.ts +565 -0
- package/dist/src/core/store.js +2093 -0
- package/dist/src/core/task-history.d.ts +7 -0
- package/dist/src/core/task-history.js +41 -0
- package/dist/src/core/tasks.d.ts +58 -0
- package/dist/src/core/tasks.js +389 -0
- package/dist/src/core/test-tasks.d.ts +19 -0
- package/dist/src/core/test-tasks.js +135 -0
- package/dist/src/core/transcription.d.ts +45 -0
- package/dist/src/core/transcription.js +92 -0
- package/dist/src/deploy/env-render.d.ts +26 -0
- package/dist/src/deploy/env-render.js +98 -0
- package/dist/src/deploy/templates.d.ts +38 -0
- package/dist/src/deploy/templates.js +130 -0
- package/dist/src/gui-api.d.ts +37 -0
- package/dist/src/gui-api.js +383 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +100 -0
- package/dist/src/mcp/client.d.ts +23 -0
- package/dist/src/mcp/client.js +81 -0
- package/dist/src/mcp/start.d.ts +14 -0
- package/dist/src/mcp/start.js +104 -0
- package/dist/src/mcp/tools.d.ts +46 -0
- package/dist/src/mcp/tools.js +348 -0
- package/dist/src/server/app.d.ts +25 -0
- package/dist/src/server/app.js +290 -0
- package/dist/src/server/config.d.ts +23 -0
- package/dist/src/server/config.js +75 -0
- package/dist/src/server/limits.d.ts +2 -0
- package/dist/src/server/limits.js +4 -0
- package/dist/src/server/login-limiter.d.ts +36 -0
- package/dist/src/server/login-limiter.js +81 -0
- package/dist/src/server/start.d.ts +5 -0
- package/dist/src/server/start.js +88 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +16 -0
- package/gui/dist/assets/index-DwkXx4Q2.js +70 -0
- package/gui/dist/assets/index-lnvnkDdg.css +1 -0
- package/gui/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/gui/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/gui/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/gui/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/gui/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/gui/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/gui/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/gui/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/gui/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/gui/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/gui/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/gui/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/gui/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/gui/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/gui/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/gui/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/gui/dist/index.html +13 -0
- package/package.json +66 -4
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { RequestStatusSchema, TaskPrioritySchema, TaskStatusSchema, TaskTypeSchema, TestResultValueSchema } from "./enums.schema.js";
|
|
3
|
+
import { ChangeSchema, EmailSchema, IdSchema, PositionSchema, TimestampSchema } from "./entities.schema.js";
|
|
4
|
+
/** Corps des requêtes et des réponses de l'API des tâches de la GUI. */
|
|
5
|
+
/** Taille maximale d'une pièce jointe : 20 Mo. */
|
|
6
|
+
export const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
|
|
7
|
+
export const MAX_TASK_TITLE_LENGTH = 200;
|
|
8
|
+
export const MAX_TAG_LENGTH = 40;
|
|
9
|
+
export const TaskTitleSchema = z.string().trim().min(1, "Le titre est requis.").max(MAX_TASK_TITLE_LENGTH, `Le titre dépasse ${MAX_TASK_TITLE_LENGTH} caractères.`);
|
|
10
|
+
export const TagNameSchema = z.string().trim().min(1, "Un tag ne peut pas être vide.").max(MAX_TAG_LENGTH, `Un tag dépasse ${MAX_TAG_LENGTH} caractères.`);
|
|
11
|
+
/** Membre d'un espace tel que l'affichent les avatars et les menus. */
|
|
12
|
+
export const MemberRefSchema = z.object({
|
|
13
|
+
userId: IdSchema,
|
|
14
|
+
displayName: z.string().nullable(),
|
|
15
|
+
email: EmailSchema,
|
|
16
|
+
});
|
|
17
|
+
export const TagViewSchema = z.object({ id: IdSchema, name: z.string() });
|
|
18
|
+
/** Avancement des sous-tâches directes : terminées / total. */
|
|
19
|
+
export const ProgressSchema = z.object({ done: z.number().int().min(0), total: z.number().int().min(0) });
|
|
20
|
+
/** Bilan d'une tâche de test : points par résultat combiné de ses testeurs (voir `shared/logic/test-summary.ts`). */
|
|
21
|
+
export const TestSummarySchema = z.object({
|
|
22
|
+
total: z.number().int().min(0),
|
|
23
|
+
verified: z.number().int().min(0),
|
|
24
|
+
ok: z.number().int().min(0),
|
|
25
|
+
ko: z.number().int().min(0),
|
|
26
|
+
blocked: z.number().int().min(0),
|
|
27
|
+
untested: z.number().int().min(0),
|
|
28
|
+
});
|
|
29
|
+
/** Tâche d'un arbre (Kanban, liste, sous-tâches), avec ses sous-tâches. */
|
|
30
|
+
export const TaskNodeSchema = z.object({
|
|
31
|
+
id: IdSchema,
|
|
32
|
+
/** Identifiant lisible : `ORG-41`. */
|
|
33
|
+
key: z.string(),
|
|
34
|
+
parentId: IdSchema.nullable(),
|
|
35
|
+
depth: z.number().int(),
|
|
36
|
+
type: TaskTypeSchema,
|
|
37
|
+
title: z.string(),
|
|
38
|
+
status: TaskStatusSchema,
|
|
39
|
+
priority: TaskPrioritySchema,
|
|
40
|
+
position: PositionSchema,
|
|
41
|
+
assigneeIds: z.array(IdSchema),
|
|
42
|
+
tagIds: z.array(IdSchema),
|
|
43
|
+
subtasks: ProgressSchema,
|
|
44
|
+
/** Bilan des points de vérification d'une tâche de test ; null pour une tâche standard. */
|
|
45
|
+
test: TestSummarySchema.nullable(),
|
|
46
|
+
get children() {
|
|
47
|
+
return z.array(TaskNodeSchema);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
/** Filtres de l'arbre d'un espace ; une tâche est gardée si elle ou l'une de ses sous-tâches y répond. */
|
|
51
|
+
export const TaskFiltersSchema = z.object({
|
|
52
|
+
type: TaskTypeSchema.optional(),
|
|
53
|
+
priority: TaskPrioritySchema.optional(),
|
|
54
|
+
assignee: IdSchema.optional(),
|
|
55
|
+
tag: IdSchema.optional(),
|
|
56
|
+
});
|
|
57
|
+
export const BoardResponseSchema = z.object({
|
|
58
|
+
spaceId: IdSchema,
|
|
59
|
+
members: z.array(MemberRefSchema),
|
|
60
|
+
tags: z.array(TagViewSchema),
|
|
61
|
+
/** Tâches de premier niveau, chacune avec ses sous-tâches. */
|
|
62
|
+
tasks: z.array(TaskNodeSchema),
|
|
63
|
+
});
|
|
64
|
+
export const TaskViewSchema = z.object({
|
|
65
|
+
id: IdSchema,
|
|
66
|
+
key: z.string(),
|
|
67
|
+
spaceId: IdSchema,
|
|
68
|
+
parentId: IdSchema.nullable(),
|
|
69
|
+
depth: z.number().int(),
|
|
70
|
+
type: TaskTypeSchema,
|
|
71
|
+
title: z.string(),
|
|
72
|
+
description: z.string(),
|
|
73
|
+
status: TaskStatusSchema,
|
|
74
|
+
priority: TaskPrioritySchema,
|
|
75
|
+
assigneeIds: z.array(IdSchema),
|
|
76
|
+
tagIds: z.array(IdSchema),
|
|
77
|
+
/** Tâche vérifiée par une tâche de test ; null sinon. */
|
|
78
|
+
testedTaskId: IdSchema.nullable(),
|
|
79
|
+
createdBy: IdSchema,
|
|
80
|
+
createdAt: TimestampSchema,
|
|
81
|
+
updatedAt: TimestampSchema,
|
|
82
|
+
version: z.number().int(),
|
|
83
|
+
});
|
|
84
|
+
export const TaskRefSchema = z.object({ id: IdSchema, key: z.string(), title: z.string() });
|
|
85
|
+
export const ChecklistItemViewSchema = z.object({ id: IdSchema, text: z.string(), done: z.boolean(), position: PositionSchema });
|
|
86
|
+
export const CommentViewSchema = z.object({
|
|
87
|
+
id: IdSchema,
|
|
88
|
+
authorId: IdSchema,
|
|
89
|
+
authorName: z.string(),
|
|
90
|
+
body: z.string(),
|
|
91
|
+
createdAt: TimestampSchema,
|
|
92
|
+
editedAt: TimestampSchema.nullable(),
|
|
93
|
+
/** Vrai pour un commentaire écrit par l'agent au nom de l'auteur d'une demande. */
|
|
94
|
+
byAgent: z.boolean(),
|
|
95
|
+
});
|
|
96
|
+
export const AttachmentViewSchema = z.object({
|
|
97
|
+
id: IdSchema,
|
|
98
|
+
filename: z.string(),
|
|
99
|
+
mime: z.string(),
|
|
100
|
+
size: z.number().int(),
|
|
101
|
+
uploaderId: IdSchema,
|
|
102
|
+
createdAt: TimestampSchema,
|
|
103
|
+
});
|
|
104
|
+
export const CheckpointViewSchema = z.object({ id: IdSchema, text: z.string(), position: PositionSchema });
|
|
105
|
+
export const TestResultViewSchema = z.object({
|
|
106
|
+
checkpointId: IdSchema,
|
|
107
|
+
result: TestResultValueSchema,
|
|
108
|
+
comment: z.string(),
|
|
109
|
+
updatedAt: TimestampSchema,
|
|
110
|
+
});
|
|
111
|
+
/** Review soumise, avec l'état de la demande `test_review` qu'elle a créée (null si la demande n'existe plus). */
|
|
112
|
+
export const SubmittedReviewViewSchema = z.object({
|
|
113
|
+
id: IdSchema,
|
|
114
|
+
testerId: IdSchema,
|
|
115
|
+
/** Nom affiché du testeur, membre ou ancien membre de l'espace. */
|
|
116
|
+
testerName: z.string(),
|
|
117
|
+
text: z.string(),
|
|
118
|
+
submittedAt: TimestampSchema,
|
|
119
|
+
requestId: IdSchema.nullable(),
|
|
120
|
+
requestStatus: RequestStatusSchema.nullable(),
|
|
121
|
+
});
|
|
122
|
+
/** Testeur d'une tâche de test : ses résultats saisis et son brouillon de review. */
|
|
123
|
+
export const TesterViewSchema = z.object({
|
|
124
|
+
userId: IdSchema,
|
|
125
|
+
results: z.array(TestResultViewSchema),
|
|
126
|
+
/** Texte du brouillon de review en cours ; vide sans brouillon. */
|
|
127
|
+
draft: z.string(),
|
|
128
|
+
});
|
|
129
|
+
/** Ancien testeur (désassigné, ou qui a quitté l'espace) : ses résultats, en lecture seule. */
|
|
130
|
+
export const PastTesterViewSchema = z.object({
|
|
131
|
+
userId: IdSchema,
|
|
132
|
+
name: z.string(),
|
|
133
|
+
results: z.array(TestResultViewSchema),
|
|
134
|
+
});
|
|
135
|
+
/** Partie propre à une tâche de test du détail. */
|
|
136
|
+
export const TestDetailSchema = z.object({
|
|
137
|
+
checkpoints: z.array(CheckpointViewSchema),
|
|
138
|
+
summary: TestSummarySchema,
|
|
139
|
+
/** Assignés de la tâche, dans l'ordre des assignés. */
|
|
140
|
+
testers: z.array(TesterViewSchema),
|
|
141
|
+
/** Testeurs qui ont saisi des résultats sans être assignés aujourd'hui, par nom. */
|
|
142
|
+
pastTesters: z.array(PastTesterViewSchema),
|
|
143
|
+
/** Reviews soumises, de la plus récente à la plus ancienne. */
|
|
144
|
+
reviews: z.array(SubmittedReviewViewSchema),
|
|
145
|
+
});
|
|
146
|
+
export const TaskDetailResponseSchema = z.object({
|
|
147
|
+
task: TaskViewSchema,
|
|
148
|
+
/** Tâches parentes, de la racine au parent direct. */
|
|
149
|
+
ancestors: z.array(TaskRefSchema),
|
|
150
|
+
/** Tâche vérifiée par une tâche de test. */
|
|
151
|
+
testedTask: TaskRefSchema.nullable(),
|
|
152
|
+
children: z.array(TaskNodeSchema),
|
|
153
|
+
checklist: z.array(ChecklistItemViewSchema),
|
|
154
|
+
/** Points de vérification, résultats et reviews d'une tâche de test ; null pour une tâche standard. */
|
|
155
|
+
test: TestDetailSchema.nullable(),
|
|
156
|
+
comments: z.array(CommentViewSchema),
|
|
157
|
+
attachments: z.array(AttachmentViewSchema),
|
|
158
|
+
members: z.array(MemberRefSchema),
|
|
159
|
+
/** Tags de l'espace. */
|
|
160
|
+
tags: z.array(TagViewSchema),
|
|
161
|
+
/** Dernière modification de la tâche quand elle vient de l'agent : l'action à annuler et sa date. */
|
|
162
|
+
agentEdit: z.object({ actionId: IdSchema, at: TimestampSchema }).nullable(),
|
|
163
|
+
});
|
|
164
|
+
// --- Requêtes ---
|
|
165
|
+
export const CreateTaskRequestSchema = z.object({
|
|
166
|
+
title: TaskTitleSchema,
|
|
167
|
+
parentId: IdSchema.nullable().optional(),
|
|
168
|
+
status: TaskStatusSchema.optional(),
|
|
169
|
+
type: TaskTypeSchema.optional(),
|
|
170
|
+
priority: TaskPrioritySchema.optional(),
|
|
171
|
+
description: z.string().optional(),
|
|
172
|
+
});
|
|
173
|
+
export const UpdateTaskRequestSchema = z
|
|
174
|
+
.object({
|
|
175
|
+
title: TaskTitleSchema.optional(),
|
|
176
|
+
description: z.string().optional(),
|
|
177
|
+
status: TaskStatusSchema.optional(),
|
|
178
|
+
priority: TaskPrioritySchema.optional(),
|
|
179
|
+
type: TaskTypeSchema.optional(),
|
|
180
|
+
parentId: IdSchema.nullable().optional(),
|
|
181
|
+
/** Tâche vérifiée, pour une tâche de test ; null retire le lien. */
|
|
182
|
+
testedTaskId: IdSchema.nullable().optional(),
|
|
183
|
+
})
|
|
184
|
+
.refine((patch) => Object.values(patch).some((value) => value !== undefined), "Aucune modification demandée.");
|
|
185
|
+
/** Déplacement Kanban : colonne cible, et tâche après laquelle se placer (null : en tête de colonne). */
|
|
186
|
+
export const MoveTaskRequestSchema = z.object({ status: TaskStatusSchema, afterId: IdSchema.nullable() });
|
|
187
|
+
export const SetAssigneesRequestSchema = z.object({ userIds: z.array(IdSchema) });
|
|
188
|
+
export const SetTagsRequestSchema = z.object({ names: z.array(TagNameSchema) });
|
|
189
|
+
export const ChecklistTextSchema = z.string().trim().min(1, "L'élément de checklist est vide.").max(500);
|
|
190
|
+
export const AddChecklistItemRequestSchema = z.object({ text: ChecklistTextSchema });
|
|
191
|
+
export const UpdateChecklistItemRequestSchema = z
|
|
192
|
+
.object({ text: ChecklistTextSchema.optional(), done: z.boolean().optional() })
|
|
193
|
+
.refine((patch) => patch.text !== undefined || patch.done !== undefined, "Aucune modification demandée.");
|
|
194
|
+
/** Réordonnancement : élément après lequel se placer (null : en tête). */
|
|
195
|
+
export const MoveAfterRequestSchema = z.object({ afterId: IdSchema.nullable() });
|
|
196
|
+
export const CheckpointTextSchema = z.string().trim().min(1, "Le point de vérification est vide.").max(500);
|
|
197
|
+
export const CheckpointTextRequestSchema = z.object({ text: CheckpointTextSchema });
|
|
198
|
+
export const MAX_TEST_COMMENT_LENGTH = 2000;
|
|
199
|
+
/** Résultat de l'utilisateur sur un point : valeur, commentaire, ou les deux. */
|
|
200
|
+
export const SetTestResultRequestSchema = z
|
|
201
|
+
.object({
|
|
202
|
+
result: TestResultValueSchema.optional(),
|
|
203
|
+
comment: z.string().trim().max(MAX_TEST_COMMENT_LENGTH, `Le commentaire dépasse ${MAX_TEST_COMMENT_LENGTH} caractères.`).optional(),
|
|
204
|
+
})
|
|
205
|
+
.refine((patch) => patch.result !== undefined || patch.comment !== undefined, "Aucune modification demandée.");
|
|
206
|
+
export const MAX_REVIEW_LENGTH = 10_000;
|
|
207
|
+
export const ReviewDraftRequestSchema = z.object({ text: z.string().max(MAX_REVIEW_LENGTH, `La review dépasse ${MAX_REVIEW_LENGTH} caractères.`) });
|
|
208
|
+
export const ReviewDraftResponseSchema = z.object({ draft: z.string() });
|
|
209
|
+
export const SubmitReviewRequestSchema = z.object({
|
|
210
|
+
text: z.string().trim().min(1, "La review est vide : décrivez ce que vous avez constaté.").max(MAX_REVIEW_LENGTH, `La review dépasse ${MAX_REVIEW_LENGTH} caractères.`),
|
|
211
|
+
});
|
|
212
|
+
export const CommentRequestSchema = z.object({ body: z.string().trim().min(1, "Le commentaire est vide.").max(10_000) });
|
|
213
|
+
/** Type MIME `type/sous-type`, sans paramètres. */
|
|
214
|
+
export const MimeTypeSchema = z.string().regex(/^[\w.+-]+\/[\w.+-]+$/, "Type de fichier invalide.");
|
|
215
|
+
/** Pièce jointe transmise par le serveur web, qui lit le formulaire multipart du navigateur. */
|
|
216
|
+
export const UploadAttachmentRequestSchema = z.object({
|
|
217
|
+
filename: z.string().trim().min(1, "Le fichier n'a pas de nom.").max(255),
|
|
218
|
+
mime: MimeTypeSchema,
|
|
219
|
+
contentBase64: z.string(),
|
|
220
|
+
});
|
|
221
|
+
/** Changements des espaces de l'utilisateur et numéro du dernier changement transmis. */
|
|
222
|
+
export const ChangesResponseSchema = z.object({
|
|
223
|
+
seq: z.number().int().min(0),
|
|
224
|
+
changes: z.array(ChangeSchema),
|
|
225
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Corps des requêtes et des réponses des jetons d'agent dans la GUI (`/api/tokens`). */
|
|
3
|
+
/** Longueur maximale du nom d'un jeton. */
|
|
4
|
+
export declare const MAX_TOKEN_NAME_LENGTH = 80;
|
|
5
|
+
/** Durée de validité maximale d'un jeton, en jours. */
|
|
6
|
+
export declare const MAX_TOKEN_DAYS = 3650;
|
|
7
|
+
/** Préfixe de tout jeton d'agent. */
|
|
8
|
+
export declare const AGENT_TOKEN_PREFIX = "org_";
|
|
9
|
+
/**
|
|
10
|
+
* État d'un jeton : actif ; révoqué ; expiré ; inactif quand son propriétaire
|
|
11
|
+
* n'est plus membre de l'espace couvert ou que son compte n'est plus actif.
|
|
12
|
+
*/
|
|
13
|
+
export declare const TOKEN_STATES: readonly ["active", "inactive", "expired", "revoked"];
|
|
14
|
+
export declare const TokenStateSchema: z.ZodEnum<{
|
|
15
|
+
active: "active";
|
|
16
|
+
expired: "expired";
|
|
17
|
+
inactive: "inactive";
|
|
18
|
+
revoked: "revoked";
|
|
19
|
+
}>;
|
|
20
|
+
export type TokenState = z.infer<typeof TokenStateSchema>;
|
|
21
|
+
export declare const CreateTokenRequestSchema: z.ZodObject<{
|
|
22
|
+
name: z.ZodString;
|
|
23
|
+
spaceId: z.ZodString;
|
|
24
|
+
expiresInDays: z.ZodNullable<z.ZodNumber>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
export type CreateTokenRequest = z.infer<typeof CreateTokenRequestSchema>;
|
|
27
|
+
/** Jeton tel que le liste la GUI : jamais sa valeur ni son hash. */
|
|
28
|
+
export declare const AgentTokenViewSchema: z.ZodObject<{
|
|
29
|
+
id: z.ZodString;
|
|
30
|
+
name: z.ZodString;
|
|
31
|
+
prefix: z.ZodString;
|
|
32
|
+
space: z.ZodObject<{
|
|
33
|
+
id: z.ZodString;
|
|
34
|
+
name: z.ZodString;
|
|
35
|
+
kind: z.ZodEnum<{
|
|
36
|
+
personal: "personal";
|
|
37
|
+
shared: "shared";
|
|
38
|
+
}>;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
state: z.ZodEnum<{
|
|
41
|
+
active: "active";
|
|
42
|
+
expired: "expired";
|
|
43
|
+
inactive: "inactive";
|
|
44
|
+
revoked: "revoked";
|
|
45
|
+
}>;
|
|
46
|
+
createdAt: z.ZodISODateTime;
|
|
47
|
+
lastUsedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
48
|
+
expiresAt: z.ZodNullable<z.ZodISODateTime>;
|
|
49
|
+
revokedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
export type AgentTokenView = z.infer<typeof AgentTokenViewSchema>;
|
|
52
|
+
export declare const TokenListResponseSchema: z.ZodObject<{
|
|
53
|
+
tokens: z.ZodArray<z.ZodObject<{
|
|
54
|
+
id: z.ZodString;
|
|
55
|
+
name: z.ZodString;
|
|
56
|
+
prefix: z.ZodString;
|
|
57
|
+
space: z.ZodObject<{
|
|
58
|
+
id: z.ZodString;
|
|
59
|
+
name: z.ZodString;
|
|
60
|
+
kind: z.ZodEnum<{
|
|
61
|
+
personal: "personal";
|
|
62
|
+
shared: "shared";
|
|
63
|
+
}>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
state: z.ZodEnum<{
|
|
66
|
+
active: "active";
|
|
67
|
+
expired: "expired";
|
|
68
|
+
inactive: "inactive";
|
|
69
|
+
revoked: "revoked";
|
|
70
|
+
}>;
|
|
71
|
+
createdAt: z.ZodISODateTime;
|
|
72
|
+
lastUsedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
73
|
+
expiresAt: z.ZodNullable<z.ZodISODateTime>;
|
|
74
|
+
revokedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
75
|
+
}, z.core.$strip>>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
export type TokenListResponse = z.infer<typeof TokenListResponseSchema>;
|
|
78
|
+
/** Jeton créé : sa valeur, montrée une seule fois, et l'entrée `.mcp.json` du mode MCP qui l'utilise. */
|
|
79
|
+
export declare const CreatedTokenResponseSchema: z.ZodObject<{
|
|
80
|
+
token: z.ZodObject<{
|
|
81
|
+
id: z.ZodString;
|
|
82
|
+
name: z.ZodString;
|
|
83
|
+
prefix: z.ZodString;
|
|
84
|
+
space: z.ZodObject<{
|
|
85
|
+
id: z.ZodString;
|
|
86
|
+
name: z.ZodString;
|
|
87
|
+
kind: z.ZodEnum<{
|
|
88
|
+
personal: "personal";
|
|
89
|
+
shared: "shared";
|
|
90
|
+
}>;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
state: z.ZodEnum<{
|
|
93
|
+
active: "active";
|
|
94
|
+
expired: "expired";
|
|
95
|
+
inactive: "inactive";
|
|
96
|
+
revoked: "revoked";
|
|
97
|
+
}>;
|
|
98
|
+
createdAt: z.ZodISODateTime;
|
|
99
|
+
lastUsedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
100
|
+
expiresAt: z.ZodNullable<z.ZodISODateTime>;
|
|
101
|
+
revokedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
102
|
+
}, z.core.$strip>;
|
|
103
|
+
value: z.ZodString;
|
|
104
|
+
mcpJson: z.ZodString;
|
|
105
|
+
}, z.core.$strip>;
|
|
106
|
+
export type CreatedTokenResponse = z.infer<typeof CreatedTokenResponseSchema>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SpaceKindSchema } from "./enums.schema.js";
|
|
3
|
+
import { IdSchema, TimestampSchema } from "./entities.schema.js";
|
|
4
|
+
/** Corps des requêtes et des réponses des jetons d'agent dans la GUI (`/api/tokens`). */
|
|
5
|
+
/** Longueur maximale du nom d'un jeton. */
|
|
6
|
+
export const MAX_TOKEN_NAME_LENGTH = 80;
|
|
7
|
+
/** Durée de validité maximale d'un jeton, en jours. */
|
|
8
|
+
export const MAX_TOKEN_DAYS = 3650;
|
|
9
|
+
/** Préfixe de tout jeton d'agent. */
|
|
10
|
+
export const AGENT_TOKEN_PREFIX = "org_";
|
|
11
|
+
/**
|
|
12
|
+
* État d'un jeton : actif ; révoqué ; expiré ; inactif quand son propriétaire
|
|
13
|
+
* n'est plus membre de l'espace couvert ou que son compte n'est plus actif.
|
|
14
|
+
*/
|
|
15
|
+
export const TOKEN_STATES = ["active", "inactive", "expired", "revoked"];
|
|
16
|
+
export const TokenStateSchema = z.enum(TOKEN_STATES);
|
|
17
|
+
export const CreateTokenRequestSchema = z.object({
|
|
18
|
+
name: z.string().trim().min(1, "Le nom du jeton est requis.").max(MAX_TOKEN_NAME_LENGTH, `Le nom du jeton dépasse ${MAX_TOKEN_NAME_LENGTH} caractères.`),
|
|
19
|
+
spaceId: IdSchema,
|
|
20
|
+
/** Durée de validité en jours ; null : sans expiration. */
|
|
21
|
+
expiresInDays: z.number().int().min(1, "Durée de validité invalide.").max(MAX_TOKEN_DAYS, "Durée de validité invalide.").nullable(),
|
|
22
|
+
});
|
|
23
|
+
/** Jeton tel que le liste la GUI : jamais sa valeur ni son hash. */
|
|
24
|
+
export const AgentTokenViewSchema = z.object({
|
|
25
|
+
id: IdSchema,
|
|
26
|
+
name: z.string(),
|
|
27
|
+
/** Début du jeton (`org_AbCd1234`), pour le reconnaître. */
|
|
28
|
+
prefix: z.string(),
|
|
29
|
+
space: z.object({ id: IdSchema, name: z.string(), kind: SpaceKindSchema }),
|
|
30
|
+
state: TokenStateSchema,
|
|
31
|
+
createdAt: TimestampSchema,
|
|
32
|
+
lastUsedAt: TimestampSchema.nullable(),
|
|
33
|
+
expiresAt: TimestampSchema.nullable(),
|
|
34
|
+
revokedAt: TimestampSchema.nullable(),
|
|
35
|
+
});
|
|
36
|
+
export const TokenListResponseSchema = z.object({ tokens: z.array(AgentTokenViewSchema) });
|
|
37
|
+
/** Jeton créé : sa valeur, montrée une seule fois, et l'entrée `.mcp.json` du mode MCP qui l'utilise. */
|
|
38
|
+
export const CreatedTokenResponseSchema = z.object({
|
|
39
|
+
token: AgentTokenViewSchema,
|
|
40
|
+
value: z.string().startsWith(AGENT_TOKEN_PREFIX),
|
|
41
|
+
mcpJson: z.string(),
|
|
42
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Corps des requêtes et des réponses de la dictée vocale. */
|
|
3
|
+
/** Taille maximale d'un enregistrement transcrit : 25 Mo, limite de l'API Groq. */
|
|
4
|
+
export declare const MAX_TRANSCRIPTION_BYTES: number;
|
|
5
|
+
/** Enregistrement transmis par le serveur web, qui lit le formulaire multipart du navigateur (champ `file`). */
|
|
6
|
+
export declare const TranscribeRequestSchema: z.ZodObject<{
|
|
7
|
+
filename: z.ZodString;
|
|
8
|
+
mime: z.ZodString;
|
|
9
|
+
contentBase64: z.ZodString;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type TranscribeRequest = z.infer<typeof TranscribeRequestSchema>;
|
|
12
|
+
export declare const TranscribeResponseSchema: z.ZodObject<{
|
|
13
|
+
text: z.ZodString;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export type TranscribeResponse = z.infer<typeof TranscribeResponseSchema>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { MimeTypeSchema } from "./tasks-api.schema.js";
|
|
3
|
+
/** Corps des requêtes et des réponses de la dictée vocale. */
|
|
4
|
+
/** Taille maximale d'un enregistrement transcrit : 25 Mo, limite de l'API Groq. */
|
|
5
|
+
export const MAX_TRANSCRIPTION_BYTES = 25 * 1024 * 1024;
|
|
6
|
+
/** Enregistrement transmis par le serveur web, qui lit le formulaire multipart du navigateur (champ `file`). */
|
|
7
|
+
export const TranscribeRequestSchema = z.object({
|
|
8
|
+
filename: z.string().trim().min(1, "L'enregistrement n'a pas de nom.").max(255),
|
|
9
|
+
mime: MimeTypeSchema,
|
|
10
|
+
contentBase64: z.string(),
|
|
11
|
+
});
|
|
12
|
+
export const TranscribeResponseSchema = z.object({ text: z.string() });
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { ChecklistDataSchema, EditChecklistBodySchema } from "../../shared/schemas/agent-tools.schema.js";
|
|
2
|
+
import { actFor, quote, resolveTask, taskData, taskKey } from "../core/agent.js";
|
|
3
|
+
import { InvariantError, NotFoundError, OrganizerError } from "../core/errors.js";
|
|
4
|
+
import { snapshotOf } from "../core/snapshots.js";
|
|
5
|
+
import { parseInput, respond } from "./types.js";
|
|
6
|
+
/** Checklist d'une tâche standard, ou points de vérification d'une tâche de test : opérations appliquées en ordre, tout ou rien. */
|
|
7
|
+
export const checklistRoutes = [
|
|
8
|
+
{
|
|
9
|
+
method: "POST",
|
|
10
|
+
path: "/tasks/:ref/checklist",
|
|
11
|
+
handle: ({ store }, { agent, params, body }) => {
|
|
12
|
+
const input = parseInput(EditChecklistBodySchema, body);
|
|
13
|
+
const { task, outcomes } = actFor(store, agent, input.request_id, (tx, space) => {
|
|
14
|
+
const task = resolveTask(tx.store, space, params.ref);
|
|
15
|
+
return { task, outcomes: input.operations.map((operation) => apply(tx, task, operation)) };
|
|
16
|
+
});
|
|
17
|
+
const items = task.type === "standard"
|
|
18
|
+
? store.listChecklist(task.id).map((item) => ({ id: item.id, text: item.text, done: item.done }))
|
|
19
|
+
: store.listCheckpoints(task.id).map((checkpoint) => ({ id: checkpoint.id, text: checkpoint.text, done: null }));
|
|
20
|
+
const current = items.map((item) => (item.done === null ? `- ${item.text} (id ${item.id})` : `- [${item.done ? "x" : " "}] ${item.text} (id ${item.id})`));
|
|
21
|
+
return respond(ChecklistDataSchema, `${taskKey(store, task)}: ${outcomes.join("; ")}.\n${task.type === "standard" ? "Checklist" : "Checkpoints"} now:\n${current.join("\n") || "(empty)"}`, { task: taskData(store, task), items });
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
/** Applique une opération et l'enregistre dans le lot ; renvoie sa description pour la réponse. */
|
|
26
|
+
function apply(tx, task, operation) {
|
|
27
|
+
return task.type === "standard" ? applyToChecklist(tx, task, operation) : applyToCheckpoints(tx, task, operation);
|
|
28
|
+
}
|
|
29
|
+
function applyToChecklist(tx, task, operation) {
|
|
30
|
+
const store = tx.store;
|
|
31
|
+
if (operation.action === "add") {
|
|
32
|
+
const item = tx.addChecklistItem(task.id, operation.text);
|
|
33
|
+
tx.recordAgentAction({
|
|
34
|
+
entityType: "checklist_item",
|
|
35
|
+
entityId: item.id,
|
|
36
|
+
operation: "create",
|
|
37
|
+
before: null,
|
|
38
|
+
after: item,
|
|
39
|
+
summary: `Élément ajouté : ${quote(item.text)}`,
|
|
40
|
+
taskId: task.id,
|
|
41
|
+
});
|
|
42
|
+
return `added item ${item.id}`;
|
|
43
|
+
}
|
|
44
|
+
const current = store.findChecklistItem(operation.item_id);
|
|
45
|
+
if (!current || current.taskId !== task.id)
|
|
46
|
+
throw new NotFoundError(`Checklist item ${operation.item_id} is not in this task. Call get_task to get the item ids.`);
|
|
47
|
+
if (operation.action === "delete") {
|
|
48
|
+
tx.deleteChecklistItem(current.id);
|
|
49
|
+
tx.recordAgentAction({
|
|
50
|
+
entityType: "checklist_item",
|
|
51
|
+
entityId: current.id,
|
|
52
|
+
operation: "delete",
|
|
53
|
+
before: current,
|
|
54
|
+
after: null,
|
|
55
|
+
summary: `Élément supprimé : ${quote(current.text)}`,
|
|
56
|
+
taskId: task.id,
|
|
57
|
+
});
|
|
58
|
+
return `deleted item ${current.id}`;
|
|
59
|
+
}
|
|
60
|
+
if (operation.text === undefined && operation.done === undefined)
|
|
61
|
+
throw new OrganizerError(`Nothing to update on item ${current.id}: give text or done.`);
|
|
62
|
+
const item = tx.updateChecklistItem(current.id, { text: operation.text, done: operation.done });
|
|
63
|
+
const parts = [];
|
|
64
|
+
if (item.text !== current.text)
|
|
65
|
+
parts.push(`Élément ${quote(current.text)} renommé en ${quote(item.text)}`);
|
|
66
|
+
if (item.done !== current.done)
|
|
67
|
+
parts.push(`Élément ${quote(item.text)} ${item.done ? "coché" : "décoché"}`);
|
|
68
|
+
tx.recordAgentAction({
|
|
69
|
+
entityType: "checklist_item",
|
|
70
|
+
entityId: item.id,
|
|
71
|
+
operation: "update",
|
|
72
|
+
before: current,
|
|
73
|
+
after: item,
|
|
74
|
+
summary: parts.join(" · ") || `Élément ${quote(item.text)} inchangé`,
|
|
75
|
+
taskId: task.id,
|
|
76
|
+
});
|
|
77
|
+
return `updated item ${item.id}`;
|
|
78
|
+
}
|
|
79
|
+
function applyToCheckpoints(tx, task, operation) {
|
|
80
|
+
const store = tx.store;
|
|
81
|
+
if (operation.action === "add") {
|
|
82
|
+
const checkpoint = tx.addCheckpoint(task.id, operation.text);
|
|
83
|
+
tx.recordAgentAction({
|
|
84
|
+
entityType: "test_checkpoint",
|
|
85
|
+
entityId: checkpoint.id,
|
|
86
|
+
operation: "create",
|
|
87
|
+
before: null,
|
|
88
|
+
after: snapshotOf(store, "test_checkpoint", checkpoint.id),
|
|
89
|
+
summary: `Point de vérification ajouté : ${quote(checkpoint.text)}`,
|
|
90
|
+
taskId: task.id,
|
|
91
|
+
});
|
|
92
|
+
return `added checkpoint ${checkpoint.id}`;
|
|
93
|
+
}
|
|
94
|
+
const current = store.findCheckpoint(operation.item_id);
|
|
95
|
+
if (!current || current.taskId !== task.id)
|
|
96
|
+
throw new NotFoundError(`Checkpoint ${operation.item_id} is not in this test task. Call get_task to get the checkpoint ids.`);
|
|
97
|
+
const before = snapshotOf(store, "test_checkpoint", current.id);
|
|
98
|
+
if (operation.action === "delete") {
|
|
99
|
+
tx.deleteCheckpoint(current.id);
|
|
100
|
+
tx.recordAgentAction({
|
|
101
|
+
entityType: "test_checkpoint",
|
|
102
|
+
entityId: current.id,
|
|
103
|
+
operation: "delete",
|
|
104
|
+
before,
|
|
105
|
+
after: null,
|
|
106
|
+
summary: `Point de vérification supprimé : ${quote(current.text)}`,
|
|
107
|
+
taskId: task.id,
|
|
108
|
+
});
|
|
109
|
+
return `deleted checkpoint ${current.id}`;
|
|
110
|
+
}
|
|
111
|
+
if (operation.done !== undefined) {
|
|
112
|
+
throw new InvariantError("A test checkpoint cannot be checked: testers record their own results. Only update its text.");
|
|
113
|
+
}
|
|
114
|
+
if (operation.text === undefined)
|
|
115
|
+
throw new OrganizerError(`Nothing to update on checkpoint ${current.id}: give its new text.`);
|
|
116
|
+
const checkpoint = tx.updateCheckpoint(current.id, operation.text);
|
|
117
|
+
tx.recordAgentAction({
|
|
118
|
+
entityType: "test_checkpoint",
|
|
119
|
+
entityId: checkpoint.id,
|
|
120
|
+
operation: "update",
|
|
121
|
+
before,
|
|
122
|
+
after: snapshotOf(store, "test_checkpoint", checkpoint.id),
|
|
123
|
+
summary: `Point ${quote(current.text)} renommé en ${quote(checkpoint.text)}`,
|
|
124
|
+
taskId: task.id,
|
|
125
|
+
});
|
|
126
|
+
return `updated checkpoint ${checkpoint.id}`;
|
|
127
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { CreateIdeaBodySchema, IdeaDataSchema, PromoteIdeaBodySchema, PromoteIdeaDataSchema, UpdateIdeaBodySchema } from "../../shared/schemas/agent-tools.schema.js";
|
|
2
|
+
import { actFor, ideaData, quote, requireText, resolveIdea, resolveTask, taskData, taskKey } from "../core/agent.js";
|
|
3
|
+
import { InvariantError, OrganizerError } from "../core/errors.js";
|
|
4
|
+
import { parseInput, respond } from "./types.js";
|
|
5
|
+
/** Boîte à idées de l'espace du jeton : ajout, reformulation, archivage, promotion en tâche. */
|
|
6
|
+
export const ideaRoutes = [
|
|
7
|
+
{
|
|
8
|
+
method: "POST",
|
|
9
|
+
path: "/ideas",
|
|
10
|
+
handle: ({ store }, { agent, body }) => {
|
|
11
|
+
const input = parseInput(CreateIdeaBodySchema, body);
|
|
12
|
+
const text = requireText(input.text, "text");
|
|
13
|
+
const { space, idea } = actFor(store, agent, input.request_id, (tx, space) => {
|
|
14
|
+
const idea = tx.createIdea({ spaceId: space.id, text });
|
|
15
|
+
tx.recordAgentAction({ entityType: "idea", entityId: idea.id, operation: "create", before: null, after: idea, summary: `Idée ajoutée : ${quote(idea.text)}`, taskId: null });
|
|
16
|
+
return { space, idea };
|
|
17
|
+
});
|
|
18
|
+
return respond(IdeaDataSchema, `Added idea ${idea.id} to the idea box of "${space.name}".`, { idea: ideaData(store, idea) });
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
method: "PATCH",
|
|
23
|
+
path: "/ideas/:ideaId",
|
|
24
|
+
handle: ({ store }, { agent, params, body }) => {
|
|
25
|
+
const input = parseInput(UpdateIdeaBodySchema, body);
|
|
26
|
+
if (input.text === undefined && input.archived === undefined)
|
|
27
|
+
throw new OrganizerError("Nothing to change: give text or archived.");
|
|
28
|
+
const text = input.text === undefined ? undefined : requireText(input.text, "text");
|
|
29
|
+
const updated = actFor(store, agent, input.request_id, (tx, space) => {
|
|
30
|
+
const idea = resolveIdea(tx.store, space, params.ideaId);
|
|
31
|
+
const updated = tx.updateIdea(idea.id, { text, archived: input.archived });
|
|
32
|
+
const parts = [];
|
|
33
|
+
if (updated.text !== idea.text)
|
|
34
|
+
parts.push(`Idée reformulée : ${quote(updated.text)}`);
|
|
35
|
+
if ((updated.archivedAt !== null) !== (idea.archivedAt !== null)) {
|
|
36
|
+
parts.push(updated.archivedAt !== null ? `Idée archivée : ${quote(updated.text)}` : `Idée désarchivée : ${quote(updated.text)}`);
|
|
37
|
+
}
|
|
38
|
+
tx.recordAgentAction({
|
|
39
|
+
entityType: "idea",
|
|
40
|
+
entityId: idea.id,
|
|
41
|
+
operation: "update",
|
|
42
|
+
before: idea,
|
|
43
|
+
after: updated,
|
|
44
|
+
summary: parts.join(" · ") || `Idée inchangée : ${quote(updated.text)}`,
|
|
45
|
+
taskId: null,
|
|
46
|
+
});
|
|
47
|
+
return updated;
|
|
48
|
+
});
|
|
49
|
+
return respond(IdeaDataSchema, `Idea ${updated.id} updated${updated.archivedAt !== null ? " (archived)" : ""}.`, { idea: ideaData(store, updated) });
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
method: "POST",
|
|
54
|
+
path: "/ideas/:ideaId/promote",
|
|
55
|
+
handle: ({ store }, { agent, params, body }) => {
|
|
56
|
+
const input = parseInput(PromoteIdeaBodySchema, body);
|
|
57
|
+
const title = requireText(input.title, "title");
|
|
58
|
+
const { idea, task } = actFor(store, agent, input.request_id, (tx, space) => {
|
|
59
|
+
const idea = resolveIdea(tx.store, space, params.ideaId);
|
|
60
|
+
if (idea.promotedTaskId !== null && tx.store.findTask(idea.promotedTaskId)) {
|
|
61
|
+
const existing = tx.store.getTask(idea.promotedTaskId);
|
|
62
|
+
throw new InvariantError(`Idea ${idea.id} is already promoted to ${taskKey(tx.store, existing)}. Update that task with update_task instead.`);
|
|
63
|
+
}
|
|
64
|
+
const parent = input.parent === undefined ? null : resolveTask(tx.store, space, input.parent);
|
|
65
|
+
const task = tx.createTask({ spaceId: space.id, parentId: parent?.id ?? null, title, description: input.description, type: input.type, priority: input.priority });
|
|
66
|
+
tx.recordAgentAction({
|
|
67
|
+
entityType: "task",
|
|
68
|
+
entityId: task.id,
|
|
69
|
+
operation: "create",
|
|
70
|
+
before: null,
|
|
71
|
+
after: tx.store.taskSnapshot(task.id),
|
|
72
|
+
summary: `Tâche créée depuis l'idée : ${quote(task.title)}`,
|
|
73
|
+
taskId: task.id,
|
|
74
|
+
});
|
|
75
|
+
const promoted = tx.updateIdea(idea.id, { promotedTaskId: task.id });
|
|
76
|
+
tx.recordAgentAction({
|
|
77
|
+
entityType: "idea",
|
|
78
|
+
entityId: idea.id,
|
|
79
|
+
operation: "update",
|
|
80
|
+
before: idea,
|
|
81
|
+
after: promoted,
|
|
82
|
+
summary: `Idée promue en ${taskKey(tx.store, task)}`,
|
|
83
|
+
taskId: null,
|
|
84
|
+
});
|
|
85
|
+
return { idea: promoted, task };
|
|
86
|
+
});
|
|
87
|
+
return respond(PromoteIdeaDataSchema, `Idea ${idea.id} promoted to ${taskKey(store, task)} "${task.title}" (id ${task.id}).`, {
|
|
88
|
+
idea: ideaData(store, idea),
|
|
89
|
+
task: taskData(store, task),
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
];
|