@lastbrain/module-ai 0.1.0 → 0.1.2

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.
Files changed (34) hide show
  1. package/dist/ai.build.config.js +11 -11
  2. package/dist/api/admin/user-token/[id].d.ts.map +1 -1
  3. package/dist/api/admin/user-token/[id].js +5 -14
  4. package/dist/api/admin/user-token.d.ts +1 -1
  5. package/dist/api/admin/user-token.d.ts.map +1 -1
  6. package/dist/api/admin/user-token.js +9 -30
  7. package/dist/api/admin/user_prompts.d.ts +1 -1
  8. package/dist/api/admin/user_prompts.d.ts.map +1 -1
  9. package/dist/api/admin/user_prompts.js +9 -14
  10. package/dist/api/admin/user_token_ledger.d.ts +1 -1
  11. package/dist/api/admin/user_token_ledger.d.ts.map +1 -1
  12. package/dist/api/admin/user_token_ledger.js +8 -10
  13. package/dist/api/auth/generate-image.d.ts +1 -1
  14. package/dist/api/auth/generate-image.d.ts.map +1 -1
  15. package/dist/api/auth/generate-image.js +10 -12
  16. package/dist/api/auth/generate-text.d.ts.map +1 -1
  17. package/dist/api/auth/generate-text.js +10 -9
  18. package/dist/api/auth/user_prompts.d.ts +1 -1
  19. package/dist/api/auth/user_prompts.d.ts.map +1 -1
  20. package/dist/api/auth/user_prompts.js +9 -14
  21. package/dist/api/auth/user_token_ledger.d.ts +1 -1
  22. package/dist/api/auth/user_token_ledger.d.ts.map +1 -1
  23. package/dist/api/auth/user_token_ledger.js +8 -10
  24. package/dist/components/Doc.js +1 -1
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/server.d.ts +3 -3
  28. package/dist/server.d.ts.map +1 -1
  29. package/dist/server.js +60 -40
  30. package/dist/web/components/ImageGenerative.d.ts.map +1 -1
  31. package/dist/web/components/ImageGenerative.js +1 -1
  32. package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
  33. package/dist/web/components/TextareaGenerative.js +1 -1
  34. package/package.json +7 -5
@@ -15,20 +15,20 @@ const buildConfig = {
15
15
  section: "admin",
16
16
  path: "/user-token/[id]",
17
17
  componentExport: "UserTokenIdPage",
18
- }
18
+ },
19
19
  ],
20
20
  apis: [
21
21
  // Routes de génération IA
22
22
  {
23
23
  method: "POST",
24
- path: "/api/auth/generate-text",
24
+ path: "/api/ai/generate-text",
25
25
  handlerExport: "POST",
26
26
  entryPoint: "api/auth/generate-text",
27
27
  authRequired: true,
28
28
  },
29
29
  {
30
30
  method: "POST",
31
- path: "/api/auth/generate-image",
31
+ path: "/api/ai/generate-image",
32
32
  handlerExport: "POST",
33
33
  entryPoint: "api/auth/generate-image",
34
34
  authRequired: true,
@@ -36,21 +36,21 @@ const buildConfig = {
36
36
  // Routes admin pour la gestion des tokens
37
37
  {
38
38
  method: "GET",
39
- path: "/api/admin/user-token",
39
+ path: "/api/ai/admin/user-token",
40
40
  handlerExport: "GET",
41
41
  entryPoint: "api/admin/user-token",
42
42
  authRequired: true,
43
43
  },
44
44
  {
45
45
  method: "POST",
46
- path: "/api/admin/user-token",
46
+ path: "/api/ai/admin/user-token",
47
47
  handlerExport: "POST",
48
48
  entryPoint: "api/admin/user-token",
49
49
  authRequired: true,
50
50
  },
51
51
  {
52
52
  method: "GET",
53
- path: "/api/admin/user-token/[id]",
53
+ path: "/api/ai/admin/user-token/[id]",
54
54
  handlerExport: "GET",
55
55
  entryPoint: "api/admin/user-token/[id]",
56
56
  authRequired: true,
@@ -68,19 +68,19 @@ const buildConfig = {
68
68
  title: "Mes Tokens",
69
69
  description: "Historique et balance des tokens IA",
70
70
  icon: "Coins",
71
- path: "/auth/token",
71
+ path: "/auth/ai/token",
72
72
  order: 100,
73
- }
73
+ },
74
74
  ],
75
75
  admin: [
76
76
  {
77
77
  title: "Gestion Tokens",
78
78
  description: "Gestion des tokens utilisateurs",
79
79
  icon: "DatabaseZap",
80
- path: "/admin/user-token",
80
+ path: "/admin/ai/user-token",
81
81
  order: 100,
82
- }
83
- ]
82
+ },
83
+ ],
84
84
  },
85
85
  };
86
86
  export default buildConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"[id].d.ts","sourceRoot":"","sources":["../../../../src/api/admin/user-token/[id].ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CACvB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;IA6E3C"}
1
+ {"version":3,"file":"[id].d.ts","sourceRoot":"","sources":["../../../../src/api/admin/user-token/[id].ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASxD,wBAAsB,GAAG,CACvB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;IA2D3C"}
@@ -1,24 +1,15 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { getSupabaseServerClient } from "@lastbrain/core/server";
3
- import { getTokenBalance, getTokenHistory, getTokenStats } from "../../../server";
4
- // GET /api/admin/user-token/[id] - Détails d'un utilisateur spécifique
3
+ import { getTokenBalance, getTokenHistory, getTokenStats, } from "../../../server";
4
+ // GET /api/ai/admin/user-token/[id] - Détails d'un utilisateur spécifique
5
5
  export async function GET(request, props) {
6
6
  const params = await props.params;
7
7
  const { id } = params;
8
8
  try {
9
9
  const supabase = await getSupabaseServerClient();
10
10
  // Vérifier l'authentification et les permissions admin
11
- const { data: { user }, error: authError, } = await supabase.auth.getUser();
12
- if (authError || !user) {
13
- return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
14
- }
15
- // Vérifier si l'utilisateur est superadmin
16
- const { data: isSuperAdmin } = await supabase.rpc("is_superadmin", {
17
- user_id: user.id,
18
- });
19
- if (!isSuperAdmin) {
20
- return NextResponse.json({ error: "Accès refusé. Droits administrateur requis." }, { status: 403 });
21
- }
11
+ const { data: { user } } = await supabase.auth.getUser();
12
+ // L'authentification et les droits superadmin sont déjà vérifiés par le middleware
22
13
  const targetUserId = id;
23
14
  // Récupérer les informations de l'utilisateur
24
15
  const { data: targetUser, error: userError } = await supabase.auth.admin.getUserById(targetUserId);
@@ -51,7 +42,7 @@ export async function GET(request, props) {
51
42
  });
52
43
  }
53
44
  catch (error) {
54
- console.error("[GET /api/admin/user-token/[id]] Error:", error);
45
+ console.error("[GET /api/ai/admin/user-token/[id]] Error:", error);
55
46
  return NextResponse.json({ error: error.message || "Erreur serveur" }, { status: 500 });
56
47
  }
57
48
  }
@@ -1,5 +1,5 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
- export declare function GET(request: NextRequest): Promise<NextResponse<{
2
+ export declare function GET(_request: NextRequest): Promise<NextResponse<{
3
3
  users: {
4
4
  id: any;
5
5
  email: string;
@@ -1 +1 @@
1
- {"version":3,"file":"user-token.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;IA8D7C;AAGD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;IAsE9C"}
1
+ {"version":3,"file":"user-token.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CAAC,QAAQ,EAAE,WAAW;;;;;;;;;;IA0C9C;AAGD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;IAqD9C"}
@@ -1,22 +1,11 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { getSupabaseServerClient } from "@lastbrain/core/server";
3
3
  import { addTokens } from "../../server";
4
- // GET /api/admin/user-token - Liste tous les utilisateurs avec leur balance
5
- export async function GET(request) {
4
+ // GET /api/ai/admin/user-token - Liste tous les utilisateurs avec leur balance
5
+ export async function GET(_request) {
6
6
  try {
7
7
  const supabase = await getSupabaseServerClient();
8
- // Vérifier l'authentification et les permissions admin
9
- const { data: { user }, error: authError, } = await supabase.auth.getUser();
10
- if (authError || !user) {
11
- return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
12
- }
13
- // Vérifier si l'utilisateur est superadmin
14
- const { data: isSuperAdmin } = await supabase.rpc("is_superadmin", {
15
- user_id: user.id,
16
- });
17
- if (!isSuperAdmin) {
18
- return NextResponse.json({ error: "Accès refusé. Droits administrateur requis." }, { status: 403 });
19
- }
8
+ // L'authentification et les droits superadmin sont déjà vérifiés par le middleware
20
9
  // Récupérer tous les utilisateurs avec leur balance
21
10
  const { data: balances, error: balanceError } = await supabase
22
11
  .from("user_token_balance_v")
@@ -25,7 +14,7 @@ export async function GET(request) {
25
14
  if (balanceError)
26
15
  throw balanceError;
27
16
  // Récupérer les informations des utilisateurs
28
- const userIds = balances?.map((b) => b.owner_id) || [];
17
+ const _userIds = balances?.map((b) => b.owner_id) || [];
29
18
  const { data: users, error: usersError } = await supabase.auth.admin.listUsers();
30
19
  if (usersError)
31
20
  throw usersError;
@@ -45,26 +34,16 @@ export async function GET(request) {
45
34
  });
46
35
  }
47
36
  catch (error) {
48
- console.error("[GET /api/admin/user-token] Error:", error);
37
+ console.error("[GET /api/ai/admin/user-token] Error:", error);
49
38
  return NextResponse.json({ error: error.message || "Erreur serveur" }, { status: 500 });
50
39
  }
51
40
  }
52
- // POST /api/admin/user-token - Ajouter/retirer des tokens à un utilisateur
41
+ // POST /api/ai/admin/user-token - Ajouter/retirer des tokens à un utilisateur
53
42
  export async function POST(request) {
54
43
  try {
55
44
  const supabase = await getSupabaseServerClient();
56
- // Vérifier l'authentification et les permissions admin
57
- const { data: { user }, error: authError, } = await supabase.auth.getUser();
58
- if (authError || !user) {
59
- return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
60
- }
61
- // Vérifier si l'utilisateur est superadmin
62
- const { data: isSuperAdmin } = await supabase.rpc("is_superadmin", {
63
- user_id: user.id,
64
- });
65
- if (!isSuperAdmin) {
66
- return NextResponse.json({ error: "Accès refusé. Droits administrateur requis." }, { status: 403 });
67
- }
45
+ // L'authentification et les droits superadmin sont déjà vérifiés par le middleware
46
+ const { data: { user }, } = await supabase.auth.getUser();
68
47
  const body = await request.json();
69
48
  const { userId, amount, type, reason } = body;
70
49
  if (!userId || !amount) {
@@ -86,7 +65,7 @@ export async function POST(request) {
86
65
  });
87
66
  }
88
67
  catch (error) {
89
- console.error("[POST /api/admin/user-token] Error:", error);
68
+ console.error("[POST /api/ai/admin/user-token] Error:", error);
90
69
  return NextResponse.json({ error: error.message || "Erreur serveur" }, { status: 500 });
91
70
  }
92
71
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * GET - Liste tous les enregistrements de user_prompts
3
3
  */
4
- export declare function GET(request: Request): Promise<Response>;
4
+ export declare function GET(_request: Request): Promise<Response>;
5
5
  /**
6
6
  * POST - Crée un nouvel enregistrement dans user_prompts
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"user_prompts.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user_prompts.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBAkBzC;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAsB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA4BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA0B5C"}
1
+ {"version":3,"file":"user_prompts.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user_prompts.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,QAAQ,EAAE,OAAO,qBAkB1C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAwB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA8BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBAyB5C"}
@@ -2,23 +2,21 @@ import { getSupabaseServerClient } from "@lastbrain/core/server";
2
2
  const jsonResponse = (payload, status = 200) => {
3
3
  return new Response(JSON.stringify(payload), {
4
4
  headers: {
5
- "content-type": "application/json"
5
+ "content-type": "application/json",
6
6
  },
7
- status
7
+ status,
8
8
  });
9
9
  };
10
10
  /**
11
11
  * GET - Liste tous les enregistrements de user_prompts
12
12
  */
13
- export async function GET(request) {
13
+ export async function GET(_request) {
14
14
  const supabase = await getSupabaseServerClient();
15
- const { data: { user }, error: authError } = await supabase.auth.getUser();
15
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
16
16
  if (authError || !user) {
17
17
  return jsonResponse({ error: "Non authentifié" }, 401);
18
18
  }
19
- const { data, error } = await supabase
20
- .from("user_prompts")
21
- .select("*");
19
+ const { data, error } = await supabase.from("user_prompts").select("*");
22
20
  if (error) {
23
21
  return jsonResponse({ error: error.message }, 400);
24
22
  }
@@ -29,7 +27,7 @@ export async function GET(request) {
29
27
  */
30
28
  export async function POST(request) {
31
29
  const supabase = await getSupabaseServerClient();
32
- const { data: { user }, error: authError } = await supabase.auth.getUser();
30
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
33
31
  if (authError || !user) {
34
32
  return jsonResponse({ error: "Non authentifié" }, 401);
35
33
  }
@@ -49,7 +47,7 @@ export async function POST(request) {
49
47
  */
50
48
  export async function PUT(request) {
51
49
  const supabase = await getSupabaseServerClient();
52
- const { data: { user }, error: authError } = await supabase.auth.getUser();
50
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
53
51
  if (authError || !user) {
54
52
  return jsonResponse({ error: "Non authentifié" }, 401);
55
53
  }
@@ -74,7 +72,7 @@ export async function PUT(request) {
74
72
  */
75
73
  export async function DELETE(request) {
76
74
  const supabase = await getSupabaseServerClient();
77
- const { data: { user }, error: authError } = await supabase.auth.getUser();
75
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
78
76
  if (authError || !user) {
79
77
  return jsonResponse({ error: "Non authentifié" }, 401);
80
78
  }
@@ -83,10 +81,7 @@ export async function DELETE(request) {
83
81
  if (!id) {
84
82
  return jsonResponse({ error: "ID requis pour la suppression" }, 400);
85
83
  }
86
- const { error } = await supabase
87
- .from("user_prompts")
88
- .delete()
89
- .eq("id", id);
84
+ const { error } = await supabase.from("user_prompts").delete().eq("id", id);
90
85
  if (error) {
91
86
  return jsonResponse({ error: error.message }, 400);
92
87
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * GET - Liste tous les enregistrements de user_token_ledger
3
3
  */
4
- export declare function GET(request: Request): Promise<Response>;
4
+ export declare function GET(_request: Request): Promise<Response>;
5
5
  /**
6
6
  * POST - Crée un nouvel enregistrement dans user_token_ledger
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"user_token_ledger.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user_token_ledger.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBAkBzC;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAsB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA4BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA0B5C"}
1
+ {"version":3,"file":"user_token_ledger.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user_token_ledger.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,QAAQ,EAAE,OAAO,qBAkB1C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAwB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA8BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA4B5C"}
@@ -2,23 +2,21 @@ import { getSupabaseServerClient } from "@lastbrain/core/server";
2
2
  const jsonResponse = (payload, status = 200) => {
3
3
  return new Response(JSON.stringify(payload), {
4
4
  headers: {
5
- "content-type": "application/json"
5
+ "content-type": "application/json",
6
6
  },
7
- status
7
+ status,
8
8
  });
9
9
  };
10
10
  /**
11
11
  * GET - Liste tous les enregistrements de user_token_ledger
12
12
  */
13
- export async function GET(request) {
13
+ export async function GET(_request) {
14
14
  const supabase = await getSupabaseServerClient();
15
- const { data: { user }, error: authError } = await supabase.auth.getUser();
15
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
16
16
  if (authError || !user) {
17
17
  return jsonResponse({ error: "Non authentifié" }, 401);
18
18
  }
19
- const { data, error } = await supabase
20
- .from("user_token_ledger")
21
- .select("*");
19
+ const { data, error } = await supabase.from("user_token_ledger").select("*");
22
20
  if (error) {
23
21
  return jsonResponse({ error: error.message }, 400);
24
22
  }
@@ -29,7 +27,7 @@ export async function GET(request) {
29
27
  */
30
28
  export async function POST(request) {
31
29
  const supabase = await getSupabaseServerClient();
32
- const { data: { user }, error: authError } = await supabase.auth.getUser();
30
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
33
31
  if (authError || !user) {
34
32
  return jsonResponse({ error: "Non authentifié" }, 401);
35
33
  }
@@ -49,7 +47,7 @@ export async function POST(request) {
49
47
  */
50
48
  export async function PUT(request) {
51
49
  const supabase = await getSupabaseServerClient();
52
- const { data: { user }, error: authError } = await supabase.auth.getUser();
50
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
53
51
  if (authError || !user) {
54
52
  return jsonResponse({ error: "Non authentifié" }, 401);
55
53
  }
@@ -74,7 +72,7 @@ export async function PUT(request) {
74
72
  */
75
73
  export async function DELETE(request) {
76
74
  const supabase = await getSupabaseServerClient();
77
- const { data: { user }, error: authError } = await supabase.auth.getUser();
75
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
78
76
  if (authError || !user) {
79
77
  return jsonResponse({ error: "Non authentifié" }, 401);
80
78
  }
@@ -3,7 +3,7 @@ export declare function POST(request: NextRequest): Promise<NextResponse<{
3
3
  imageUrl: string;
4
4
  tokensUsed: number;
5
5
  tokensRemaining: number;
6
- model: "dall-e-3" | "dall-e-2";
6
+ model: "dall-e-2" | "dall-e-3";
7
7
  cost: number;
8
8
  }> | NextResponse<{
9
9
  error: any;
@@ -1 +1 @@
1
- {"version":3,"file":"generate-image.d.ts","sourceRoot":"","sources":["../../../src/api/auth/generate-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoCxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;IA2H9C"}
1
+ {"version":3,"file":"generate-image.d.ts","sourceRoot":"","sources":["../../../src/api/auth/generate-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoCxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;IA0H9C"}
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { getSupabaseServerClient } from "@lastbrain/core/server";
3
- import { useTokens, getTokenBalance } from "../../server";
3
+ import { deductTokens, getTokenBalance } from "../../server";
4
4
  import OpenAI from "openai";
5
5
  const openai = new OpenAI({
6
6
  apiKey: process.env.OPENAI_API_KEY,
@@ -27,19 +27,15 @@ export async function POST(request) {
27
27
  try {
28
28
  const supabase = await getSupabaseServerClient();
29
29
  // Vérifier l'authentification
30
- const { data: { user }, error: authError, } = await supabase.auth.getUser();
31
- if (authError || !user) {
32
- return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
33
- }
30
+ const { data: { user } } = await supabase.auth.getUser();
31
+ // L'utilisateur est déjà authentifié grâce au middleware
34
32
  const body = await request.json();
35
33
  const { prompt, model = "dall-e-3", size = "1024x1024", quality = "standard", } = body;
36
34
  if (!prompt) {
37
35
  return NextResponse.json({ error: "Le prompt est requis" }, { status: 400 });
38
36
  }
39
37
  // Calculer le coût et les tokens
40
- const costKey = model === "dall-e-3"
41
- ? `${model}-${size}-${quality}`
42
- : `${model}-${size}`;
38
+ const costKey = model === "dall-e-3" ? `${model}-${size}-${quality}` : `${model}-${size}`;
43
39
  const cost = IMAGE_COSTS[costKey] || 0.04;
44
40
  const tokenKey = model === "dall-e-3" ? `${model}-${quality}` : model;
45
41
  const tokensRequired = TOKENS_PER_IMAGE[tokenKey] || 1000;
@@ -74,14 +70,16 @@ export async function POST(request) {
74
70
  throw new Error("Aucune image générée");
75
71
  }
76
72
  // Déduire les tokens utilisés
77
- const tokenResult = await useTokens(user.id, tokensRequired, model, prompt, {
73
+ const tokenResult = await deductTokens(user.id, tokensRequired, model, prompt, {
78
74
  size,
79
75
  quality,
80
76
  cost,
81
77
  imageUrl,
82
78
  });
83
79
  if (!tokenResult.success) {
84
- return NextResponse.json({ error: tokenResult.error || "Erreur lors de la déduction des tokens" }, { status: 500 });
80
+ return NextResponse.json({
81
+ error: tokenResult.error || "Erreur lors de la déduction des tokens",
82
+ }, { status: 500 });
85
83
  }
86
84
  return NextResponse.json({
87
85
  imageUrl,
@@ -93,10 +91,10 @@ export async function POST(request) {
93
91
  }
94
92
  catch (error) {
95
93
  console.error("Erreur de génération d'image:", error);
96
- if (error.code === 'insufficient_quota') {
94
+ if (error.code === "insufficient_quota") {
97
95
  return NextResponse.json({ error: "Quota OpenAI dépassé. Contactez l'administrateur." }, { status: 503 });
98
96
  }
99
- if (error.code === 'content_policy_violation') {
97
+ if (error.code === "content_policy_violation") {
100
98
  return NextResponse.json({ error: "Le prompt viole la politique de contenu d'OpenAI." }, { status: 400 });
101
99
  }
102
100
  return NextResponse.json({ error: error.message || "Erreur lors de la génération de l'image" }, { status: 500 });
@@ -1 +1 @@
1
- {"version":3,"file":"generate-text.d.ts","sourceRoot":"","sources":["../../../src/api/auth/generate-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;IA+H9C"}
1
+ {"version":3,"file":"generate-text.d.ts","sourceRoot":"","sources":["../../../src/api/auth/generate-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;IA4H9C"}
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { getSupabaseServerClient } from "@lastbrain/core/server";
3
- import { useTokens, getTokenBalance } from "../../server";
3
+ import { deductTokens, getTokenBalance } from "../../server";
4
4
  import OpenAI from "openai";
5
5
  const openai = new OpenAI({
6
6
  apiKey: process.env.OPENAI_API_KEY,
@@ -9,10 +9,8 @@ export async function POST(request) {
9
9
  try {
10
10
  const supabase = await getSupabaseServerClient();
11
11
  // Vérifier l'authentification
12
- const { data: { user }, error: authError, } = await supabase.auth.getUser();
13
- if (authError || !user) {
14
- return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
15
- }
12
+ const { data: { user } } = await supabase.auth.getUser();
13
+ // L'utilisateur est déjà authentifié grâce au middleware
16
14
  const body = await request.json();
17
15
  const { prompt, model = "gpt-4o-mini", context, maxTokens = 2000, temperature = 0.7, } = body;
18
16
  if (!prompt) {
@@ -57,7 +55,8 @@ export async function POST(request) {
57
55
  const outputTokens = completion.usage?.completion_tokens || 0;
58
56
  let cost = 0;
59
57
  if (model.includes("gpt-4o-mini")) {
60
- cost = (inputTokens / 1_000_000) * 0.15 + (outputTokens / 1_000_000) * 0.6;
58
+ cost =
59
+ (inputTokens / 1_000_000) * 0.15 + (outputTokens / 1_000_000) * 0.6;
61
60
  }
62
61
  else if (model.includes("gpt-4o")) {
63
62
  cost = (inputTokens / 1_000_000) * 2.5 + (outputTokens / 1_000_000) * 10;
@@ -69,14 +68,16 @@ export async function POST(request) {
69
68
  cost = (tokensUsed / 1_000_000) * 2; // Fallback
70
69
  }
71
70
  // Déduire les tokens utilisés
72
- const tokenResult = await useTokens(user.id, tokensUsed, model, prompt, {
71
+ const tokenResult = await deductTokens(user.id, tokensUsed, model, prompt, {
73
72
  inputTokens,
74
73
  outputTokens,
75
74
  cost,
76
75
  generatedText: generatedText.substring(0, 500), // Stocker un extrait
77
76
  });
78
77
  if (!tokenResult.success) {
79
- return NextResponse.json({ error: tokenResult.error || "Erreur lors de la déduction des tokens" }, { status: 500 });
78
+ return NextResponse.json({
79
+ error: tokenResult.error || "Erreur lors de la déduction des tokens",
80
+ }, { status: 500 });
80
81
  }
81
82
  return NextResponse.json({
82
83
  text: generatedText,
@@ -88,7 +89,7 @@ export async function POST(request) {
88
89
  }
89
90
  catch (error) {
90
91
  console.error("Erreur de génération:", error);
91
- if (error.code === 'insufficient_quota') {
92
+ if (error.code === "insufficient_quota") {
92
93
  return NextResponse.json({ error: "Quota OpenAI dépassé. Contactez l'administrateur." }, { status: 503 });
93
94
  }
94
95
  return NextResponse.json({ error: error.message || "Erreur lors de la génération" }, { status: 500 });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * GET - Liste tous les enregistrements de user_prompts
3
3
  */
4
- export declare function GET(request: Request): Promise<Response>;
4
+ export declare function GET(_request: Request): Promise<Response>;
5
5
  /**
6
6
  * POST - Crée un nouvel enregistrement dans user_prompts
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"user_prompts.d.ts","sourceRoot":"","sources":["../../../src/api/auth/user_prompts.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBAkBzC;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAsB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA4BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA0B5C"}
1
+ {"version":3,"file":"user_prompts.d.ts","sourceRoot":"","sources":["../../../src/api/auth/user_prompts.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,QAAQ,EAAE,OAAO,qBAkB1C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAwB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA8BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBAyB5C"}
@@ -2,23 +2,21 @@ import { getSupabaseServerClient } from "@lastbrain/core/server";
2
2
  const jsonResponse = (payload, status = 200) => {
3
3
  return new Response(JSON.stringify(payload), {
4
4
  headers: {
5
- "content-type": "application/json"
5
+ "content-type": "application/json",
6
6
  },
7
- status
7
+ status,
8
8
  });
9
9
  };
10
10
  /**
11
11
  * GET - Liste tous les enregistrements de user_prompts
12
12
  */
13
- export async function GET(request) {
13
+ export async function GET(_request) {
14
14
  const supabase = await getSupabaseServerClient();
15
- const { data: { user }, error: authError } = await supabase.auth.getUser();
15
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
16
16
  if (authError || !user) {
17
17
  return jsonResponse({ error: "Non authentifié" }, 401);
18
18
  }
19
- const { data, error } = await supabase
20
- .from("user_prompts")
21
- .select("*");
19
+ const { data, error } = await supabase.from("user_prompts").select("*");
22
20
  if (error) {
23
21
  return jsonResponse({ error: error.message }, 400);
24
22
  }
@@ -29,7 +27,7 @@ export async function GET(request) {
29
27
  */
30
28
  export async function POST(request) {
31
29
  const supabase = await getSupabaseServerClient();
32
- const { data: { user }, error: authError } = await supabase.auth.getUser();
30
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
33
31
  if (authError || !user) {
34
32
  return jsonResponse({ error: "Non authentifié" }, 401);
35
33
  }
@@ -49,7 +47,7 @@ export async function POST(request) {
49
47
  */
50
48
  export async function PUT(request) {
51
49
  const supabase = await getSupabaseServerClient();
52
- const { data: { user }, error: authError } = await supabase.auth.getUser();
50
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
53
51
  if (authError || !user) {
54
52
  return jsonResponse({ error: "Non authentifié" }, 401);
55
53
  }
@@ -74,7 +72,7 @@ export async function PUT(request) {
74
72
  */
75
73
  export async function DELETE(request) {
76
74
  const supabase = await getSupabaseServerClient();
77
- const { data: { user }, error: authError } = await supabase.auth.getUser();
75
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
78
76
  if (authError || !user) {
79
77
  return jsonResponse({ error: "Non authentifié" }, 401);
80
78
  }
@@ -83,10 +81,7 @@ export async function DELETE(request) {
83
81
  if (!id) {
84
82
  return jsonResponse({ error: "ID requis pour la suppression" }, 400);
85
83
  }
86
- const { error } = await supabase
87
- .from("user_prompts")
88
- .delete()
89
- .eq("id", id);
84
+ const { error } = await supabase.from("user_prompts").delete().eq("id", id);
90
85
  if (error) {
91
86
  return jsonResponse({ error: error.message }, 400);
92
87
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * GET - Liste tous les enregistrements de user_token_ledger
3
3
  */
4
- export declare function GET(request: Request): Promise<Response>;
4
+ export declare function GET(_request: Request): Promise<Response>;
5
5
  /**
6
6
  * POST - Crée un nouvel enregistrement dans user_token_ledger
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"user_token_ledger.d.ts","sourceRoot":"","sources":["../../../src/api/auth/user_token_ledger.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBAkBzC;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAsB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA4BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA0B5C"}
1
+ {"version":3,"file":"user_token_ledger.d.ts","sourceRoot":"","sources":["../../../src/api/auth/user_token_ledger.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,QAAQ,EAAE,OAAO,qBAkB1C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAwB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA8BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA4B5C"}
@@ -2,23 +2,21 @@ import { getSupabaseServerClient } from "@lastbrain/core/server";
2
2
  const jsonResponse = (payload, status = 200) => {
3
3
  return new Response(JSON.stringify(payload), {
4
4
  headers: {
5
- "content-type": "application/json"
5
+ "content-type": "application/json",
6
6
  },
7
- status
7
+ status,
8
8
  });
9
9
  };
10
10
  /**
11
11
  * GET - Liste tous les enregistrements de user_token_ledger
12
12
  */
13
- export async function GET(request) {
13
+ export async function GET(_request) {
14
14
  const supabase = await getSupabaseServerClient();
15
- const { data: { user }, error: authError } = await supabase.auth.getUser();
15
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
16
16
  if (authError || !user) {
17
17
  return jsonResponse({ error: "Non authentifié" }, 401);
18
18
  }
19
- const { data, error } = await supabase
20
- .from("user_token_ledger")
21
- .select("*");
19
+ const { data, error } = await supabase.from("user_token_ledger").select("*");
22
20
  if (error) {
23
21
  return jsonResponse({ error: error.message }, 400);
24
22
  }
@@ -29,7 +27,7 @@ export async function GET(request) {
29
27
  */
30
28
  export async function POST(request) {
31
29
  const supabase = await getSupabaseServerClient();
32
- const { data: { user }, error: authError } = await supabase.auth.getUser();
30
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
33
31
  if (authError || !user) {
34
32
  return jsonResponse({ error: "Non authentifié" }, 401);
35
33
  }
@@ -49,7 +47,7 @@ export async function POST(request) {
49
47
  */
50
48
  export async function PUT(request) {
51
49
  const supabase = await getSupabaseServerClient();
52
- const { data: { user }, error: authError } = await supabase.auth.getUser();
50
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
53
51
  if (authError || !user) {
54
52
  return jsonResponse({ error: "Non authentifié" }, 401);
55
53
  }
@@ -74,7 +72,7 @@ export async function PUT(request) {
74
72
  */
75
73
  export async function DELETE(request) {
76
74
  const supabase = await getSupabaseServerClient();
77
- const { data: { user }, error: authError } = await supabase.auth.getUser();
75
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
78
76
  if (authError || !user) {
79
77
  return jsonResponse({ error: "Non authentifié" }, 401);
80
78
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Card, CardBody, CardHeader, Snippet, Chip, TableStructure, } from "@lastbrain/ui";
3
3
  export function AiModuleDoc() {
4
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-3xl font-bold mb-2", children: "Module AI" }), _jsx("p", { className: "text-slate-600 dark:text-slate-400", children: "G\u00E9n\u00E9ration IA (texte et images) avec syst\u00E8me de tokens et gestion des co\u00FBts" })] }), _jsx(Chip, { color: "primary", variant: "flat", children: "v0.1.0" })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCDD Informations" }) }), _jsxs(CardBody, { className: "space-y-2", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Auteur:" }), " LastBrain Team"] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Package:" }), " @lastbrain/module-ai"] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Derni\u00E8re mise \u00E0 jour:" }), " 21 novembre 2025"] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83C\uDFA8 Composants Disponibles" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "TextareaGenerative" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Textarea avec g\u00E9n\u00E9ration de texte IA int\u00E9gr\u00E9e. Bouton de g\u00E9n\u00E9ration avec s\u00E9lection de prompts." }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("span", { children: ["import ", "{ TextareaGenerative }", " from \"@lastbrain/module-ai\";"] }), _jsx("span", {}), _jsx("span", { children: "<TextareaGenerative" }), _jsx("span", { children: " label=\"Description\"" }), _jsx("span", { children: " placeholder=\"Entrez une description...\"" }), _jsxs("span", { children: [" value=", "{value}"] }), _jsxs("span", { children: [" onChange=", "{setValue}"] }), _jsx("span", { children: "/>" })] }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "ImageGenerative" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Composant de g\u00E9n\u00E9ration d'images avec DALL-E. Upload ou g\u00E9n\u00E9ration IA." }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("span", { children: ["import ", "{ ImageGenerative }", " from \"@lastbrain/module-ai\";"] }), _jsx("span", {}), _jsx("span", { children: "<ImageGenerative" }), _jsx("span", { children: " label=\"Image du produit\"" }), _jsxs("span", { children: [" value=", "{imageUrl}"] }), _jsxs("span", { children: [" onChange=", "{setImageUrl}"] }), _jsx("span", { children: "/>" })] }) })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCC4 Pages Disponibles" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Pages Prot\u00E9g\u00E9es (Auth)" }), _jsx("div", { className: "space-y-2", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "primary", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/auth/token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Historique et balance des tokens" })] }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Pages Admin" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Gestion des tokens utilisateurs" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/admin/user-token/[id]" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- D\u00E9tail d'un utilisateur" })] })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDD0C Routes API" }) }), _jsxs(CardBody, { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/auth/generate-text" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- G\u00E9n\u00E9ration de texte" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/auth/generate-image" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- G\u00E9n\u00E9ration d'image" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/api/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Liste des tokens utilisateurs" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Cr\u00E9er/modifier des tokens" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/api/admin/user-token/[id]" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- D\u00E9tails tokens utilisateur" })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCC1 Structure" }) }), _jsx(CardBody, { children: _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1 text-xs font-mono", children: [_jsx("span", { children: "module-ai/" }), _jsx("span", { children: "\u251C\u2500\u2500 src/" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 web/" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 TextareaGenerative.tsx" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 ImageGenerative.tsx" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 api/" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 generate-text.ts" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 generate-image.ts" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 prompts.ts" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 balance.ts" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 components/" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 Doc.tsx" }), _jsx("span", { children: "\u2502 \u2514\u2500\u2500 ai.build.config.ts" }), _jsx("span", { children: "\u2514\u2500\u2500 supabase/" }), _jsx("span", { children: " \u2514\u2500\u2500 migrations/" }), _jsx("span", { children: " \u251C\u2500\u2500 20251121000000_ai_tokens.sql" }), _jsx("span", { children: " \u2514\u2500\u2500 20251121093113_module-ai_init.sql" })] }) }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDDC4\uFE0F Tables de Donn\u00E9es" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsx(TableStructure, { tableName: "user_token_ledger", title: "user_token_ledger", description: "Comptabilit\u00E9 des tokens (cr\u00E9dits, d\u00E9bits, balance). Vue user_token_balance pour le solde actuel." }), _jsx(TableStructure, { tableName: "user_prompts", title: "user_prompts", description: "Biblioth\u00E8que de prompts r\u00E9utilisables par utilisateur (titre, contenu, type)." })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCE6 Installation" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "pnpm lastbrain add-module ai" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "pnpm build:modules" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "supabase migration up" })] }), _jsxs("div", { className: "border-2 border-danger rounded-lg p-4 mt-6", children: [_jsx("h4", { className: "text-lg font-semibold text-danger mb-3", children: "\u26A0\uFE0F Danger Zone" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "La suppression du module supprimera toutes les pages, routes API et migrations associ\u00E9es. Cette action est irr\u00E9versible." }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Snippet, { symbol: "", color: "danger", size: "sm", children: "pnpm lastbrain remove-module ai" }), _jsx(Snippet, { symbol: "", color: "danger", size: "sm", children: "pnpm build:modules" })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\u2699\uFE0F Configuration" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Ajoutez votre cl\u00E9 API OpenAI dans vos variables d'environnement :" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "OPENAI_API_KEY=sk-..." })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCB0 Syst\u00E8me de Tokens" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Co\u00FBts" }), _jsxs("ul", { className: "text-sm text-slate-600 dark:text-slate-400 space-y-1", children: [_jsx("li", { children: "\u2022 G\u00E9n\u00E9ration de texte : 10 tokens" }), _jsx("li", { children: "\u2022 G\u00E9n\u00E9ration d'image : 50 tokens" })] })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "Trigger de validation" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Un trigger emp\u00EAche les transactions si le solde devient n\u00E9gatif." })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "Ajouter des tokens" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { children: "INSERT INTO user_token_ledger" }), _jsx("span", { children: "(user_id, amount, transaction_type, description)" }), _jsx("span", { children: "VALUES" }), _jsx("span", { children: "(\"('user-uuid', 1000, 'credit', 'Achat de tokens');\")" })] }) })] })] })] })] }));
4
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-3xl font-bold mb-2", children: "Module AI" }), _jsx("p", { className: "text-slate-600 dark:text-slate-400", children: "G\u00E9n\u00E9ration IA (texte et images) avec syst\u00E8me de tokens et gestion des co\u00FBts" })] }), _jsx(Chip, { color: "primary", variant: "flat", children: "v0.1.0" })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCDD Informations" }) }), _jsxs(CardBody, { className: "space-y-2", children: [_jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Auteur:" }), " LastBrain Team"] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Package:" }), " @lastbrain/module-ai"] }), _jsxs("div", { children: [_jsx("span", { className: "font-semibold", children: "Derni\u00E8re mise \u00E0 jour:" }), " 21 novembre 2025"] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83C\uDFA8 Composants Disponibles" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "TextareaGenerative" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Textarea avec g\u00E9n\u00E9ration de texte IA int\u00E9gr\u00E9e. Bouton de g\u00E9n\u00E9ration avec s\u00E9lection de prompts." }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("span", { children: ["import ", "{ TextareaGenerative }", " from \"@lastbrain/module-ai\";"] }), _jsx("span", {}), _jsx("span", { children: "<TextareaGenerative" }), _jsx("span", { children: " label=\"Description\"" }), _jsx("span", { children: " placeholder=\"Entrez une description...\"" }), _jsxs("span", { children: [" value=", "{value}"] }), _jsxs("span", { children: [" onChange=", "{setValue}"] }), _jsx("span", { children: "/>" })] }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "ImageGenerative" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Composant de g\u00E9n\u00E9ration d'images avec DALL-E. Upload ou g\u00E9n\u00E9ration IA." }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("span", { children: ["import ", "{ ImageGenerative }", " from \"@lastbrain/module-ai\";"] }), _jsx("span", {}), _jsx("span", { children: "<ImageGenerative" }), _jsx("span", { children: " label=\"Image du produit\"" }), _jsxs("span", { children: [" value=", "{imageUrl}"] }), _jsxs("span", { children: [" onChange=", "{setImageUrl}"] }), _jsx("span", { children: "/>" })] }) })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCC4 Pages Disponibles" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Pages Prot\u00E9g\u00E9es (Auth)" }), _jsx("div", { className: "space-y-2", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "primary", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/auth/token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Historique et balance des tokens" })] }) })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Pages Admin" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Gestion des tokens utilisateurs" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "danger", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/admin/user-token/[id]" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- D\u00E9tail d'un utilisateur" })] })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDD0C Routes API" }) }), _jsxs(CardBody, { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/ai/generate-text" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- G\u00E9n\u00E9ration de texte" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/ai/generate-image" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- G\u00E9n\u00E9ration d'image" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/api/ai/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Liste des tokens utilisateurs" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "warning", variant: "flat", children: "POST" }), _jsx("code", { className: "text-sm", children: "/api/ai/admin/user-token" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- Cr\u00E9er/modifier des tokens" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Chip, { size: "sm", color: "success", variant: "flat", children: "GET" }), _jsx("code", { className: "text-sm", children: "/api/ai/admin/user-token/[id]" }), _jsx("span", { className: "text-slate-600 dark:text-slate-400", children: "- D\u00E9tails tokens utilisateur" })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCC1 Structure" }) }), _jsx(CardBody, { children: _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1 text-xs font-mono", children: [_jsx("span", { children: "module-ai/" }), _jsx("span", { children: "\u251C\u2500\u2500 src/" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 web/" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 TextareaGenerative.tsx" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 ImageGenerative.tsx" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 api/" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 generate-text.ts" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 generate-image.ts" }), _jsx("span", { children: "\u2502 \u2502 \u251C\u2500\u2500 prompts.ts" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 balance.ts" }), _jsx("span", { children: "\u2502 \u251C\u2500\u2500 components/" }), _jsx("span", { children: "\u2502 \u2502 \u2514\u2500\u2500 Doc.tsx" }), _jsx("span", { children: "\u2502 \u2514\u2500\u2500 ai.build.config.ts" }), _jsx("span", { children: "\u2514\u2500\u2500 supabase/" }), _jsx("span", { children: " \u2514\u2500\u2500 migrations/" }), _jsx("span", { children: " \u251C\u2500\u2500 20251121000000_ai_tokens.sql" }), _jsx("span", { children: " \u2514\u2500\u2500 20251121093113_module-ai_init.sql" })] }) }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDDC4\uFE0F Tables de Donn\u00E9es" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsx(TableStructure, { tableName: "user_token_ledger", title: "user_token_ledger", description: "Comptabilit\u00E9 des tokens (cr\u00E9dits, d\u00E9bits, balance). Vue user_token_balance pour le solde actuel." }), _jsx(TableStructure, { tableName: "user_prompts", title: "user_prompts", description: "Biblioth\u00E8que de prompts r\u00E9utilisables par utilisateur (titre, contenu, type)." })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCE6 Installation" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "pnpm lastbrain add-module ai" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "pnpm build:modules" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "supabase migration up" })] }), _jsxs("div", { className: "border-2 border-danger rounded-lg p-4 mt-6", children: [_jsx("h4", { className: "text-lg font-semibold text-danger mb-3", children: "\u26A0\uFE0F Danger Zone" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "La suppression du module supprimera toutes les pages, routes API et migrations associ\u00E9es. Cette action est irr\u00E9versible." }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Snippet, { symbol: "", color: "danger", size: "sm", children: "pnpm lastbrain remove-module ai" }), _jsx(Snippet, { symbol: "", color: "danger", size: "sm", children: "pnpm build:modules" })] })] })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\u2699\uFE0F Configuration" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Ajoutez votre cl\u00E9 API OpenAI dans vos variables d'environnement :" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", children: "OPENAI_API_KEY=sk-..." })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-xl font-semibold", children: "\uD83D\uDCB0 Syst\u00E8me de Tokens" }) }), _jsxs(CardBody, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2", children: "Co\u00FBts" }), _jsxs("ul", { className: "text-sm text-slate-600 dark:text-slate-400 space-y-1", children: [_jsx("li", { children: "\u2022 G\u00E9n\u00E9ration de texte : 10 tokens" }), _jsx("li", { children: "\u2022 G\u00E9n\u00E9ration d'image : 50 tokens" })] })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "Trigger de validation" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400", children: "Un trigger emp\u00EAche les transactions si le solde devient n\u00E9gatif." })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-semibold mb-2 mt-4", children: "Ajouter des tokens" }), _jsx(Snippet, { symbol: "", color: "default", size: "sm", hideSymbol: true, children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { children: "INSERT INTO user_token_ledger" }), _jsx("span", { children: "(user_id, amount, transaction_type, description)" }), _jsx("span", { children: "VALUES" }), _jsx("span", { children: "(\"('user-uuid', 1000, 'credit', 'Achat de tokens');\")" })] }) })] })] })] })] }));
5
5
  }
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { TokenPage } from "./web/auth/TokenPage.js";
4
4
  export { UserTokenPage } from "./web/admin/UserTokenPage.js";
5
5
  export { UserTokenIdPage } from "./web/admin/UserTokenIdPage.js";
6
6
  export { AiModuleDoc } from "./components/Doc.js";
7
- export type { GenerativeResponse, TextareaGenerativeProps } from "./web/components/TextareaGenerative.js";
8
- export type { GenerativeImageResponse, ImageGenerativeProps } from "./web/components/ImageGenerative.js";
7
+ export type { GenerativeResponse, TextareaGenerativeProps, } from "./web/components/TextareaGenerative.js";
8
+ export type { GenerativeImageResponse, ImageGenerativeProps, } from "./web/components/ImageGenerative.js";
9
9
  export { default as buildConfig } from "./ai.build.config.js";
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGtE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,wCAAwC,CAAC;AAEhD,YAAY,EACV,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGtE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,YAAY,EACV,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAEhD,YAAY,EACV,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/server.d.ts CHANGED
@@ -13,7 +13,7 @@ export interface TokenLedgerEntry {
13
13
  id: string;
14
14
  owner_id: string;
15
15
  ts: string;
16
- type: 'purchase' | 'gift' | 'use' | 'adjust';
16
+ type: "purchase" | "gift" | "use" | "adjust";
17
17
  amount: number;
18
18
  model?: string;
19
19
  prompt?: string;
@@ -24,11 +24,11 @@ export interface TokenLedgerEntry {
24
24
  /**
25
25
  * Ajoute des tokens au compte d'un utilisateur
26
26
  */
27
- export declare function addTokens(userId: string, amount: number, type?: 'purchase' | 'gift' | 'adjust', meta?: Record<string, any>, createdBy?: string): Promise<TokenOperationResult>;
27
+ export declare function addTokens(userId: string, amount: number, type?: "purchase" | "gift" | "adjust", meta?: Record<string, any>, createdBy?: string): Promise<TokenOperationResult>;
28
28
  /**
29
29
  * Utilise des tokens du compte d'un utilisateur
30
30
  */
31
- export declare function useTokens(userId: string, amount: number, model?: string, prompt?: string, meta?: Record<string, any>): Promise<TokenOperationResult>;
31
+ export declare function deductTokens(userId: string, amount: number, model?: string, prompt?: string, meta?: Record<string, any>): Promise<TokenOperationResult>;
32
32
  /**
33
33
  * Récupère le solde de tokens d'un utilisateur
34
34
  */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,UAAU,GAAG,MAAM,GAAG,QAAiB,EAC7C,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAC9B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAmB/B;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CA4C/B;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBrE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGxF;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU,GACjB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiB7B;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM;;;;;;GA4CjD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU;;;KAiBnB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,UAAU,GAAG,MAAM,GAAG,QAAiB,EAC7C,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAC9B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA+B/B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAoD/B;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBrE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU,GACjB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiB7B;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM;;;;;;GA4CjD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU;;;KAiBnB"}
package/dist/server.js CHANGED
@@ -3,14 +3,22 @@ import { getSupabaseServerClient } from "@lastbrain/core/server";
3
3
  /**
4
4
  * Ajoute des tokens au compte d'un utilisateur
5
5
  */
6
- export async function addTokens(userId, amount, type = 'gift', meta = {}, createdBy) {
6
+ export async function addTokens(userId, amount, type = "gift", meta = {}, createdBy) {
7
7
  if (amount <= 0) {
8
- return { success: false, balance: 0, error: "Le montant doit être positif" };
8
+ return {
9
+ success: false,
10
+ balance: 0,
11
+ error: "Le montant doit être positif",
12
+ };
9
13
  }
10
14
  const supabase = await getSupabaseServerClient();
11
15
  try {
12
- const { error } = await supabase.from('user_token_ledger').insert({
13
- owner_id: userId, type, amount, meta, created_by: createdBy
16
+ const { error } = await supabase.from("user_token_ledger").insert({
17
+ owner_id: userId,
18
+ type,
19
+ amount,
20
+ meta,
21
+ created_by: createdBy,
14
22
  });
15
23
  if (error)
16
24
  throw error;
@@ -18,16 +26,24 @@ export async function addTokens(userId, amount, type = 'gift', meta = {}, create
18
26
  return { success: true, balance };
19
27
  }
20
28
  catch (error) {
21
- console.error('[addTokens] Error:', error);
22
- return { success: false, balance: 0, error: error.message || 'Erreur lors de l\'ajout de tokens' };
29
+ console.error("[addTokens] Error:", error);
30
+ return {
31
+ success: false,
32
+ balance: 0,
33
+ error: error.message || "Erreur lors de l'ajout de tokens",
34
+ };
23
35
  }
24
36
  }
25
37
  /**
26
38
  * Utilise des tokens du compte d'un utilisateur
27
39
  */
28
- export async function useTokens(userId, amount, model, prompt, meta = {}) {
40
+ export async function deductTokens(userId, amount, model, prompt, meta = {}) {
29
41
  if (amount <= 0) {
30
- return { success: false, balance: 0, error: "Le montant doit être positif" };
42
+ return {
43
+ success: false,
44
+ balance: 0,
45
+ error: "Le montant doit être positif",
46
+ };
31
47
  }
32
48
  const supabase = await getSupabaseServerClient();
33
49
  try {
@@ -36,24 +52,24 @@ export async function useTokens(userId, amount, model, prompt, meta = {}) {
36
52
  return {
37
53
  success: false,
38
54
  balance: currentBalance,
39
- error: `Solde insuffisant. Disponible: ${currentBalance}, requis: ${amount}`
55
+ error: `Solde insuffisant. Disponible: ${currentBalance}, requis: ${amount}`,
40
56
  };
41
57
  }
42
- const { error } = await supabase.from('user_token_ledger').insert({
58
+ const { error } = await supabase.from("user_token_ledger").insert({
43
59
  owner_id: userId,
44
- type: 'use',
60
+ type: "use",
45
61
  amount: -amount,
46
62
  model,
47
63
  prompt,
48
- meta
64
+ meta,
49
65
  });
50
66
  if (error) {
51
- if (error.message?.includes('INSUFFICIENT_TOKEN_BALANCE')) {
67
+ if (error.message?.includes("INSUFFICIENT_TOKEN_BALANCE")) {
52
68
  const currentBalance = await getTokenBalance(userId);
53
69
  return {
54
70
  success: false,
55
71
  balance: currentBalance,
56
- error: `Solde insuffisant. Disponible: ${currentBalance}, requis: ${amount}`
72
+ error: `Solde insuffisant. Disponible: ${currentBalance}, requis: ${amount}`,
57
73
  };
58
74
  }
59
75
  throw error;
@@ -62,8 +78,12 @@ export async function useTokens(userId, amount, model, prompt, meta = {}) {
62
78
  return { success: true, balance };
63
79
  }
64
80
  catch (error) {
65
- console.error('[useTokens] Error:', error);
66
- return { success: false, balance: 0, error: error.message || 'Erreur lors de l\'utilisation de tokens' };
81
+ console.error("[deductTokens] Error:", error);
82
+ return {
83
+ success: false,
84
+ balance: 0,
85
+ error: error.message || "Erreur lors de l'utilisation de tokens",
86
+ };
67
87
  }
68
88
  }
69
89
  /**
@@ -73,19 +93,19 @@ export async function getTokenBalance(userId) {
73
93
  const supabase = await getSupabaseServerClient();
74
94
  try {
75
95
  const { data, error } = await supabase
76
- .from('user_token_balance_v')
77
- .select('balance')
78
- .eq('owner_id', userId)
96
+ .from("user_token_balance_v")
97
+ .select("balance")
98
+ .eq("owner_id", userId)
79
99
  .single();
80
100
  if (error) {
81
- if (error.code === 'PGRST116')
101
+ if (error.code === "PGRST116")
82
102
  return 0;
83
103
  throw error;
84
104
  }
85
105
  return data?.balance || 0;
86
106
  }
87
107
  catch (error) {
88
- console.error('[getTokenBalance] Error:', error);
108
+ console.error("[getTokenBalance] Error:", error);
89
109
  return 0;
90
110
  }
91
111
  }
@@ -103,17 +123,17 @@ export async function getTokenHistory(userId, limit = 50, offset = 0) {
103
123
  const supabase = await getSupabaseServerClient();
104
124
  try {
105
125
  const { data, error } = await supabase
106
- .from('user_token_ledger')
107
- .select('*')
108
- .eq('owner_id', userId)
109
- .order('ts', { ascending: false })
126
+ .from("user_token_ledger")
127
+ .select("*")
128
+ .eq("owner_id", userId)
129
+ .order("ts", { ascending: false })
110
130
  .range(offset, offset + limit - 1);
111
131
  if (error)
112
132
  throw error;
113
133
  return data || [];
114
134
  }
115
135
  catch (error) {
116
- console.error('[getTokenHistory] Error:', error);
136
+ console.error("[getTokenHistory] Error:", error);
117
137
  return [];
118
138
  }
119
139
  }
@@ -124,9 +144,9 @@ export async function getTokenStats(userId) {
124
144
  const supabase = await getSupabaseServerClient();
125
145
  try {
126
146
  const { data, error } = await supabase
127
- .from('user_token_ledger')
128
- .select('type, amount')
129
- .eq('owner_id', userId);
147
+ .from("user_token_ledger")
148
+ .select("type, amount")
149
+ .eq("owner_id", userId);
130
150
  if (error)
131
151
  throw error;
132
152
  const stats = {
@@ -134,33 +154,33 @@ export async function getTokenStats(userId) {
134
154
  totalPurchased: 0,
135
155
  totalGifted: 0,
136
156
  totalUsed: 0,
137
- totalAdjusted: 0
157
+ totalAdjusted: 0,
138
158
  };
139
159
  data?.forEach((entry) => {
140
160
  stats.balance += entry.amount;
141
- if (entry.type === 'purchase' && entry.amount > 0) {
161
+ if (entry.type === "purchase" && entry.amount > 0) {
142
162
  stats.totalPurchased += entry.amount;
143
163
  }
144
- else if (entry.type === 'gift' && entry.amount > 0) {
164
+ else if (entry.type === "gift" && entry.amount > 0) {
145
165
  stats.totalGifted += entry.amount;
146
166
  }
147
- else if (entry.type === 'use' && entry.amount < 0) {
167
+ else if (entry.type === "use" && entry.amount < 0) {
148
168
  stats.totalUsed += Math.abs(entry.amount);
149
169
  }
150
- else if (entry.type === 'adjust') {
170
+ else if (entry.type === "adjust") {
151
171
  stats.totalAdjusted += entry.amount;
152
172
  }
153
173
  });
154
174
  return stats;
155
175
  }
156
176
  catch (error) {
157
- console.error('[getTokenStats] Error:', error);
177
+ console.error("[getTokenStats] Error:", error);
158
178
  return {
159
179
  balance: 0,
160
180
  totalPurchased: 0,
161
181
  totalGifted: 0,
162
182
  totalUsed: 0,
163
- totalAdjusted: 0
183
+ totalAdjusted: 0,
164
184
  };
165
185
  }
166
186
  }
@@ -171,16 +191,16 @@ export async function getAllUsersTokenBalance(limit = 50, offset = 0) {
171
191
  const supabase = await getSupabaseServerClient();
172
192
  try {
173
193
  const { data, error } = await supabase
174
- .from('user_token_balance_v')
175
- .select('owner_id, balance')
176
- .order('balance', { ascending: false })
194
+ .from("user_token_balance_v")
195
+ .select("owner_id, balance")
196
+ .order("balance", { ascending: false })
177
197
  .range(offset, offset + limit - 1);
178
198
  if (error)
179
199
  throw error;
180
200
  return data || [];
181
201
  }
182
202
  catch (error) {
183
- console.error('[getAllUsersTokenBalance] Error:', error);
203
+ console.error("[getAllUsersTokenBalance] Error:", error);
184
204
  return [];
185
205
  }
186
206
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ImageGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/ImageGenerative.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IACvE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,KAAkB,EAClB,IAAkB,EAClB,OAAoB,EACpB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,WAAwC,EACxC,gBAAuB,GACxB,EAAE,oBAAoB,2CA6LtB"}
1
+ {"version":3,"file":"ImageGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/ImageGenerative.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IACvE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,KAAkB,EAClB,IAAkB,EAClB,OAAoB,EACpB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,WAAsC,EACtC,gBAAuB,GACxB,EAAE,oBAAoB,2CA6LtB"}
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { Button, Chip, Progress, Card, CardBody } from "@lastbrain/ui";
5
5
  import { Sparkles, Loader2, AlertCircle, Download } from "lucide-react";
6
6
  import Image from "next/image";
7
- export function ImageGenerative({ prompt, model = "dall-e-3", size = "1024x1024", quality = "standard", onChange, onError, className, disabled = false, apiEndpoint = "/api/auth/generate-image", showTokenBalance = true, }) {
7
+ export function ImageGenerative({ prompt, model = "dall-e-3", size = "1024x1024", quality = "standard", onChange, onError, className, disabled = false, apiEndpoint = "/api/ai/generate-image", showTokenBalance = true, }) {
8
8
  const [isGenerating, setIsGenerating] = useState(false);
9
9
  const [generatedImage, setGeneratedImage] = useState(null);
10
10
  const [error, setError] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"file":"TextareaGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/TextareaGenerative.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAqB,EACrB,WAAmC,EACnC,YAAiB,EACjB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,OAAW,EACX,OAAY,EACZ,WAAuC,EACvC,gBAAuB,GACxB,EAAE,uBAAuB,2CAwIzB"}
1
+ {"version":3,"file":"TextareaGenerative.d.ts","sourceRoot":"","sources":["../../../src/web/components/TextareaGenerative.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAqB,EACrB,WAAmC,EACnC,YAAiB,EACjB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAgB,EAChB,OAAW,EACX,OAAY,EACZ,WAAqC,EACrC,gBAAuB,GACxB,EAAE,uBAAuB,2CAwIzB"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
4
  import { Textarea, Button, Chip, Progress } from "@lastbrain/ui";
5
5
  import { Sparkles, Loader2, AlertCircle } from "lucide-react";
6
- export function TextareaGenerative({ prompt, model = "gpt-4o-mini", placeholder = "Générer du texte...", defaultValue = "", onChange, onError, className, disabled = false, minRows = 4, maxRows = 20, apiEndpoint = "/api/auth/generate-text", showTokenBalance = true, }) {
6
+ export function TextareaGenerative({ prompt, model = "gpt-4o-mini", placeholder = "Générer du texte...", defaultValue = "", onChange, onError, className, disabled = false, minRows = 4, maxRows = 20, apiEndpoint = "/api/ai/generate-text", showTokenBalance = true, }) {
7
7
  const [value, setValue] = useState(defaultValue);
8
8
  const [isGenerating, setIsGenerating] = useState(false);
9
9
  const [lastResponse, setLastResponse] = useState(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/module-ai",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Module de génération IA (texte et images) avec gestion de tokens pour LastBrain",
5
5
  "private": false,
6
6
  "type": "module",
@@ -32,13 +32,14 @@
32
32
  "@heroui/react": "^2.4.23",
33
33
  "@heroui/system": "^2.4.23",
34
34
  "@heroui/theme": "^2.4.23",
35
- "@lastbrain/core": "0.1.0",
36
- "@lastbrain/ui": "0.1.0",
37
35
  "lucide-react": "^0.554.0",
38
36
  "next": "^15.5.6",
39
37
  "openai": "^4.76.0",
40
38
  "react": "^19.0.0",
41
- "react-dom": "^19.0.0"
39
+ "react-dom": "^19.0.0",
40
+ "zod": "^3.23.8",
41
+ "@lastbrain/core": "0.1.2",
42
+ "@lastbrain/ui": "0.1.6"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "next": ">=15.0.0"
@@ -67,6 +68,7 @@
67
68
  "sideEffects": false,
68
69
  "scripts": {
69
70
  "build": "tsc -p tsconfig.json",
70
- "dev": "tsc -p tsconfig.json --watch"
71
+ "dev": "tsc -p tsconfig.json --watch",
72
+ "lint": "eslint ."
71
73
  }
72
74
  }