@meistrari/chat-nuxt 4.11.0-rc.9 → 4.12.0-rc.0

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 CHANGED
@@ -165,13 +165,11 @@ Installing `@meistrari/chat-nuxt` installs the runtime packages used by the laye
165
165
  | `mermaid` | `^11.13.0` | Native markstream Mermaid diagram rendering |
166
166
  | `h3` | `^1.15.0` | Server route utilities provided by Nuxt/Nitro |
167
167
  | `markstream-vue` | `2.0.7` | Markdown streaming renderer |
168
- | `motion` | `^11.13.0` | Animation component runtime |
169
168
  | `nitropack` | `^2.10.0` | Runtime config and task utilities provided by Nuxt/Nitro |
170
169
  | `pdfjs-dist` | `^5.4.530` | PDF file previews |
171
170
  | `posthog-js` | `^1.364.2` | Optional PostHog feature-flag helper |
172
171
  | `stream-diffs` | `0.0.2` | Native markstream rich code and diff rendering |
173
172
  | `virtua` | `^0.42.0` | Virtualized list runtime expected by shared UI |
174
- | `xlsx` | `^0.18.5` | Spreadsheet file previews |
175
173
  | `zod` | `^4.1.13` | Schema validation for API payloads |
176
174
 
177
175
  The chat package does not declare or expect the host application to configure an i18n plugin. Hosts can override the supported conversation-search text through `labels`, using plain strings or their own translator. This is not a full-chat localization API; other UI text retains its existing defaults.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "4.11.0-rc.9",
4
+ "version": "4.12.0-rc.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -5,7 +5,7 @@ const props = defineProps({
5
5
  variant: { type: String, required: false }
6
6
  });
7
7
  const DEFAULT_GROUP_NAME = "Nova pasta";
8
- const { groups, createGroup, pendingRenameGroupId } = useConversationGroups();
8
+ const { visibleGroups, createGroup, pendingRenameGroupId } = useConversationGroups();
9
9
  const isCreating = ref(false);
10
10
  const hasLabel = computed(() => Boolean(props.label?.trim()));
11
11
  const isEmptyState = computed(() => props.variant === "empty-state");
@@ -17,7 +17,7 @@ const buttonClasses = computed(() => {
17
17
  return "h-32px! w-40px! p-0!";
18
18
  });
19
19
  function nextGroupName() {
20
- const names = new Set(groups.value.map((group) => group.name));
20
+ const names = new Set(visibleGroups.value.map((group) => group.name));
21
21
  if (!names.has(DEFAULT_GROUP_NAME))
22
22
  return DEFAULT_GROUP_NAME;
23
23
  let index = 2;
@@ -1,6 +1,7 @@
1
1
  import type { ConversationGroup } from '../../types/schemas/chat/conversations.js';
2
2
  export declare function useConversationGroups(): {
3
3
  groups: any;
4
+ visibleGroups: any;
4
5
  loading: any;
5
6
  error: any;
6
7
  pendingRenameGroupId: any;
@@ -1,5 +1,6 @@
1
1
  import { useChatAuth } from "#chat-auth";
2
2
  import * as Sentry from "@sentry/nuxt";
3
+ import { filterGroupsByCreator } from "../groups.js";
3
4
  import { resolveChatStateScope } from "../scope.js";
4
5
  import { useChatApi } from "../../shared/composables/chat-api.js";
5
6
  import { useConversations } from "./conversations.js";
@@ -15,7 +16,8 @@ function sortConversationGroups(groups) {
15
16
  export function useConversationGroups() {
16
17
  const chatApi = useChatApi();
17
18
  const embedConfig = useEmbedConfig();
18
- const { activeOrganization } = useChatAuth();
19
+ const { user: authUser, activeOrganization } = useChatAuth();
20
+ const { conversationCreatorFilter, clearGroupFromConversations } = useConversations();
19
21
  const groupBuckets = useState("conversation-groups", () => ({}));
20
22
  const loadingBuckets = useState("conversation-groups-loading", () => ({}));
21
23
  const fetchedBuckets = useState("conversation-groups-fetched", () => ({}));
@@ -35,6 +37,9 @@ export function useConversationGroups() {
35
37
  };
36
38
  }
37
39
  });
40
+ const visibleGroups = computed(
41
+ () => filterGroupsByCreator(groups.value, conversationCreatorFilter.value, authUser.value?.email)
42
+ );
38
43
  const loading = computed({
39
44
  get: () => loadingBuckets.value[runtimeScope.value] ?? false,
40
45
  set: (value) => {
@@ -133,7 +138,6 @@ export function useConversationGroups() {
133
138
  const deleteGroup = async (groupId) => {
134
139
  error.value = null;
135
140
  const previousGroups = groups.value;
136
- const { clearGroupFromConversations } = useConversations();
137
141
  removeGroup(groupId);
138
142
  const restoreConversations = clearGroupFromConversations(groupId);
139
143
  try {
@@ -154,6 +158,7 @@ export function useConversationGroups() {
154
158
  };
155
159
  return {
156
160
  groups: readonly(groups),
161
+ visibleGroups: readonly(visibleGroups),
157
162
  loading: readonly(loading),
158
163
  error: readonly(error),
159
164
  pendingRenameGroupId,
@@ -14,7 +14,7 @@ export function useConversationList(options) {
14
14
  const { activeOrganization } = useChatAuth();
15
15
  const { deleteConversation, renameConversation, duplicateConversation, moveConversationToGroup } = useConversations();
16
16
  const { copyConversationLink } = useConversationActions();
17
- const { groups, fetchGroups, createGroup, deleteGroup, renameGroup, error: groupError, pendingRenameGroupId } = useConversationGroups();
17
+ const { groups, visibleGroups, fetchGroups, createGroup, deleteGroup, renameGroup, error: groupError, pendingRenameGroupId } = useConversationGroups();
18
18
  const { setConversation, currentConversation } = useChat();
19
19
  const exportConversationId = ref(null);
20
20
  const debugConversationId = ref(null);
@@ -60,8 +60,8 @@ export function useConversationList(options) {
60
60
  shouldInclude: (conversation) => !conversation.title && conversation.id === options.currentId
61
61
  })
62
62
  );
63
- const filteredGroupedConversations = computed(() => groupConversationsByGroup(filteredConversations.value, groups.value));
64
- const allGroupedConversations = computed(() => groupConversationsByGroup(options.conversations, groups.value));
63
+ const filteredGroupedConversations = computed(() => groupConversationsByGroup(filteredConversations.value, visibleGroups.value));
64
+ const allGroupedConversations = computed(() => groupConversationsByGroup(options.conversations, visibleGroups.value));
65
65
  const groupedConversations = computed(() => {
66
66
  if (!hasConversationSearchQuery.value)
67
67
  return filteredGroupedConversations.value;
@@ -261,12 +261,12 @@ export function useConversationList(options) {
261
261
  const ungroupedConversationLabel = computed(() => `Conversas`);
262
262
  const shouldShowUngroupedHeader = computed(() => groupItems.value.length > 0);
263
263
  const showGroupCreationPrompt = computed(
264
- () => groupsReady.value && !hasConversationSearchQuery.value && groups.value.length === 0
264
+ () => groupsReady.value && !hasConversationSearchQuery.value && visibleGroups.value.length === 0
265
265
  );
266
266
  const hasConversationContent = computed(() => {
267
267
  if (hasConversationSearchQuery.value)
268
268
  return groupItems.value.length > 0 || ungroupedConversations.value.length > 0;
269
- return showGroupCreationPrompt.value || groups.value.length > 0 || filteredConversations.value.length > 0;
269
+ return showGroupCreationPrompt.value || visibleGroups.value.length > 0 || filteredConversations.value.length > 0;
270
270
  });
271
271
  const deleteGroupConfirmationMessage = computed(() => {
272
272
  const target = deleteGroupTarget.value;
@@ -506,7 +506,7 @@ export function useConversationList(options) {
506
506
  const DEFAULT_GROUP_NAME = "Nova pasta";
507
507
  const isCreatingGroup = ref(false);
508
508
  function nextGroupName() {
509
- const names = new Set(groups.value.map((group) => group.name));
509
+ const names = new Set(visibleGroups.value.map((group) => group.name));
510
510
  if (!names.has(DEFAULT_GROUP_NAME))
511
511
  return DEFAULT_GROUP_NAME;
512
512
  let index = 2;
@@ -3,6 +3,16 @@ export type ConversationGroupLike = {
3
3
  name: string;
4
4
  createdAt?: Date | string;
5
5
  };
6
+ export type ConversationGroupCreatorFilter = 'all' | 'mine';
7
+ /**
8
+ * Applies the sidebar creator filter to folders: with `'mine'` only folders the
9
+ * current user created stay visible (`createdBy` is the creator's email, the same
10
+ * value conversations carry); `'all'` keeps every folder. A folder without a
11
+ * creator, or a user without an email, never matches `'mine'`.
12
+ */
13
+ export declare function filterGroupsByCreator<TGroup extends ConversationGroupLike & {
14
+ createdBy?: string | null;
15
+ }>(groups: readonly TGroup[], filter: ConversationGroupCreatorFilter, currentUserEmail: string | null | undefined): TGroup[];
6
16
  export type ConversationGroupItem = {
7
17
  groupId?: string | null;
8
18
  updatedAt: Date | string;
@@ -1,3 +1,15 @@
1
+ function normalizedEmail(value) {
2
+ const email = value?.trim().toLowerCase();
3
+ return email || null;
4
+ }
5
+ export function filterGroupsByCreator(groups, filter, currentUserEmail) {
6
+ if (filter !== "mine")
7
+ return [...groups];
8
+ const email = normalizedEmail(currentUserEmail);
9
+ if (!email)
10
+ return [];
11
+ return groups.filter((group) => normalizedEmail(group.createdBy) === email);
12
+ }
1
13
  function byUpdatedAtDesc(a, b) {
2
14
  return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
3
15
  }
@@ -31,7 +31,7 @@ let dragOpenGroupTimeout = null;
31
31
  let dragOpenGroupId = null;
32
32
  const embedConfig = useEmbedConfig();
33
33
  const { activeOrganization } = useChatAuth();
34
- const { groups, fetchGroups } = useConversationGroups();
34
+ const { visibleGroups, fetchGroups } = useConversationGroups();
35
35
  const { moveConversationToGroup } = useConversations();
36
36
  const runtimeScope = computed(() => resolveChatStateScope(
37
37
  embedConfig?.workspaceId.value || activeOrganization.value?.id,
@@ -46,8 +46,8 @@ const filteredConversations = computed(
46
46
  })
47
47
  );
48
48
  const hasConversationSearchQuery = computed(() => conversationSearch.value.trim().length > 0);
49
- const filteredGroupedConversations = computed(() => groupConversationsByGroup(filteredConversations.value, groups.value));
50
- const allGroupedConversations = computed(() => groupConversationsByGroup(props.conversations, groups.value));
49
+ const filteredGroupedConversations = computed(() => groupConversationsByGroup(filteredConversations.value, visibleGroups.value));
50
+ const allGroupedConversations = computed(() => groupConversationsByGroup(props.conversations, visibleGroups.value));
51
51
  const groupedConversations = computed(() => {
52
52
  if (!hasConversationSearchQuery.value)
53
53
  return filteredGroupedConversations.value;
@@ -82,7 +82,7 @@ const visibleGroupBuckets = computed(() => groupedConversations.value.groups.fil
82
82
  const hasConversationContent = computed(() => {
83
83
  if (hasConversationSearchQuery.value)
84
84
  return visibleGroupBuckets.value.length > 0 || groupedConversations.value.unfiled.length > 0;
85
- return groups.value.length > 0 || filteredConversations.value.length > 0;
85
+ return visibleGroups.value.length > 0 || filteredConversations.value.length > 0;
86
86
  });
87
87
  const shouldShowUngroupedHeader = computed(() => visibleGroupBuckets.value.length > 0);
88
88
  const ungroupedConversationLabel = computed(() => `Conversas \xB7 ${groupedConversations.value.unfiled.length}`);
@@ -72,8 +72,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
72
72
  copyLink: () => any;
73
73
  debug: () => any;
74
74
  selectConversation: (id: string) => any;
75
- settings: () => any;
76
75
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
76
+ settings: () => any;
77
77
  selectExecutionProfile: (profile: ChatExecutionProfile) => any;
78
78
  tabChange: (tab: TopbarTab) => any;
79
79
  newConversation: (groupId?: string | null | undefined) => any;
@@ -94,8 +94,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
94
94
  onCopyLink?: (() => any) | undefined;
95
95
  onDebug?: (() => any) | undefined;
96
96
  onSelectConversation?: ((id: string) => any) | undefined;
97
- onSettings?: (() => any) | undefined;
98
97
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
98
+ onSettings?: (() => any) | undefined;
99
99
  onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
100
100
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
101
101
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
@@ -72,8 +72,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
72
72
  copyLink: () => any;
73
73
  debug: () => any;
74
74
  selectConversation: (id: string) => any;
75
- settings: () => any;
76
75
  send: (content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any;
76
+ settings: () => any;
77
77
  selectExecutionProfile: (profile: ChatExecutionProfile) => any;
78
78
  tabChange: (tab: TopbarTab) => any;
79
79
  newConversation: (groupId?: string | null | undefined) => any;
@@ -94,8 +94,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
94
94
  onCopyLink?: (() => any) | undefined;
95
95
  onDebug?: (() => any) | undefined;
96
96
  onSelectConversation?: ((id: string) => any) | undefined;
97
- onSettings?: (() => any) | undefined;
98
97
  onSend?: ((content: string, files: FileUploadInput[], agentInputs?: TelaAgentExecutionInput[] | undefined) => any) | undefined;
98
+ onSettings?: (() => any) | undefined;
99
99
  onSelectExecutionProfile?: ((profile: ChatExecutionProfile) => any) | undefined;
100
100
  "onUpdate:selectedModel"?: ((value: string | null) => any) | undefined;
101
101
  "onUpdate:selectedReasoningEffort"?: ((value: any) => any) | undefined;
@@ -38,7 +38,6 @@ export declare const v5ErrorStatus: {
38
38
  readonly invalid_event_limit: 400;
39
39
  readonly invalid_artifact_limit: 400;
40
40
  readonly invalid_user_event_batch: 400;
41
- readonly provider_harness_mismatch: 400;
42
41
  readonly insufficient_role: 403;
43
42
  readonly agent_publication_forbidden: 403;
44
43
  readonly session_not_found: 404;
@@ -32,7 +32,6 @@ export const v5ErrorStatus = {
32
32
  invalid_event_limit: 400,
33
33
  invalid_artifact_limit: 400,
34
34
  invalid_user_event_batch: 400,
35
- provider_harness_mismatch: 400,
36
35
  insufficient_role: 403,
37
36
  agent_publication_forbidden: 403,
38
37
  session_not_found: 404,
@@ -51,6 +51,10 @@ export declare const botTargetSchema: z.ZodObject<{
51
51
  workspaceId: z.ZodUUID;
52
52
  agentId: z.ZodUUID;
53
53
  }, z.core.$strip>;
54
+ export declare const botPhotoSchema: z.ZodObject<{
55
+ url: z.ZodURL;
56
+ sha256: z.ZodString;
57
+ }, z.core.$strict>;
54
58
  export declare const botRequestSchema: z.ZodObject<{
55
59
  actorId: z.ZodString;
56
60
  workspaceId: z.ZodUUID;
@@ -60,6 +64,10 @@ export declare const botRequestSchema: z.ZodObject<{
60
64
  agentName: z.ZodString;
61
65
  name: z.ZodString;
62
66
  requestKey: z.ZodUUID;
67
+ photo: z.ZodOptional<z.ZodObject<{
68
+ url: z.ZodURL;
69
+ sha256: z.ZodString;
70
+ }, z.core.$strict>>;
63
71
  }, z.core.$strip>;
64
72
  export declare const botChangesSchema: z.ZodObject<{
65
73
  actorId: z.ZodString;
@@ -68,6 +76,10 @@ export declare const botChangesSchema: z.ZodObject<{
68
76
  name: z.ZodOptional<z.ZodString>;
69
77
  addAdminActorIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
70
78
  removeAdminSlackUserIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
79
+ photo: z.ZodOptional<z.ZodObject<{
80
+ url: z.ZodURL;
81
+ sha256: z.ZodString;
82
+ }, z.core.$strict>>;
71
83
  }, z.core.$strip>;
72
84
  export declare const botIdSchema: z.ZodUUID;
73
85
  export type BotTarget = z.infer<typeof botTargetSchema>;
@@ -104,6 +116,13 @@ export declare const botSchema: z.ZodObject<{
104
116
  }>;
105
117
  error: z.ZodNullable<z.ZodString>;
106
118
  updating: z.ZodBoolean;
119
+ photoUrl: z.ZodNullable<z.ZodString>;
120
+ pendingPhotoUrl: z.ZodNullable<z.ZodString>;
121
+ photoError: z.ZodNullable<z.ZodEnum<{
122
+ photo_rejected: "photo_rejected";
123
+ photo_unavailable: "photo_unavailable";
124
+ photo_unknown: "photo_unknown";
125
+ }>>;
107
126
  openUrl: z.ZodNullable<z.ZodURL>;
108
127
  creatorActorId: z.ZodString;
109
128
  admins: z.ZodArray<z.ZodObject<{
@@ -21,9 +21,16 @@ export const appNameSchema = z.string().max(200).transform(value => value.normal
21
21
  export const botStatuses = ['creating', 'configuring', 'awaiting_distribution', 'awaiting_installation', 'active', 'uninstalled', 'failed', 'unknown', 'deactivated', 'deleting', 'delete_failed', 'deleted'];
22
22
  export const botStatusSchema = z.enum(botStatuses);
23
23
  export const botTargetSchema = z.object({ actorId: actorIdSchema, workspaceId: z.uuid(), agentId: z.uuid() });
24
+ // A photo Tela validated and published, which Slack fetches as the app icon. Its SHA-256 identifies the image: Tela publishes
25
+ // every upload at a new URL, so a retry of the same request may carry another URL for the same bytes.
26
+ // Checked without the URL global, which the embeddable chat's declaration build does not type.
27
+ export const botPhotoSchema = z.strictObject({
28
+ url: z.url({ protocol: /^https$/ }).max(2048).refine(value => !/^https:\/\/[^/?#]*@/i.test(value)),
29
+ sha256: z.string().regex(/^[0-9a-f]{64}$/),
30
+ });
24
31
  // Workspace and agent names are Tela's current titles, shown in the Slack app; the name is validated by the bot rules.
25
- export const botRequestSchema = botTargetSchema.extend({ teamId: z.string().regex(/^T[A-Z0-9]+$/).max(100), workspaceName: z.string().min(1).max(200), agentName: z.string().min(1).max(200), name: z.string().max(200), requestKey: z.uuid() });
26
- export const botChangesSchema = botTargetSchema.extend({ name: z.string().max(200).optional(), addAdminActorIds: z.array(actorIdSchema).max(20).optional(), removeAdminSlackUserIds: z.array(z.string().regex(/^[UW][A-Z0-9]{1,99}$/)).max(20).optional() });
32
+ export const botRequestSchema = botTargetSchema.extend({ teamId: z.string().regex(/^T[A-Z0-9]+$/).max(100), workspaceName: z.string().min(1).max(200), agentName: z.string().min(1).max(200), name: z.string().max(200), requestKey: z.uuid(), photo: botPhotoSchema.optional() });
33
+ export const botChangesSchema = botTargetSchema.extend({ name: z.string().max(200).optional(), addAdminActorIds: z.array(actorIdSchema).max(20).optional(), removeAdminSlackUserIds: z.array(z.string().regex(/^[UW][A-Z0-9]{1,99}$/)).max(20).optional(), photo: botPhotoSchema.optional() });
27
34
  export const botIdSchema = z.uuid();
28
35
  export const botAdministratorSchema = z.object({
29
36
  slackUserId: z.string(),
@@ -35,8 +42,11 @@ export const botSchema = z.object({
35
42
  teamId: z.string().describe('Slack workspace the bot is installed in.'),
36
43
  teamName: z.string().nullable().describe('Workspace name learned from Sign in with Slack.'),
37
44
  status: botStatusSchema,
38
- error: z.string().nullable().describe('Why provisioning failed or is unknown, or why a deletion waits or failed.'),
39
- updating: z.boolean().describe('A rename is being applied to the Slack app.'),
45
+ error: z.string().nullable().describe('Why provisioning failed or is unknown, why a configuring bot\'s chosen photo is still being retried (`photo_unavailable`, `photo_unknown`), or why a deletion waits or failed.'),
46
+ updating: z.boolean().describe('A rename or a changed photo is being applied to the Slack app.'),
47
+ photoUrl: z.string().nullable().describe('The app icon Slack accepted last, as Tela published it; null while the bot keeps Slack\'s default icon.'),
48
+ pendingPhotoUrl: z.string().nullable().describe('A photo an administrator chose, not the app icon yet.'),
49
+ photoError: z.enum(['photo_rejected', 'photo_unavailable', 'photo_unknown']).nullable().describe('Why a changed photo is not the app icon: Slack refused the image (`photo_rejected`) or the change was given up on (`photo_unavailable`), or why it is still being retried (`photo_unavailable`, `photo_unknown`).'),
40
50
  openUrl: z.url().nullable().describe('Slack deep link, only while the bot is installed.'),
41
51
  creatorActorId: z.string(),
42
52
  // An administrator's Tela account while their Slack identity in the bot's team is connected, otherwise null.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "4.11.0-rc.9",
3
+ "version": "4.12.0-rc.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -52,7 +52,7 @@
52
52
  "@iconify-json/ph": "^1.2.2",
53
53
  "@iconify/vue": "^5.0.0",
54
54
  "@meistrari/auth-nuxt": "3.31.0",
55
- "@meistrari/chat-ui-lang": "0.5.0-rc.1",
55
+ "@meistrari/chat-ui-lang": "0.5.0",
56
56
  "@meistrari/logger": "^2.1.3",
57
57
  "@meistrari/tela-build": "^1.79.0",
58
58
  "@openuidev/lang-core": "1.0.0",
@@ -64,14 +64,12 @@
64
64
  "ipaddr.js": "2.5.0",
65
65
  "markstream-vue": "2.0.7",
66
66
  "mermaid": "^11.13.0",
67
- "motion": "^11.13.0",
68
67
  "nitropack": "^2.10.0",
69
68
  "pdfjs-dist": "^5.4.530",
70
69
  "posthog-js": "^1.364.2",
71
70
  "stream-diffs": "0.0.2",
72
71
  "tldts": "^7.4.10",
73
72
  "virtua": "^0.42.0",
74
- "xlsx": "^0.18.5",
75
73
  "zod": "^4.1.13"
76
74
  },
77
75
  "devDependencies": {
@@ -79,7 +77,6 @@
79
77
  "@meistrari/chat-schemas": "workspace:*",
80
78
  "@nuxt/module-builder": "^1.0.2",
81
79
  "@types/node": "^20.19.0",
82
- "dotenv-cli": "^11.0.0",
83
80
  "eslint": "9.16.0",
84
81
  "nuxt": "4.4.8",
85
82
  "pino": "^10.3.1",