@meistrari/chat-nuxt 1.5.1 → 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.
- package/README.md +57 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +109 -2
- package/dist/runtime/components/chat/message-bubble.vue +2 -1
- package/dist/runtime/composables/useChat.js +2 -1
- package/dist/runtime/composables/useChatApi.js +2 -1
- package/dist/runtime/composables/useConversations.js +2 -1
- package/dist/runtime/composables/useGitHubSkills.js +2 -1
- package/dist/runtime/composables/useWorkspaceExternalSkills.js +2 -1
- package/dist/runtime/composables/useWorkspaceMembers.js +2 -1
- package/dist/runtime/composables/useWorkspaceSettings.js +2 -1
- package/dist/runtime/composables/useWorkspaces.js +2 -1
- package/dist/runtime/embed/components/ChatEmbed.vue +2 -1
- package/dist/runtime/embed/components/ChatEmbedInner.vue +2 -1
- package/dist/runtime/internal/agent-environment-resolver.stub.d.ts +2 -0
- package/dist/runtime/internal/agent-environment-resolver.stub.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +6 -2
- package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +6 -2
- package/dist/runtime/server/utils/agent-api.js +4 -3
- package/dist/runtime/server/utils/agent-environment.d.ts +8 -0
- package/dist/runtime/server/utils/agent-environment.js +34 -0
- package/dist/runtime/server/utils/auth-token.d.ts +2 -0
- package/dist/runtime/server/utils/auth-token.js +21 -0
- package/dist/runtime/server/utils/conversation-agent-turn.d.ts +6 -1
- package/dist/runtime/server/utils/conversation-agent-turn.js +27 -8
- package/dist/runtime/server/utils/tela-api.js +2 -1
- package/dist/runtime/server/utils/workspace.js +3 -1
- package/dist/runtime/types/chat-agent-environment-resolver.d.ts +4 -0
- package/dist/runtime/types/chat-auth.d.ts +35 -0
- 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
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, addPlugin, 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
|
|
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")
|
|
@@ -349,6 +454,8 @@ export default globalThis.ELK;
|
|
|
349
454
|
nitroConfig.scanDirs.push(serverDir);
|
|
350
455
|
nitroConfig.alias = {
|
|
351
456
|
...nitroConfig.alias,
|
|
457
|
+
"#chat-auth": chatAuthTemplate.dst,
|
|
458
|
+
"#chat-agent-environment-resolver": agentEnvironmentResolverEntry,
|
|
352
459
|
"#chat-runtime": runtimeDir,
|
|
353
460
|
"#chat-workspace-settings-resolver": workspaceSettingsResolverEntry
|
|
354
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 } =
|
|
9
|
+
const { user } = useChatAuth();
|
|
9
10
|
const { getMemberByEmail } = useWorkspaceMembers();
|
|
10
11
|
const embedConfig = useEmbedConfig();
|
|
11
12
|
const mergedCustomComponents = computed(() => ({
|
|
@@ -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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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,4 +1,5 @@
|
|
|
1
1
|
import * as Sentry from "@sentry/nuxt";
|
|
2
|
+
import { useChatAuth } from "#chat-auth";
|
|
2
3
|
function isNullableArray(value) {
|
|
3
4
|
return value === null || Array.isArray(value);
|
|
4
5
|
}
|
|
@@ -19,7 +20,7 @@ function assertWorkspaceSettingsResponse(value) {
|
|
|
19
20
|
export function useWorkspaceSettings() {
|
|
20
21
|
const chatApi = useChatApi();
|
|
21
22
|
const embedConfig = useEmbedConfig();
|
|
22
|
-
const { activeOrganization } =
|
|
23
|
+
const { activeOrganization } = useChatAuth();
|
|
23
24
|
const workspaceScope = computed(() => embedConfig?.workspaceId.value.trim() || activeOrganization.value?.id?.trim() || "default");
|
|
24
25
|
const settingsBuckets = useState("workspace-settings", () => ({}));
|
|
25
26
|
const loadingBuckets = useState("workspace-settings-loading", () => ({}));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { useChatAuth } from "#chat-auth";
|
|
1
2
|
export function useWorkspaces() {
|
|
2
|
-
const { getAvailableOrganizations } =
|
|
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 } =
|
|
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 } =
|
|
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();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default null;
|
|
@@ -90,8 +90,12 @@ export default defineEventHandler(async (event) => {
|
|
|
90
90
|
const environmentVariables = await resolveDefaultAgentEnvironmentVariables(
|
|
91
91
|
event,
|
|
92
92
|
context,
|
|
93
|
-
{
|
|
94
|
-
|
|
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
|
-
{
|
|
119
|
-
|
|
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) {
|
|
@@ -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
|
|
8
|
-
if (
|
|
9
|
-
return
|
|
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,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,
|
|
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,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
7
|
+
const token = resolveAuthToken(event);
|
|
7
8
|
if (!token) {
|
|
8
9
|
throw createError({ statusCode: 401, statusMessage: "No auth token" });
|
|
9
10
|
}
|
|
@@ -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":
|
|
20
|
+
"x-data-token": token ?? ""
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
) ?? {};
|
|
@@ -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
|
+
}
|