@meistrari/chat-nuxt 1.5.0 → 1.6.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.
Files changed (38) hide show
  1. package/README.md +57 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +120 -2
  4. package/dist/runtime/components/chat/message-bubble.vue +2 -1
  5. package/dist/runtime/components/chat/message-input.d.vue.ts +1 -1
  6. package/dist/runtime/components/chat/message-input.vue +1 -1
  7. package/dist/runtime/components/chat/message-input.vue.d.ts +1 -1
  8. package/dist/runtime/composables/useChat.js +2 -1
  9. package/dist/runtime/composables/useChatApi.js +2 -1
  10. package/dist/runtime/composables/useConversations.js +2 -1
  11. package/dist/runtime/composables/useGitHubSkills.js +2 -1
  12. package/dist/runtime/composables/useWorkspaceExternalSkills.js +2 -1
  13. package/dist/runtime/composables/useWorkspaceMembers.js +2 -1
  14. package/dist/runtime/composables/useWorkspaceSettings.js +26 -6
  15. package/dist/runtime/composables/useWorkspaces.js +2 -1
  16. package/dist/runtime/embed/components/ChatEmbed.vue +2 -1
  17. package/dist/runtime/embed/components/ChatEmbedInner.vue +16 -1
  18. package/dist/runtime/internal/agent-environment-resolver.stub.d.ts +2 -0
  19. package/dist/runtime/internal/agent-environment-resolver.stub.js +1 -0
  20. package/dist/runtime/server/api/chat/workspace/settings.patch.d.ts +2 -0
  21. package/dist/runtime/server/api/chat/workspace/settings.patch.js +8 -0
  22. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +6 -2
  23. package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +6 -2
  24. package/dist/runtime/server/api/workspace/settings.patch.js +3 -82
  25. package/dist/runtime/server/utils/agent-api.js +4 -3
  26. package/dist/runtime/server/utils/agent-environment.d.ts +8 -0
  27. package/dist/runtime/server/utils/agent-environment.js +34 -0
  28. package/dist/runtime/server/utils/auth-token.d.ts +2 -0
  29. package/dist/runtime/server/utils/auth-token.js +21 -0
  30. package/dist/runtime/server/utils/conversation-agent-turn.d.ts +6 -1
  31. package/dist/runtime/server/utils/conversation-agent-turn.js +27 -8
  32. package/dist/runtime/server/utils/tela-api.js +2 -1
  33. package/dist/runtime/server/utils/workspace-settings-update.d.ts +16 -0
  34. package/dist/runtime/server/utils/workspace-settings-update.js +84 -0
  35. package/dist/runtime/server/utils/workspace.js +3 -1
  36. package/dist/runtime/types/chat-agent-environment-resolver.d.ts +4 -0
  37. package/dist/runtime/types/chat-auth.d.ts +35 -0
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -103,6 +103,15 @@ These never appear in module options — set them via `.env`, Infisical, etc. Wh
103
103
 
104
104
  Set `chatNuxt.validateConfig` to `'warn'` to log a warning when keys for an enabled feature are missing, or `'error'` to fail fast at module setup. The check is off by default since some deployments inject runtime config after build.
105
105
 
106
+ ### Auth modes
107
+
108
+ `@meistrari/chat-nuxt` can run with either auth mode exposed by `@meistrari/auth-nuxt`:
109
+
110
+ - Application auth (`telaAuth.application.enabled: true`) delegates chat identity and workspace state to `useTelaApplicationAuth()`.
111
+ - First-party auth delegates chat identity to `useTelaSession()` and workspace state to `useTelaOrganization()`.
112
+
113
+ The module chooses the bridge during Nuxt setup. Host apps should keep registering `@meistrari/auth-nuxt` before `@meistrari/chat-nuxt`.
114
+
106
115
  ### Local mode (no database)
107
116
 
108
117
  When `databaseUrl` is empty, chat-nuxt runs an embedded [pglite](https://pglite.dev) instance — Postgres compiled to WASM, in-process, file-backed at `<rootDir>/.data/chat-nuxt.db`. Migrations are applied automatically on first boot. Tables live under a dedicated `chat` Postgres schema so the database can be safely shared with the host app's own Drizzle setup.
@@ -256,8 +265,17 @@ runtimeConfig: {
256
265
  ```vue
257
266
  <script setup lang="ts">
258
267
  const { posthogPublicKey, posthogHost, posthogDefaults } = useRuntimeConfig().public
268
+
269
+ // Application auth hosts:
259
270
  const { user, activeOrganization } = useTelaApplicationAuth()
260
271
 
272
+ // First-party auth hosts can use this instead:
273
+ // const session = useTelaSession()
274
+ // const organization = useTelaOrganization()
275
+ // const user = session.user
276
+ // const activeOrganization = organization.activeOrganization
277
+ // await organization.getActiveOrganization()
278
+
261
279
  onMounted(async () => {
262
280
  if (!posthogPublicKey || !posthogHost) return
263
281
  await initPosthog(posthogPublicKey, posthogHost, posthogDefaults)
@@ -335,6 +353,45 @@ export default async function resolveWorkspaceSettings(
335
353
  }
336
354
  ```
337
355
 
356
+ ## Optional: Agent API Environment Resolver
357
+
358
+ When default chat mode starts a new Agent API session, `chat-nuxt` can attach `environmentVariables` to the Agent API create payload. Built-in workspace credentials still come from the `credentials` feature. Host apps can add server-only variables by creating `server/chat/resolve-agent-environment.ts`:
359
+
360
+ ```typescript
361
+ import type { H3Event } from 'h3'
362
+
363
+ export default async function resolveAgentEnvironment(
364
+ event: H3Event,
365
+ context: {
366
+ user: { id: string; email: string }
367
+ workspaceId: string
368
+ telaAgentId: string | null
369
+ conversationId: string
370
+ skills: readonly string[]
371
+ },
372
+ ) {
373
+ if (!context.skills.includes('github:meistrari/backoffice/skills/backoffice-admin')) {
374
+ return null
375
+ }
376
+
377
+ return {
378
+ BACKOFFICE_ADMIN_TOOL_URL: 'https://backoffice.example.com/api/admin-chat/tool',
379
+ BACKOFFICE_ADMIN_INVOCATION_GRANT: await mintInvocationGrant({
380
+ userId: context.user.id,
381
+ workspaceId: context.workspaceId,
382
+ conversationId: context.conversationId,
383
+ skills: context.skills,
384
+ }),
385
+ }
386
+ }
387
+ ```
388
+
389
+ The resolver runs on the server only. It is called for new default Agent API sessions, before `POST /v2/chat`. Continued sessions do not receive new environment variables.
390
+
391
+ If the resolver returns the same environment variable name as a workspace credential, the resolver value wins.
392
+
393
+ `chat-nuxt` only transports these variables to Agent API. A skill that calls a host backend must implement its own callback contract with the host backend. In short, callback authorization belongs to the skill and host backend, not `chat-nuxt`. The host backend remains responsible for validating invocation grants, allowed tools, schemas, replay protection, confirmation for writes, and audit logging.
394
+
338
395
  ## `<ChatConfigurationModal>`
339
396
 
340
397
  A reusable settings modal (system prompt, context files, Tela workstations, Tela skills, external skills, credentials) that any host app can open from any button. By default, the modal uses `useWorkspaceSettings()` to fetch and persist settings. If the host passes the `settings` prop, the host owns workspace settings state and persistence.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "1.5.0",
4
+ "version": "1.6.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,7 +1,33 @@
1
1
  import { existsSync, readFileSync, mkdirSync, symlinkSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { join, dirname } from 'node:path';
4
- import { defineNuxtModule, createResolver, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
4
+ import { defineNuxtModule, createResolver, addTemplate, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
5
+
6
+ const agentEnvironmentResolverCandidates = [
7
+ "server/chat/resolve-agent-environment.ts",
8
+ "server/chat/resolve-agent-environment.mts",
9
+ "server/chat/resolve-agent-environment.mjs",
10
+ "server/chat/resolve-agent-environment.js"
11
+ ];
12
+ const stubExtensions$1 = [".ts", ".mjs", ".js"];
13
+ function resolveAgentEnvironmentResolverEntry(srcDir, fallbackResolverBase) {
14
+ for (const relativePath of agentEnvironmentResolverCandidates) {
15
+ const candidate = join(srcDir, relativePath);
16
+ if (existsSync(candidate)) {
17
+ return candidate;
18
+ }
19
+ }
20
+ for (const ext of stubExtensions$1) {
21
+ const candidate = fallbackResolverBase + ext;
22
+ if (existsSync(candidate)) {
23
+ return candidate;
24
+ }
25
+ }
26
+ const attempted = stubExtensions$1.map((ext) => fallbackResolverBase + ext).join(", ");
27
+ throw new Error(
28
+ `[@meistrari/chat-nuxt] Unable to resolve agent-environment stub. Tried: ${attempted}. This indicates a packaging bug - please report it.`
29
+ );
30
+ }
5
31
 
6
32
  const localRequire = createRequire(import.meta.url);
7
33
  const runtimeAliasPackages = [
@@ -153,6 +179,74 @@ const DEFAULT_FEATURES = {
153
179
  generateTitle: false,
154
180
  credentials: false
155
181
  };
182
+ function resolveChatAuthMode(runtimeConfig) {
183
+ const publicConfig = runtimeConfig.public;
184
+ const telaAuth = publicConfig?.telaAuth;
185
+ return telaAuth?.application?.enabled ? "application" : "first-party";
186
+ }
187
+ function buildChatAuthTemplate(mode) {
188
+ if (mode === "application") {
189
+ return `
190
+ import { useTelaApplicationAuth } from '#imports'
191
+
192
+ export function useChatAuth() {
193
+ const auth = useTelaApplicationAuth()
194
+
195
+ return {
196
+ user: auth.user,
197
+ activeOrganization: auth.activeOrganization,
198
+ getToken: auth.getToken,
199
+ getAvailableOrganizations: auth.getAvailableOrganizations,
200
+ switchOrganization: auth.switchOrganization,
201
+ }
202
+ }
203
+ `.trimStart();
204
+ }
205
+ return `
206
+ import { useState, useTelaOrganization, useTelaSession, watch } from '#imports'
207
+
208
+ export function useChatAuth() {
209
+ const session = useTelaSession()
210
+ const organization = useTelaOrganization()
211
+ const isHydratingActiveOrganization = useState('chat-nuxt:auth:is-hydrating-active-organization', () => false)
212
+ const hasHydratedActiveOrganization = useState('chat-nuxt:auth:has-hydrated-active-organization', () => false)
213
+
214
+ if (import.meta.client) {
215
+ watch(
216
+ () => session.user.value,
217
+ async (user) => {
218
+ if (!user) {
219
+ hasHydratedActiveOrganization.value = false
220
+ return
221
+ }
222
+
223
+ if (organization.activeOrganization.value || hasHydratedActiveOrganization.value || isHydratingActiveOrganization.value) {
224
+ return
225
+ }
226
+
227
+ isHydratingActiveOrganization.value = true
228
+ try {
229
+ await organization.getActiveOrganization()
230
+ hasHydratedActiveOrganization.value = true
231
+ }
232
+ finally {
233
+ isHydratingActiveOrganization.value = false
234
+ }
235
+ },
236
+ { immediate: true },
237
+ )
238
+ }
239
+
240
+ return {
241
+ user: session.user,
242
+ activeOrganization: organization.activeOrganization,
243
+ getToken: session.getToken,
244
+ getAvailableOrganizations: organization.listOrganizations,
245
+ switchOrganization: organization.setActiveOrganization,
246
+ }
247
+ }
248
+ `.trimStart();
249
+ }
156
250
  const ALWAYS_REQUIRED_KEYS = [
157
251
  "telaApiUrl",
158
252
  "agentApiUrl",
@@ -225,7 +319,7 @@ const module$1 = defineNuxtModule({
225
319
  const hasAuthModule = (nuxt.options.modules ?? []).some(isAuthModuleEntry);
226
320
  if (!hasAuthModule) {
227
321
  throw new Error(
228
- '[@meistrari/chat-nuxt] Missing required module "@meistrari/auth-nuxt". Install and enable it so useTelaApplicationAuth() and event.context.auth are available.'
322
+ '[@meistrari/chat-nuxt] Missing required module "@meistrari/auth-nuxt". Install and enable it so chat auth composables and event.context.auth are available.'
229
323
  );
230
324
  }
231
325
  ensureIconifyPackages(nuxt.options.rootDir);
@@ -237,14 +331,25 @@ const module$1 = defineNuxtModule({
237
331
  nuxt.options.srcDir,
238
332
  join(runtimeDir, "internal/workspace-settings-resolver.stub")
239
333
  );
334
+ const agentEnvironmentResolverEntry = resolveAgentEnvironmentResolverEntry(
335
+ nuxt.options.srcDir,
336
+ join(runtimeDir, "internal/agent-environment-resolver.stub")
337
+ );
240
338
  const telaBuildPath = resolveTelaBuildLayer();
241
339
  const runtimeAliases = resolveRuntimeAliases(telaBuildPath);
340
+ const chatAuthMode = resolveChatAuthMode(nuxt.options.runtimeConfig);
341
+ const chatAuthTemplate = addTemplate({
342
+ filename: "chat-nuxt/auth.mjs",
343
+ getContents: () => buildChatAuthTemplate(chatAuthMode)
344
+ });
242
345
  if (!Array.isArray(nuxt.options.extends)) {
243
346
  nuxt.options.extends = nuxt.options.extends ? [nuxt.options.extends] : [];
244
347
  }
245
348
  nuxt.options.extends.unshift(telaBuildPath);
246
349
  nuxt.options.alias = {
247
350
  ...nuxt.options.alias,
351
+ "#chat-auth": chatAuthTemplate.dst,
352
+ "#chat-agent-environment-resolver": agentEnvironmentResolverEntry,
248
353
  "#chat-workspace-settings-resolver": workspaceSettingsResolverEntry,
249
354
  "#chat-runtime": runtimeDir,
250
355
  "@iconify-json/ph": resolvePackageRoot("@iconify-json/ph")
@@ -277,6 +382,17 @@ const module$1 = defineNuxtModule({
277
382
  features
278
383
  };
279
384
  validateRuntimeConfig(rc, options.validateConfig ?? false, features);
385
+ nuxt.options.css = nuxt.options.css || [];
386
+ for (const css of [
387
+ "markstream-vue/index.css",
388
+ join(runtimeDir, "assets/css/markstream.css"),
389
+ join(runtimeDir, "assets/css/code-theme.css")
390
+ ]) {
391
+ if (!nuxt.options.css.includes(css)) {
392
+ nuxt.options.css.push(css);
393
+ }
394
+ }
395
+ addPlugin(join(runtimeDir, "plugins/markstream"));
280
396
  addImportsDir(join(runtimeDir, "composables"));
281
397
  addImportsDir(join(runtimeDir, "utils"));
282
398
  addComponent({
@@ -338,6 +454,8 @@ export default globalThis.ELK;
338
454
  nitroConfig.scanDirs.push(serverDir);
339
455
  nitroConfig.alias = {
340
456
  ...nitroConfig.alias,
457
+ "#chat-auth": chatAuthTemplate.dst,
458
+ "#chat-agent-environment-resolver": agentEnvironmentResolverEntry,
341
459
  "#chat-runtime": runtimeDir,
342
460
  "#chat-workspace-settings-resolver": workspaceSettingsResolverEntry
343
461
  };
@@ -1,11 +1,12 @@
1
1
  <script setup>
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { parseContentWithFiles } from "../../utils/file";
3
4
  import ChatVaultLink from "./vault-link.vue";
4
5
  const props = defineProps({
5
6
  message: { type: Object, required: true }
6
7
  });
7
8
  const emit = defineEmits(["retry"]);
8
- const { user } = useTelaApplicationAuth();
9
+ const { user } = useChatAuth();
9
10
  const { getMemberByEmail } = useWorkspaceMembers();
10
11
  const embedConfig = useEmbedConfig();
11
12
  const mergedCustomComponents = computed(() => ({
@@ -14,7 +14,7 @@ type __VLS_Props = {
14
14
  conversationFiles?: ConversationFile[];
15
15
  workspaceSettings?: DeepReadonly<WorkspaceSettings> | null;
16
16
  allowModelSelection?: boolean;
17
- agentInputSchema?: DeepReadonly<TelaAgentInputVariable[]> | null;
17
+ agentInputSchema?: readonly TelaAgentInputVariable[] | null;
18
18
  agentInputSchemaLoading?: boolean;
19
19
  showModelSelector?: boolean;
20
20
  showCancelButton?: boolean;
@@ -15,7 +15,7 @@ const props = defineProps({
15
15
  conversationFiles: { type: Array, required: false },
16
16
  workspaceSettings: { type: null, required: false },
17
17
  allowModelSelection: { type: Boolean, required: false },
18
- agentInputSchema: { type: null, required: false },
18
+ agentInputSchema: { type: [Array, null], required: false },
19
19
  agentInputSchemaLoading: { type: Boolean, required: false },
20
20
  showModelSelector: { type: Boolean, required: false },
21
21
  showCancelButton: { type: Boolean, required: false }
@@ -14,7 +14,7 @@ type __VLS_Props = {
14
14
  conversationFiles?: ConversationFile[];
15
15
  workspaceSettings?: DeepReadonly<WorkspaceSettings> | null;
16
16
  allowModelSelection?: boolean;
17
- agentInputSchema?: DeepReadonly<TelaAgentInputVariable[]> | null;
17
+ agentInputSchema?: readonly TelaAgentInputVariable[] | null;
18
18
  agentInputSchemaLoading?: boolean;
19
19
  showModelSelector?: boolean;
20
20
  showCancelButton?: boolean;
@@ -1,4 +1,5 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { exportConversationToMarkdown } from "../utils/export-conversation.js";
3
4
  import { resolveChatStateScope } from "../utils/tela-chat.js";
4
5
  import { useConversationFileActions } from "./useConversationFiles.js";
@@ -150,7 +151,7 @@ function parseReasoningFromPolling(reasoning) {
150
151
  export function useChat() {
151
152
  const chatApi = useChatApi();
152
153
  const embedConfig = useEmbedConfig();
153
- const { user: authUser, activeOrganization } = useTelaApplicationAuth();
154
+ const { user: authUser, activeOrganization } = useChatAuth();
154
155
  const { addFilesToConversation } = useConversationFileActions();
155
156
  const isTelaAgentMode = computed(() => !!embedConfig?.telaAgentId.value);
156
157
  const chatScope = computed(() => resolveChatStateScope(
@@ -1,6 +1,7 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useChatApi() {
2
3
  const embedConfig = useEmbedConfig();
3
- const { activeOrganization } = useTelaApplicationAuth();
4
+ const { activeOrganization } = useChatAuth();
4
5
  function buildHeaders(headers) {
5
6
  const mergedHeaders = {
6
7
  ...headers ?? {}
@@ -1,4 +1,5 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { resolveChatStateScope } from "../utils/tela-chat.js";
3
4
  function sortConversationsByUpdatedAt(conversations) {
4
5
  return [...conversations].sort(
@@ -26,7 +27,7 @@ export function mergeFetchedConversations(fetchedConversations, localConversatio
26
27
  export function useConversations() {
27
28
  const chatApi = useChatApi();
28
29
  const embedConfig = useEmbedConfig();
29
- const { activeOrganization } = useTelaApplicationAuth();
30
+ const { activeOrganization } = useChatAuth();
30
31
  const conversationBuckets = useState("conversations", () => ({}));
31
32
  const loadingBuckets = useState("conversations-loading", () => ({}));
32
33
  const errorBuckets = useState("conversations-error", () => ({}));
@@ -1,7 +1,8 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
2
3
  export function useGitHubSkills() {
3
4
  const config = useRuntimeConfig();
4
- const { activeOrganization } = useTelaApplicationAuth();
5
+ const { activeOrganization } = useChatAuth();
5
6
  const { settings, updateSettings } = useWorkspaceSettings();
6
7
  const connection = useState("github-skills-connection", () => ({
7
8
  connected: false,
@@ -1,5 +1,6 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useWorkspaceExternalSkills(currentExternalSkills, searchQuery) {
2
- const { user } = useTelaApplicationAuth();
3
+ const { user } = useChatAuth();
3
4
  const { connection: skillsConnection, fetchConnection: fetchGitHubConnection, connect: connectGitHub } = useGitHubSkills();
4
5
  const pendingSkills = ref([]);
5
6
  const removedRefs = ref([]);
@@ -1,5 +1,6 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useWorkspaceMembers() {
2
- const { activeOrganization } = useTelaApplicationAuth();
3
+ const { activeOrganization } = useChatAuth();
3
4
  const members = computed(() => activeOrganization.value?.members ?? []);
4
5
  const membersMap = computed(() => {
5
6
  const map = /* @__PURE__ */ new Map();
@@ -1,8 +1,26 @@
1
1
  import * as Sentry from "@sentry/nuxt";
2
+ import { useChatAuth } from "#chat-auth";
3
+ function isNullableArray(value) {
4
+ return value === null || Array.isArray(value);
5
+ }
6
+ function assertWorkspaceSettingsResponse(value) {
7
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
8
+ throw new Error("Invalid workspace settings response");
9
+ }
10
+ const settings = value;
11
+ const hasValidWorkspaceId = typeof settings.workspaceId === "string" && settings.workspaceId.length > 0;
12
+ const hasValidSystemMessage = settings.systemMessage === null || typeof settings.systemMessage === "string";
13
+ const hasValidUpdatedAt = settings.updatedAt instanceof Date || typeof settings.updatedAt === "string" && settings.updatedAt.length > 0;
14
+ const hasValidCollections = isNullableArray(settings.contextFiles) && isNullableArray(settings.canvasTools) && isNullableArray(settings.knowledgeSources) && isNullableArray(settings.externalSkills);
15
+ if (!hasValidWorkspaceId || !hasValidSystemMessage || !hasValidUpdatedAt || !hasValidCollections) {
16
+ throw new Error("Invalid workspace settings response");
17
+ }
18
+ return value;
19
+ }
2
20
  export function useWorkspaceSettings() {
3
21
  const chatApi = useChatApi();
4
22
  const embedConfig = useEmbedConfig();
5
- const { activeOrganization } = useTelaApplicationAuth();
23
+ const { activeOrganization } = useChatAuth();
6
24
  const workspaceScope = computed(() => embedConfig?.workspaceId.value.trim() || activeOrganization.value?.id?.trim() || "default");
7
25
  const settingsBuckets = useState("workspace-settings", () => ({}));
8
26
  const loadingBuckets = useState("workspace-settings-loading", () => ({}));
@@ -65,12 +83,14 @@ export function useWorkspaceSettings() {
65
83
  if (hasWorkspaceSettingsOverride.value) {
66
84
  return settings.value;
67
85
  } else if (embedConfig !== null) {
68
- data = await $fetch(
69
- chatApi.path("/workspace/settings"),
70
- chatApi.withChatHeaders()
86
+ data = assertWorkspaceSettingsResponse(
87
+ await $fetch(
88
+ chatApi.path("/workspace/settings"),
89
+ chatApi.withChatHeaders()
90
+ )
71
91
  );
72
92
  } else {
73
- data = await $fetch("/api/workspace/settings");
93
+ data = assertWorkspaceSettingsResponse(await $fetch("/api/workspace/settings"));
74
94
  }
75
95
  if (import.meta.dev && embedConfig !== null && currentSettings) {
76
96
  const hydratedSettings = {
@@ -109,7 +129,7 @@ export function useWorkspaceSettings() {
109
129
  try {
110
130
  const path = embedConfig !== null ? chatApi.path("/workspace/settings") : "/api/workspace/settings";
111
131
  const options = embedConfig !== null ? chatApi.withChatHeaders({ method: "PATCH", body: updates }) : { method: "PATCH", body: updates };
112
- const data = await $fetch(path, options);
132
+ const data = assertWorkspaceSettingsResponse(await $fetch(path, options));
113
133
  settings.value = data;
114
134
  return data;
115
135
  } catch (e) {
@@ -1,5 +1,6 @@
1
+ import { useChatAuth } from "#chat-auth";
1
2
  export function useWorkspaces() {
2
- const { getAvailableOrganizations } = useTelaApplicationAuth();
3
+ const { getAvailableOrganizations } = useChatAuth();
3
4
  const { data: workspaces, status, refresh: refreshWorkspaces } = useAsyncData(
4
5
  "workspaces",
5
6
  () => getAvailableOrganizations(),
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { provideEmbedConfig, useEmbedConfig } from "../../composables/useEmbedConfig";
3
4
  import { provideChatAction } from "../../composables/useChatAction";
4
5
  import { normalizeTelaAgentId } from "../../utils/tela-chat";
@@ -19,7 +20,7 @@ const props = defineProps({
19
20
  });
20
21
  const emit = defineEmits(["update:conversationId", "action"]);
21
22
  const parentEmbedConfig = useEmbedConfig();
22
- const { activeOrganization } = useTelaApplicationAuth();
23
+ const { activeOrganization } = useChatAuth();
23
24
  const workspaceId = computed(() => activeOrganization.value?.id?.trim() ?? "");
24
25
  const normalizedTelaAgentId = computed(() => normalizeTelaAgentId(props.telaAgentId));
25
26
  const ignoredPropsWarningKey = ref(null);
@@ -1,4 +1,5 @@
1
1
  <script setup>
2
+ import { useChatAuth } from "#chat-auth";
2
3
  import { exportConversationToMarkdown } from "../../utils/export-conversation";
3
4
  import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
4
5
  import {
@@ -16,7 +17,7 @@ const props = defineProps({
16
17
  const emit = defineEmits(["update:conversationId"]);
17
18
  const chatApi = useChatApi();
18
19
  const embedConfig = useEmbedConfig();
19
- const { user: authUser } = useTelaApplicationAuth();
20
+ const { user: authUser } = useChatAuth();
20
21
  const { getMember } = useWorkspaceMembers();
21
22
  const statusToast = useStatusToast();
22
23
  const { conversations, loading: conversationsLoading, fetchConversations, createConversation, deleteConversation, renameConversation, duplicateConversation, updateConversationInList } = useConversations();
@@ -34,6 +35,7 @@ const effectiveWorkspaceSettings = computed(() => {
34
35
  return embedConfig?.workspaceSettings.value !== void 0 ? embedConfig.workspaceSettings.value : workspaceSettingsState.value;
35
36
  });
36
37
  const { closePanel: closeFilesPanel } = useConversationFilesPanel();
38
+ const { isOpen: isPreviewPanelOpen, panelWidth: previewPanelWidth, closePreview } = useFilePreviewPanel();
37
39
  const activeConversationId = ref(
38
40
  normalizeConversationId(props.conversationId) ?? normalizeConversationId(props.initialConversationId)
39
41
  );
@@ -119,6 +121,7 @@ function resetConversation(shouldEmit = true) {
119
121
  clearConversation();
120
122
  activeTab.value = "chat";
121
123
  closeFilesPanel();
124
+ closePreview();
122
125
  if (shouldEmit) {
123
126
  updateConversationId(null);
124
127
  } else {
@@ -130,6 +133,9 @@ async function openConversation(nextConversationId) {
130
133
  if (!normalizedConversationId) {
131
134
  return;
132
135
  }
136
+ if (activeConversationId.value !== normalizedConversationId) {
137
+ closePreview();
138
+ }
133
139
  updateConversationId(normalizedConversationId);
134
140
  await ensureConversationLoaded(normalizedConversationId);
135
141
  }
@@ -150,6 +156,7 @@ watch(() => props.conversationId, async (nextConversationId) => {
150
156
  return;
151
157
  }
152
158
  syncConversationId(normalizedConversationId);
159
+ closePreview();
153
160
  await ensureConversationLoaded(normalizedConversationId);
154
161
  });
155
162
  onMounted(async () => {
@@ -640,6 +647,14 @@ async function handleDelete() {
640
647
  </div>
641
648
  </TelaModal>
642
649
  </div>
650
+
651
+ <div
652
+ v-if="isPreviewPanelOpen"
653
+ h-full flex-shrink-0 pt-64px
654
+ :style="{ width: `${previewPanelWidth}px` }"
655
+ >
656
+ <FilePreviewPanel />
657
+ </div>
643
658
  </div>
644
659
  </template>
645
660
 
@@ -0,0 +1,2 @@
1
+ declare const _default: null;
2
+ export default _default;
@@ -0,0 +1 @@
1
+ export default null;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import { defineEventHandler, readBody } from "h3";
2
+ import { resolveChatContext } from "#chat-runtime/server/utils/chat-context";
3
+ import { persistWorkspaceSettingsUpdate } from "#chat-runtime/server/utils/workspace-settings-update";
4
+ export default defineEventHandler(async (event) => {
5
+ const context = resolveChatContext(event);
6
+ const body = await readBody(event);
7
+ return persistWorkspaceSettingsUpdate(context.workspaceId, context.user, body);
8
+ });
@@ -90,8 +90,12 @@ export default defineEventHandler(async (event) => {
90
90
  const environmentVariables = await resolveDefaultAgentEnvironmentVariables(
91
91
  event,
92
92
  context,
93
- { conversationId, messageId },
94
- "Failed to resolve workspace credentials on retry, proceeding without env vars"
93
+ {
94
+ conversationId,
95
+ skills: defaultAgentTurn.createPayload.skills ?? [],
96
+ logContext: { conversationId, messageId },
97
+ credentialsFailureMessage: "Failed to resolve workspace credentials on retry, proceeding without env vars"
98
+ }
95
99
  );
96
100
  const result = await createChatAgentSession(token, withEnvironmentVariables(defaultAgentTurn.createPayload, environmentVariables));
97
101
  if (result.success) {
@@ -115,8 +115,12 @@ export default defineEventHandler(async (event) => {
115
115
  const environmentVariables = await resolveDefaultAgentEnvironmentVariables(
116
116
  event,
117
117
  context,
118
- { conversationId },
119
- "Failed to resolve workspace credentials, proceeding without env vars"
118
+ {
119
+ conversationId,
120
+ skills: defaultAgentTurn.createPayload.skills ?? [],
121
+ logContext: { conversationId },
122
+ credentialsFailureMessage: "Failed to resolve workspace credentials, proceeding without env vars"
123
+ }
120
124
  );
121
125
  createChatAgentSession(token, withEnvironmentVariables(defaultAgentTurn.createPayload, environmentVariables)).then(async (result) => {
122
126
  if (result.success) {
@@ -1,87 +1,8 @@
1
- import { createError, defineEventHandler, readBody } from "h3";
2
- import { eq, and, inArray } from "drizzle-orm";
3
- import { useDb, schema } from "#chat-runtime/server/db";
1
+ import { defineEventHandler, readBody } from "h3";
4
2
  import { requireUser } from "#chat-runtime/server/utils/auth";
5
- import { rowToExternalSkill } from "#chat-runtime/server/utils/external-skills";
6
- import { logger } from "#chat-runtime/server/utils/logger";
3
+ import { persistWorkspaceSettingsUpdate } from "#chat-runtime/server/utils/workspace-settings-update";
7
4
  export default defineEventHandler(async (event) => {
8
5
  const session = requireUser(event);
9
- const db = useDb();
10
6
  const body = await readBody(event);
11
- const hasSystemMessage = body.systemMessage !== void 0;
12
- const hasContextFiles = body.contextFiles !== void 0;
13
- const hasCanvasTools = body.canvasTools !== void 0;
14
- const hasKnowledgeSources = body.knowledgeSources !== void 0;
15
- const hasExternalSkills = body.externalSkills !== void 0;
16
- if (!hasSystemMessage && !hasContextFiles && !hasCanvasTools && !hasKnowledgeSources && !hasExternalSkills) {
17
- throw createError({
18
- statusCode: 400,
19
- statusMessage: "At least one field is required to update"
20
- });
21
- }
22
- const updateData = {
23
- updatedAt: /* @__PURE__ */ new Date()
24
- };
25
- if (hasSystemMessage) {
26
- updateData.systemMessage = body.systemMessage?.trim() || null;
27
- }
28
- if (hasContextFiles) {
29
- updateData.contextFiles = body.contextFiles && body.contextFiles.length > 0 ? body.contextFiles : null;
30
- }
31
- if (hasCanvasTools) {
32
- updateData.canvasTools = body.canvasTools && body.canvasTools.length > 0 ? body.canvasTools : null;
33
- }
34
- if (hasKnowledgeSources) {
35
- updateData.knowledgeSources = body.knowledgeSources && body.knowledgeSources.length > 0 ? body.knowledgeSources : null;
36
- }
37
- const [existing] = await db.select().from(schema.workspaceSettings).where(eq(schema.workspaceSettings.workspaceId, session.workspace.id));
38
- if (hasExternalSkills) {
39
- const existingSkillRows = await db.select().from(schema.externalSkills).where(eq(schema.externalSkills.workspaceId, session.workspace.id));
40
- const existingRefs = new Set(existingSkillRows.map((s) => s.ref));
41
- const newSkills = body.externalSkills ?? [];
42
- const newRefs = new Set(newSkills.map((s) => s.ref));
43
- const refsToDelete = existingSkillRows.filter((s) => !newRefs.has(s.ref)).map((s) => s.ref);
44
- if (refsToDelete.length > 0) {
45
- await db.delete(schema.externalSkills).where(
46
- and(
47
- eq(schema.externalSkills.workspaceId, session.workspace.id),
48
- inArray(schema.externalSkills.ref, refsToDelete)
49
- )
50
- );
51
- }
52
- const skillsToInsert = newSkills.filter((s) => !existingRefs.has(s.ref)).map((skill) => ({
53
- workspaceId: session.workspace.id,
54
- ref: skill.ref,
55
- name: skill.name,
56
- description: skill.description,
57
- runtime: skill.runtime,
58
- allowedTools: [...skill.allowedTools],
59
- isPublic: skill.isPublic,
60
- addedByUserId: skill.addedBy?.userId || session.user.id,
61
- addedByUsername: skill.addedBy?.username || session.user.name || "",
62
- addedAt: skill.addedAt ? new Date(skill.addedAt) : /* @__PURE__ */ new Date()
63
- }));
64
- if (skillsToInsert.length > 0) {
65
- await db.insert(schema.externalSkills).values(skillsToInsert);
66
- }
67
- }
68
- logger.info({ workspaceId: session.workspace.id, fields: Object.keys(updateData) }, "Workspace settings updating");
69
- let settings;
70
- if (existing) {
71
- const [updated] = await db.update(schema.workspaceSettings).set(updateData).where(eq(schema.workspaceSettings.workspaceId, session.workspace.id)).returning();
72
- settings = updated;
73
- } else {
74
- const [created] = await db.insert(schema.workspaceSettings).values({
75
- workspaceId: session.workspace.id,
76
- ...updateData
77
- }).returning();
78
- settings = created;
79
- }
80
- const skillRows = await db.select().from(schema.externalSkills).where(eq(schema.externalSkills.workspaceId, session.workspace.id));
81
- const externalSkills = skillRows.map(rowToExternalSkill);
82
- logger.info({ workspaceId: session.workspace.id, externalSkillsCount: externalSkills.length }, "Workspace settings updated");
83
- return {
84
- ...settings,
85
- externalSkills: externalSkills.length > 0 ? externalSkills : null
86
- };
7
+ return persistWorkspaceSettingsUpdate(session.workspace.id, session.user, body);
87
8
  });
@@ -2,11 +2,12 @@ import { createError } from "h3";
2
2
  import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { agentApiQueryResponseSchema, agentApiSessionSchema } from "#chat-runtime/types/agent";
4
4
  import { logger } from "#chat-runtime/server/utils/logger";
5
+ import { resolveAuthToken } from "#chat-runtime/server/utils/auth-token";
5
6
  const getConfig = () => useRuntimeConfig();
6
7
  export function getAgentDataToken(event) {
7
- const cookieToken = event.context.auth?.token;
8
- if (cookieToken) {
9
- return cookieToken;
8
+ const token = resolveAuthToken(event);
9
+ if (token) {
10
+ return token;
10
11
  }
11
12
  throw createError({
12
13
  statusCode: 401,
@@ -0,0 +1,8 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
3
+ export type AgentEnvironmentResolverContext = ChatRuntimeContext & {
4
+ conversationId: string;
5
+ skills: readonly string[];
6
+ };
7
+ export type AgentEnvironmentVariables = Record<string, string>;
8
+ export declare function resolveHostAgentEnvironmentVariables(event: H3Event, context: AgentEnvironmentResolverContext, logContext: Record<string, unknown>): Promise<AgentEnvironmentVariables>;
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ import registeredAgentEnvironmentResolver from "#chat-agent-environment-resolver";
3
+ import { logger } from "#chat-runtime/server/utils/logger";
4
+ const agentEnvironmentVariablesSchema = z.record(z.string(), z.string()).nullable().optional();
5
+ function getRegisteredAgentEnvironmentResolver() {
6
+ const resolver = registeredAgentEnvironmentResolver;
7
+ return typeof resolver === "function" ? resolver : null;
8
+ }
9
+ function parseAgentEnvironmentVariables(value, logContext) {
10
+ const parsed = agentEnvironmentVariablesSchema.safeParse(value);
11
+ if (!parsed.success) {
12
+ logger.warn({
13
+ ...logContext,
14
+ issues: parsed.error.issues
15
+ }, "Agent environment resolver returned an invalid payload");
16
+ return {};
17
+ }
18
+ return parsed.data ?? {};
19
+ }
20
+ export async function resolveHostAgentEnvironmentVariables(event, context, logContext) {
21
+ const resolver = getRegisteredAgentEnvironmentResolver();
22
+ if (!resolver) {
23
+ return {};
24
+ }
25
+ try {
26
+ return parseAgentEnvironmentVariables(await resolver(event, context), logContext);
27
+ } catch (error) {
28
+ logger.warn({
29
+ ...logContext,
30
+ error
31
+ }, "Agent environment resolver failed, proceeding without host env vars");
32
+ return {};
33
+ }
34
+ }
@@ -0,0 +1,2 @@
1
+ import { type H3Event } from 'h3';
2
+ export declare function resolveAuthToken(event: H3Event): string | undefined;
@@ -0,0 +1,21 @@
1
+ import { getCookie } from "h3";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ const DEFAULT_FIRST_PARTY_JWT_COOKIE_NAME = "tela-jwt";
4
+ function resolveFirstPartyJwtCookieName(event) {
5
+ const config = useRuntimeConfig(event);
6
+ return config.public?.telaAuth?.jwtCookieName || DEFAULT_FIRST_PARTY_JWT_COOKIE_NAME;
7
+ }
8
+ export function resolveAuthToken(event) {
9
+ const auth = event.context.auth;
10
+ if (auth?.token) {
11
+ return auth.token;
12
+ }
13
+ if (!auth?.user || !auth.workspace) {
14
+ return void 0;
15
+ }
16
+ const token = getCookie(event, resolveFirstPartyJwtCookieName(event));
17
+ if (token) {
18
+ auth.token = token;
19
+ }
20
+ return token;
21
+ }
@@ -34,6 +34,11 @@ export declare function prepareDefaultAgentTurn(event: H3Event, db: DbInstance,
34
34
  requestedModel?: string;
35
35
  workspaceSettingsSnapshot?: unknown;
36
36
  }): Promise<PreparedDefaultAgentTurn>;
37
- export declare function resolveDefaultAgentEnvironmentVariables(event: H3Event, context: ChatRuntimeContext, logContext: Record<string, unknown>, failureMessage: string): Promise<Record<string, string>>;
37
+ export declare function resolveDefaultAgentEnvironmentVariables(event: H3Event, context: ChatRuntimeContext, options: {
38
+ conversationId: string;
39
+ skills?: readonly string[];
40
+ logContext: Record<string, unknown>;
41
+ credentialsFailureMessage: string;
42
+ }): Promise<Record<string, string>>;
38
43
  export declare function withEnvironmentVariables(payload: AgentApiChatPayload, environmentVariables: Record<string, string>): AgentApiChatPayload;
39
44
  export {};
@@ -2,6 +2,7 @@ import { and, eq, isNull } from "drizzle-orm";
2
2
  import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { schema } from "#chat-runtime/server/db";
4
4
  import { buildMessageWithContext, renderSystemPrompt, truncateTitle } from "#chat-runtime/server/utils/chat-message-builder";
5
+ import { resolveHostAgentEnvironmentVariables } from "#chat-runtime/server/utils/agent-environment";
5
6
  import {
6
7
  getConversationAppliedSettings,
7
8
  getWorkspaceSettingsUpdatedAt,
@@ -150,15 +151,33 @@ export async function prepareDefaultAgentTurn(event, db, context, options) {
150
151
  }
151
152
  };
152
153
  }
153
- export async function resolveDefaultAgentEnvironmentVariables(event, context, logContext, failureMessage) {
154
- if (!resolveChatNuxtFeatures(useRuntimeConfig()).credentials)
155
- return {};
156
- try {
157
- return await resolveWorkspaceCredentials(event, context.workspaceId);
158
- } catch (error) {
159
- logger.warn({ ...logContext, error }, failureMessage);
160
- return {};
154
+ export async function resolveDefaultAgentEnvironmentVariables(event, context, options) {
155
+ let credentialVariables = {};
156
+ const logContext = {
157
+ workspaceId: context.workspaceId,
158
+ conversationId: options.conversationId,
159
+ ...options.logContext
160
+ };
161
+ if (resolveChatNuxtFeatures(useRuntimeConfig()).credentials) {
162
+ try {
163
+ credentialVariables = await resolveWorkspaceCredentials(event, context.workspaceId);
164
+ } catch (error) {
165
+ logger.warn({ ...logContext, error }, options.credentialsFailureMessage);
166
+ }
161
167
  }
168
+ const hostVariables = await resolveHostAgentEnvironmentVariables(
169
+ event,
170
+ {
171
+ ...context,
172
+ conversationId: options.conversationId,
173
+ skills: options.skills ?? []
174
+ },
175
+ logContext
176
+ );
177
+ return {
178
+ ...credentialVariables,
179
+ ...hostVariables
180
+ };
162
181
  }
163
182
  export function withEnvironmentVariables(payload, environmentVariables) {
164
183
  if (Object.keys(environmentVariables).length === 0)
@@ -1,9 +1,10 @@
1
1
  import { createError } from "h3";
2
2
  import { useRuntimeConfig } from "nitropack/runtime";
3
3
  import { logger } from "#chat-runtime/server/utils/logger";
4
+ import { resolveAuthToken } from "#chat-runtime/server/utils/auth-token";
4
5
  export async function telaFetch(event, path, options) {
5
6
  const config = useRuntimeConfig();
6
- const token = event.context.auth?.token;
7
+ const token = resolveAuthToken(event);
7
8
  if (!token) {
8
9
  throw createError({ statusCode: 401, statusMessage: "No auth token" });
9
10
  }
@@ -0,0 +1,16 @@
1
+ import { type ContextFile, type ExternalSkill } from '#chat-runtime/server/db';
2
+ import type { CanvasTool } from '#chat-runtime/types/agent';
3
+ import type { KnowledgeSource } from '#chat-runtime/types/chat';
4
+ export type WorkspaceSettingsPatchBody = {
5
+ systemMessage?: string | null;
6
+ contextFiles?: ContextFile[] | null;
7
+ canvasTools?: CanvasTool[] | null;
8
+ knowledgeSources?: KnowledgeSource[] | null;
9
+ externalSkills?: ExternalSkill[] | null;
10
+ };
11
+ type WorkspaceSettingsUser = {
12
+ id: string;
13
+ name?: string | null;
14
+ };
15
+ export declare function persistWorkspaceSettingsUpdate(workspaceId: string, user: WorkspaceSettingsUser, body: WorkspaceSettingsPatchBody): Promise<any>;
16
+ export {};
@@ -0,0 +1,84 @@
1
+ import { createError } from "h3";
2
+ import { and, eq, inArray } from "drizzle-orm";
3
+ import { schema, useDb } from "#chat-runtime/server/db";
4
+ import { rowToExternalSkill } from "#chat-runtime/server/utils/external-skills";
5
+ import { logger } from "#chat-runtime/server/utils/logger";
6
+ export async function persistWorkspaceSettingsUpdate(workspaceId, user, body) {
7
+ const db = useDb();
8
+ const hasSystemMessage = body.systemMessage !== void 0;
9
+ const hasContextFiles = body.contextFiles !== void 0;
10
+ const hasCanvasTools = body.canvasTools !== void 0;
11
+ const hasKnowledgeSources = body.knowledgeSources !== void 0;
12
+ const hasExternalSkills = body.externalSkills !== void 0;
13
+ if (!hasSystemMessage && !hasContextFiles && !hasCanvasTools && !hasKnowledgeSources && !hasExternalSkills) {
14
+ throw createError({
15
+ statusCode: 400,
16
+ statusMessage: "At least one field is required to update"
17
+ });
18
+ }
19
+ const updateData = {
20
+ updatedAt: /* @__PURE__ */ new Date()
21
+ };
22
+ if (hasSystemMessage) {
23
+ updateData.systemMessage = body.systemMessage?.trim() || null;
24
+ }
25
+ if (hasContextFiles) {
26
+ updateData.contextFiles = body.contextFiles && body.contextFiles.length > 0 ? body.contextFiles : null;
27
+ }
28
+ if (hasCanvasTools) {
29
+ updateData.canvasTools = body.canvasTools && body.canvasTools.length > 0 ? body.canvasTools : null;
30
+ }
31
+ if (hasKnowledgeSources) {
32
+ updateData.knowledgeSources = body.knowledgeSources && body.knowledgeSources.length > 0 ? body.knowledgeSources : null;
33
+ }
34
+ const [existing] = await db.select().from(schema.workspaceSettings).where(eq(schema.workspaceSettings.workspaceId, workspaceId));
35
+ if (hasExternalSkills) {
36
+ const existingSkillRows = await db.select().from(schema.externalSkills).where(eq(schema.externalSkills.workspaceId, workspaceId));
37
+ const existingRefs = new Set(existingSkillRows.map((s) => s.ref));
38
+ const newSkills = body.externalSkills ?? [];
39
+ const newRefs = new Set(newSkills.map((s) => s.ref));
40
+ const refsToDelete = existingSkillRows.filter((s) => !newRefs.has(s.ref)).map((s) => s.ref);
41
+ if (refsToDelete.length > 0) {
42
+ await db.delete(schema.externalSkills).where(
43
+ and(
44
+ eq(schema.externalSkills.workspaceId, workspaceId),
45
+ inArray(schema.externalSkills.ref, refsToDelete)
46
+ )
47
+ );
48
+ }
49
+ const skillsToInsert = newSkills.filter((s) => !existingRefs.has(s.ref)).map((skill) => ({
50
+ workspaceId,
51
+ ref: skill.ref,
52
+ name: skill.name,
53
+ description: skill.description,
54
+ runtime: skill.runtime,
55
+ allowedTools: [...skill.allowedTools],
56
+ isPublic: skill.isPublic,
57
+ addedByUserId: skill.addedBy?.userId || user.id,
58
+ addedByUsername: skill.addedBy?.username || user.name || "",
59
+ addedAt: skill.addedAt ? new Date(skill.addedAt) : /* @__PURE__ */ new Date()
60
+ }));
61
+ if (skillsToInsert.length > 0) {
62
+ await db.insert(schema.externalSkills).values(skillsToInsert);
63
+ }
64
+ }
65
+ logger.info({ workspaceId, fields: Object.keys(updateData) }, "Workspace settings updating");
66
+ let settings;
67
+ if (existing) {
68
+ const [updated] = await db.update(schema.workspaceSettings).set(updateData).where(eq(schema.workspaceSettings.workspaceId, workspaceId)).returning();
69
+ settings = updated;
70
+ } else {
71
+ const [created] = await db.insert(schema.workspaceSettings).values({
72
+ workspaceId,
73
+ ...updateData
74
+ }).returning();
75
+ settings = created;
76
+ }
77
+ const skillRows = await db.select().from(schema.externalSkills).where(eq(schema.externalSkills.workspaceId, workspaceId));
78
+ const externalSkills = skillRows.map(rowToExternalSkill);
79
+ logger.info({ workspaceId, externalSkillsCount: externalSkills.length }, "Workspace settings updated");
80
+ return {
81
+ ...settings,
82
+ externalSkills: externalSkills.length > 0 ? externalSkills : null
83
+ };
84
+ }
@@ -1,6 +1,7 @@
1
1
  import { useRuntimeConfig } from "nitropack/runtime";
2
2
  import { eq } from "drizzle-orm";
3
3
  import { schema } from "#chat-runtime/server/db";
4
+ import { resolveAuthToken } from "#chat-runtime/server/utils/auth-token";
4
5
  import { telaFetch } from "#chat-runtime/server/utils/tela-api";
5
6
  export async function getWorkspaceSettings(db, workspaceId) {
6
7
  const [settings] = await db.select().from(schema.workspaceSettings).where(eq(schema.workspaceSettings.workspaceId, workspaceId));
@@ -8,6 +9,7 @@ export async function getWorkspaceSettings(db, workspaceId) {
8
9
  }
9
10
  export async function resolveWorkspaceCredentials(event, workspaceId) {
10
11
  const config = useRuntimeConfig();
12
+ const token = resolveAuthToken(event);
11
13
  return await telaFetch(
12
14
  event,
13
15
  `/workspaces/${workspaceId}/credentials/resolve`,
@@ -15,7 +17,7 @@ export async function resolveWorkspaceCredentials(event, workspaceId) {
15
17
  headers: {
16
18
  "X-Service-Name": "chat-app",
17
19
  "X-Service-Key": config.chatAppServiceKey,
18
- "x-data-token": event.context.auth?.token ?? ""
20
+ "x-data-token": token ?? ""
19
21
  }
20
22
  }
21
23
  ) ?? {};
@@ -0,0 +1,4 @@
1
+ declare module '#chat-agent-environment-resolver' {
2
+ const resolver: unknown
3
+ export default resolver
4
+ }
@@ -0,0 +1,35 @@
1
+ import type { Ref } from 'vue'
2
+
3
+ type ChatAuthUser = {
4
+ id: string
5
+ email?: string | null
6
+ name?: string | null
7
+ image?: string | null
8
+ }
9
+
10
+ type ChatAuthMemberUser = {
11
+ name: string
12
+ email: string
13
+ image?: string
14
+ }
15
+
16
+ type ChatAuthMember = {
17
+ id: string
18
+ userId: string
19
+ user: ChatAuthMemberUser
20
+ }
21
+
22
+ type ChatAuthOrganization = {
23
+ id: string
24
+ members?: ChatAuthMember[]
25
+ }
26
+
27
+ declare module '#chat-auth' {
28
+ export function useChatAuth(): {
29
+ user: Ref<ChatAuthUser | null>
30
+ activeOrganization: Ref<ChatAuthOrganization | null>
31
+ getToken: () => Promise<string | null | undefined>
32
+ getAvailableOrganizations: () => Promise<ChatAuthOrganization[]>
33
+ switchOrganization: (organizationId: string) => Promise<void>
34
+ }
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {