@imenam/mcp-organizer 1.0.2 → 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/README.md +609 -548
- package/dist/shared/schemas/admin-api.schema.d.ts +101 -0
- package/dist/shared/schemas/admin-api.schema.js +39 -0
- package/dist/shared/schemas/agent-api.schema.d.ts +2 -0
- package/dist/shared/schemas/agent-api.schema.js +4 -0
- package/dist/shared/schemas/agent-tools.schema.d.ts +40 -1
- package/dist/shared/schemas/agent-tools.schema.js +8 -1
- package/dist/shared/schemas/api.schema.d.ts +2 -0
- package/dist/shared/schemas/api.schema.js +2 -0
- package/dist/shared/schemas/entities.schema.d.ts +27 -1
- package/dist/shared/schemas/entities.schema.js +21 -2
- package/dist/shared/schemas/notifications-api.schema.d.ts +31 -0
- package/dist/shared/schemas/notifications-api.schema.js +22 -1
- package/dist/src/agent-api/notifications.js +1 -0
- package/dist/src/agent-api/requests.js +2 -1
- package/dist/src/cli/set-admin.d.ts +1 -0
- package/dist/src/cli/set-admin.js +45 -0
- package/dist/src/cli/watch.d.ts +0 -3
- package/dist/src/cli/watch.js +1 -9
- package/dist/src/core/accounts.js +2 -1
- package/dist/src/core/admin.d.ts +24 -0
- package/dist/src/core/admin.js +85 -0
- package/dist/src/core/agent-tokens.d.ts +9 -1
- package/dist/src/core/agent-tokens.js +25 -2
- package/dist/src/core/agent.d.ts +5 -0
- package/dist/src/core/agent.js +23 -0
- package/dist/src/core/migrations.js +369 -344
- package/dist/src/core/notifications.d.ts +7 -1
- package/dist/src/core/notifications.js +32 -1
- package/dist/src/core/requests.d.ts +1 -1
- package/dist/src/core/requests.js +4 -4
- package/dist/src/core/store.d.ts +38 -2
- package/dist/src/core/store.js +161 -38
- package/dist/src/gui-api.d.ts +6 -0
- package/dist/src/gui-api.js +25 -4
- package/dist/src/index.js +8 -1
- package/dist/src/mcp/notifications.d.ts +22 -5
- package/dist/src/mcp/notifications.js +95 -9
- package/dist/src/mcp/start.d.ts +1 -1
- package/dist/src/mcp/start.js +39 -32
- package/dist/src/mcp/tools.js +1 -1
- package/dist/src/server/runner.d.ts +51 -0
- package/dist/src/server/runner.js +222 -0
- package/dist/src/server/start.js +8 -0
- package/gui/dist/assets/index-DuoSNpBr.js +70 -0
- package/gui/dist/assets/index-duxL0fvO.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -2
- package/gui/dist/assets/index-BVN0agCc.css +0 -1
- package/gui/dist/assets/index-DI1_kr81.js +0 -70
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { normalizeEmail } from "./accounts.js";
|
|
2
|
+
import { userLabel } from "./agent.js";
|
|
3
|
+
import { ForbiddenError, InvariantError, NotFoundError, OrganizerError } from "./errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Administration du site : rôle d'administrateur des comptes, et
|
|
6
|
+
* autorisation du runner par espace. Seul un administrateur voit tous les
|
|
7
|
+
* comptes et tous les espaces, et décide des espaces où le runner travaille.
|
|
8
|
+
*/
|
|
9
|
+
/** L'utilisateur est administrateur ; sinon, refus. */
|
|
10
|
+
export function requireAdmin(store, userId) {
|
|
11
|
+
const user = store.getUser(userId);
|
|
12
|
+
if (!user.isAdmin)
|
|
13
|
+
throw new ForbiddenError("Réservé aux administrateurs du site.");
|
|
14
|
+
return user;
|
|
15
|
+
}
|
|
16
|
+
function spaceView(store, space) {
|
|
17
|
+
return {
|
|
18
|
+
id: space.id,
|
|
19
|
+
name: space.name,
|
|
20
|
+
kind: space.kind,
|
|
21
|
+
keyPrefix: space.keyPrefix,
|
|
22
|
+
runnerAllowed: space.runnerAllowed,
|
|
23
|
+
members: store.listMemberships(space.id).map((membership) => {
|
|
24
|
+
const member = store.getUser(membership.userId);
|
|
25
|
+
return { id: member.id, name: userLabel(member), email: member.email };
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Tous les comptes, chacun avec les espaces dont il est propriétaire, et l'état du runner. */
|
|
30
|
+
export function listAdminSpaces(store, userId, runner) {
|
|
31
|
+
requireAdmin(store, userId);
|
|
32
|
+
const spaces = store.listAllSpaces();
|
|
33
|
+
const users = store.listUsers().map((user) => ({
|
|
34
|
+
id: user.id,
|
|
35
|
+
email: user.email,
|
|
36
|
+
displayName: user.displayName,
|
|
37
|
+
status: user.status,
|
|
38
|
+
isAdmin: user.isAdmin,
|
|
39
|
+
spaces: spaces.filter((space) => space.ownerId === user.id).map((space) => spaceView(store, space)),
|
|
40
|
+
}));
|
|
41
|
+
return { users, allowedCount: spaces.filter((space) => space.runnerAllowed).length, spaceCount: spaces.length, runner };
|
|
42
|
+
}
|
|
43
|
+
/** Autorise ou interdit le runner sur un espace. */
|
|
44
|
+
export function setRunnerAllowed(store, userId, spaceId, allowed) {
|
|
45
|
+
return store.mutate({ type: "user", userId }, (tx) => {
|
|
46
|
+
requireAdmin(tx.store, userId);
|
|
47
|
+
if (!tx.store.findSpace(spaceId))
|
|
48
|
+
throw new NotFoundError("Espace introuvable.");
|
|
49
|
+
return spaceView(tx.store, tx.setSpaceRunnerAllowed(spaceId, allowed));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fait traiter une fois la file d'un espace par le runner, qu'il y soit
|
|
54
|
+
* autorisé ou non ; refus quand aucune demande n'y attend.
|
|
55
|
+
*/
|
|
56
|
+
export function processQueueNow(store, userId, spaceId, runNow) {
|
|
57
|
+
requireAdmin(store, userId);
|
|
58
|
+
const space = store.findSpace(spaceId);
|
|
59
|
+
if (!space)
|
|
60
|
+
throw new NotFoundError("Espace introuvable.");
|
|
61
|
+
const pendingCount = store.countPendingRequestsOfSpace(spaceId);
|
|
62
|
+
if (pendingCount === 0)
|
|
63
|
+
throw new InvariantError(`Aucune demande en attente dans « ${space.name} ».`);
|
|
64
|
+
runNow(spaceId);
|
|
65
|
+
return { pendingCount };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Accorde ou retire le rôle d'administrateur au compte actif `email`. Le
|
|
69
|
+
* dernier administrateur garde son rôle : le site n'en reste jamais dépourvu.
|
|
70
|
+
*/
|
|
71
|
+
export function setAdmin(store, email, isAdmin) {
|
|
72
|
+
const normalized = normalizeEmail(email);
|
|
73
|
+
const target = store.findUserByEmail(normalized);
|
|
74
|
+
if (!target)
|
|
75
|
+
throw new NotFoundError(`Aucun compte pour ${normalized} : créez-le d'abord (--set-user ${normalized}).`);
|
|
76
|
+
return store.mutate({ type: "user", userId: target.id }, (tx) => {
|
|
77
|
+
const user = tx.store.getUser(target.id);
|
|
78
|
+
if (isAdmin && user.status !== "active")
|
|
79
|
+
throw new OrganizerError(`Le compte ${normalized} n'est pas actif : activez-le d'abord (--set-user ${normalized}).`);
|
|
80
|
+
if (!isAdmin && user.isAdmin && tx.store.countAdmins() === 1) {
|
|
81
|
+
throw new InvariantError(`${normalized} est le seul administrateur : désignez-en un autre (--set-admin <email>) avant de lui retirer ce rôle.`);
|
|
82
|
+
}
|
|
83
|
+
return tx.setUserAdmin(user.id, isAdmin);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -7,7 +7,7 @@ export declare function generateAgentToken(): string;
|
|
|
7
7
|
export declare function hashAgentToken(token: string): string;
|
|
8
8
|
/** État d'un jeton à l'instant `now` ; la révocation l'emporte sur l'expiration, l'expiration sur la perte d'accès. */
|
|
9
9
|
export declare function tokenState(store: Store, token: AgentToken, now: Date): TokenState;
|
|
10
|
-
/** Jetons de l'utilisateur : actifs d'abord, puis du plus récent au plus ancien. */
|
|
10
|
+
/** Jetons de l'utilisateur, sans ceux du runner : actifs d'abord, puis du plus récent au plus ancien. */
|
|
11
11
|
export declare function listAgentTokens(store: Store, userId: string, now?: Date): TokenListResponse;
|
|
12
12
|
/**
|
|
13
13
|
* Crée un jeton de l'utilisateur sur un espace dont il est membre ; renvoie
|
|
@@ -15,6 +15,14 @@ export declare function listAgentTokens(store: Store, userId: string, now?: Date
|
|
|
15
15
|
* relié au site `publicUrl`.
|
|
16
16
|
*/
|
|
17
17
|
export declare function createAgentToken(store: Store, userId: string, input: CreateTokenRequest, publicUrl: string, now?: Date): CreatedTokenResponse;
|
|
18
|
+
/** Nom du jeton du runner, affiché dans la page Agent et l'historique. */
|
|
19
|
+
export declare const RUNNER_TOKEN_NAME = "Runner";
|
|
20
|
+
/**
|
|
21
|
+
* Donne une nouvelle valeur au jeton du runner de l'espace, en le créant au
|
|
22
|
+
* nom du propriétaire de l'espace s'il n'existe pas, et la renvoie. L'ancienne
|
|
23
|
+
* valeur est aussitôt refusée.
|
|
24
|
+
*/
|
|
25
|
+
export declare function issueRunnerToken(store: Store, spaceId: string): string;
|
|
18
26
|
/** Révoque un jeton de l'utilisateur ; un jeton d'un autre est introuvable. */
|
|
19
27
|
export declare function revokeAgentToken(store: Store, userId: string, tokenId: string, now?: Date): AgentTokenView;
|
|
20
28
|
/** Jeton résolu par l'API agent : le jeton, son propriétaire et l'espace couvert. */
|
|
@@ -49,9 +49,12 @@ function tokenView(store, token, now) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
const STATE_ORDER = { active: 0, inactive: 1, expired: 2, revoked: 3 };
|
|
52
|
-
/** Jetons de l'utilisateur : actifs d'abord, puis du plus récent au plus ancien. */
|
|
52
|
+
/** Jetons de l'utilisateur, sans ceux du runner : actifs d'abord, puis du plus récent au plus ancien. */
|
|
53
53
|
export function listAgentTokens(store, userId, now = new Date()) {
|
|
54
|
-
const tokens = store
|
|
54
|
+
const tokens = store
|
|
55
|
+
.listAgentTokensOf(userId)
|
|
56
|
+
.filter((token) => !token.isRunner)
|
|
57
|
+
.map((token) => tokenView(store, token, now));
|
|
55
58
|
tokens.sort((a, b) => STATE_ORDER[a.state] - STATE_ORDER[b.state] || b.createdAt.localeCompare(a.createdAt));
|
|
56
59
|
return { tokens };
|
|
57
60
|
}
|
|
@@ -74,6 +77,26 @@ export function createAgentToken(store, userId, input, publicUrl, now = new Date
|
|
|
74
77
|
});
|
|
75
78
|
return { token: tokenView(store, token, now), value, mcpJson: mcpJsonText(buildAgentEntry({ url: publicUrl, token: value })) };
|
|
76
79
|
}
|
|
80
|
+
/** Nom du jeton du runner, affiché dans la page Agent et l'historique. */
|
|
81
|
+
export const RUNNER_TOKEN_NAME = "Runner";
|
|
82
|
+
/**
|
|
83
|
+
* Donne une nouvelle valeur au jeton du runner de l'espace, en le créant au
|
|
84
|
+
* nom du propriétaire de l'espace s'il n'existe pas, et la renvoie. L'ancienne
|
|
85
|
+
* valeur est aussitôt refusée.
|
|
86
|
+
*/
|
|
87
|
+
export function issueRunnerToken(store, spaceId) {
|
|
88
|
+
const value = generateAgentToken();
|
|
89
|
+
const prefix = value.slice(0, AGENT_TOKEN_PREFIX.length + PREFIX_RANDOM_CHARS);
|
|
90
|
+
const space = store.getSpace(spaceId);
|
|
91
|
+
store.mutate(asUser(space.ownerId), (tx) => {
|
|
92
|
+
const existing = tx.store.findRunnerToken(spaceId);
|
|
93
|
+
if (existing)
|
|
94
|
+
tx.rotateRunnerToken(existing.id, prefix, hashAgentToken(value));
|
|
95
|
+
else
|
|
96
|
+
tx.createAgentToken({ spaceId, name: RUNNER_TOKEN_NAME, prefix, tokenHash: hashAgentToken(value), expiresAt: null, isRunner: true });
|
|
97
|
+
});
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
77
100
|
/** Révoque un jeton de l'utilisateur ; un jeton d'un autre est introuvable. */
|
|
78
101
|
export function revokeAgentToken(store, userId, tokenId, now = new Date()) {
|
|
79
102
|
const token = store.mutate(asUser(userId), (tx) => {
|
package/dist/src/core/agent.d.ts
CHANGED
|
@@ -44,6 +44,11 @@ export declare function memberLines(store: Store, space: Space): string[];
|
|
|
44
44
|
export declare function ago(iso: string, now?: Date): string;
|
|
45
45
|
/** Résumé d'une demande pour list_requests. */
|
|
46
46
|
export declare function requestLine(store: Store, agent: AgentCaller, request: AgentRequest): string;
|
|
47
|
+
/**
|
|
48
|
+
* Échanges précédents de l'auteur d'un message dans l'espace visé, du plus ancien
|
|
49
|
+
* au plus récent, au nombre fixé dans ses paramètres ; aucun pour les autres types.
|
|
50
|
+
*/
|
|
51
|
+
export declare function requestHistory(store: Store, request: AgentRequest): AgentRequest[];
|
|
47
52
|
/** Contexte complet d'une demande, renvoyé par claim_request. */
|
|
48
53
|
export declare function requestContext(store: Store, agent: AgentCaller, request: AgentRequest): string;
|
|
49
54
|
/** Idées actives récentes de la boîte à idées d'un espace. */
|
package/dist/src/core/agent.js
CHANGED
|
@@ -168,6 +168,23 @@ const KIND_GUIDANCE = {
|
|
|
168
168
|
test_review: "A test review written by a tester of the context task: feed it back into the existing tasks (status, checklist, comments, priorities) or create a task for each problem found.",
|
|
169
169
|
idea_promotion: "Promote the context idea into a well-formed task (clear title, description, type, priority, parent if relevant) with promote_idea.",
|
|
170
170
|
};
|
|
171
|
+
/**
|
|
172
|
+
* Échanges précédents de l'auteur d'un message dans l'espace visé, du plus ancien
|
|
173
|
+
* au plus récent, au nombre fixé dans ses paramètres ; aucun pour les autres types.
|
|
174
|
+
*/
|
|
175
|
+
export function requestHistory(store, request) {
|
|
176
|
+
if (request.kind !== "message")
|
|
177
|
+
return [];
|
|
178
|
+
const limit = store.getUserSettings(request.authorId).assistantHistory;
|
|
179
|
+
return store.listClosedRequestsOfAuthorBefore(request.targetSpaceId, request.authorId, request, limit);
|
|
180
|
+
}
|
|
181
|
+
/** Lignes d'un échange précédent : demande de l'auteur, puis réponse ou raison de l'échec. */
|
|
182
|
+
function historyLines(store, request) {
|
|
183
|
+
const task = request.contextTaskId === null ? null : store.findTask(request.contextTaskId);
|
|
184
|
+
const about = [request.kind !== "message" ? request.kind : null, task ? `context ${taskKey(store, task)}` : null].filter(Boolean).join(", ");
|
|
185
|
+
const answer = request.status === "done" ? `Reply: ${request.reply ?? ""}` : `Failed: ${request.failureReason ?? ""}`;
|
|
186
|
+
return [`- ${ago(request.createdAt)}${about ? ` (${about})` : ""}: ${request.text.trim()}`, ` ${answer.trim()}`];
|
|
187
|
+
}
|
|
171
188
|
/** Contexte complet d'une demande, renvoyé par claim_request. */
|
|
172
189
|
export function requestContext(store, agent, request) {
|
|
173
190
|
const author = store.getUser(request.authorId);
|
|
@@ -201,6 +218,12 @@ export function requestContext(store, agent, request) {
|
|
|
201
218
|
if (idea)
|
|
202
219
|
lines.push(`Context idea: ${idea.id} "${idea.text}".`);
|
|
203
220
|
}
|
|
221
|
+
const history = requestHistory(store, request);
|
|
222
|
+
if (history.length > 0) {
|
|
223
|
+
lines.push("", `Previous exchanges of the author in this space (oldest first, ${history.length} most recent) — use them to understand the message:`);
|
|
224
|
+
for (const previous of history)
|
|
225
|
+
lines.push(...historyLines(store, previous));
|
|
226
|
+
}
|
|
204
227
|
lines.push("", "Text:", request.text, "");
|
|
205
228
|
lines.push(...spaceContextLines(store, target));
|
|
206
229
|
lines.push("", "Next: read with get_task / search_tasks, act with the write tools (pass this request_id), then call complete_request with a short reply in the author's language.");
|