@meistrari/chat-nuxt 1.5.0 → 1.5.1
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/dist/module.json +1 -1
- package/dist/module.mjs +12 -1
- package/dist/runtime/components/chat/message-input.d.vue.ts +1 -1
- package/dist/runtime/components/chat/message-input.vue +1 -1
- package/dist/runtime/components/chat/message-input.vue.d.ts +1 -1
- package/dist/runtime/composables/useWorkspaceSettings.js +24 -5
- package/dist/runtime/embed/components/ChatEmbedInner.vue +14 -0
- package/dist/runtime/server/api/chat/workspace/settings.patch.d.ts +2 -0
- package/dist/runtime/server/api/chat/workspace/settings.patch.js +8 -0
- package/dist/runtime/server/api/workspace/settings.patch.js +3 -82
- package/dist/runtime/server/utils/workspace-settings-update.d.ts +16 -0
- package/dist/runtime/server/utils/workspace-settings-update.js +84 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
|
|
5
5
|
|
|
6
6
|
const localRequire = createRequire(import.meta.url);
|
|
7
7
|
const runtimeAliasPackages = [
|
|
@@ -277,6 +277,17 @@ const module$1 = defineNuxtModule({
|
|
|
277
277
|
features
|
|
278
278
|
};
|
|
279
279
|
validateRuntimeConfig(rc, options.validateConfig ?? false, features);
|
|
280
|
+
nuxt.options.css = nuxt.options.css || [];
|
|
281
|
+
for (const css of [
|
|
282
|
+
"markstream-vue/index.css",
|
|
283
|
+
join(runtimeDir, "assets/css/markstream.css"),
|
|
284
|
+
join(runtimeDir, "assets/css/code-theme.css")
|
|
285
|
+
]) {
|
|
286
|
+
if (!nuxt.options.css.includes(css)) {
|
|
287
|
+
nuxt.options.css.push(css);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
addPlugin(join(runtimeDir, "plugins/markstream"));
|
|
280
291
|
addImportsDir(join(runtimeDir, "composables"));
|
|
281
292
|
addImportsDir(join(runtimeDir, "utils"));
|
|
282
293
|
addComponent({
|
|
@@ -14,7 +14,7 @@ type __VLS_Props = {
|
|
|
14
14
|
conversationFiles?: ConversationFile[];
|
|
15
15
|
workspaceSettings?: DeepReadonly<WorkspaceSettings> | null;
|
|
16
16
|
allowModelSelection?: boolean;
|
|
17
|
-
agentInputSchema?:
|
|
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?:
|
|
17
|
+
agentInputSchema?: readonly TelaAgentInputVariable[] | null;
|
|
18
18
|
agentInputSchemaLoading?: boolean;
|
|
19
19
|
showModelSelector?: boolean;
|
|
20
20
|
showCancelButton?: boolean;
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import * as Sentry from "@sentry/nuxt";
|
|
2
|
+
function isNullableArray(value) {
|
|
3
|
+
return value === null || Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function assertWorkspaceSettingsResponse(value) {
|
|
6
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7
|
+
throw new Error("Invalid workspace settings response");
|
|
8
|
+
}
|
|
9
|
+
const settings = value;
|
|
10
|
+
const hasValidWorkspaceId = typeof settings.workspaceId === "string" && settings.workspaceId.length > 0;
|
|
11
|
+
const hasValidSystemMessage = settings.systemMessage === null || typeof settings.systemMessage === "string";
|
|
12
|
+
const hasValidUpdatedAt = settings.updatedAt instanceof Date || typeof settings.updatedAt === "string" && settings.updatedAt.length > 0;
|
|
13
|
+
const hasValidCollections = isNullableArray(settings.contextFiles) && isNullableArray(settings.canvasTools) && isNullableArray(settings.knowledgeSources) && isNullableArray(settings.externalSkills);
|
|
14
|
+
if (!hasValidWorkspaceId || !hasValidSystemMessage || !hasValidUpdatedAt || !hasValidCollections) {
|
|
15
|
+
throw new Error("Invalid workspace settings response");
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
2
19
|
export function useWorkspaceSettings() {
|
|
3
20
|
const chatApi = useChatApi();
|
|
4
21
|
const embedConfig = useEmbedConfig();
|
|
@@ -65,12 +82,14 @@ export function useWorkspaceSettings() {
|
|
|
65
82
|
if (hasWorkspaceSettingsOverride.value) {
|
|
66
83
|
return settings.value;
|
|
67
84
|
} else if (embedConfig !== null) {
|
|
68
|
-
data =
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
data = assertWorkspaceSettingsResponse(
|
|
86
|
+
await $fetch(
|
|
87
|
+
chatApi.path("/workspace/settings"),
|
|
88
|
+
chatApi.withChatHeaders()
|
|
89
|
+
)
|
|
71
90
|
);
|
|
72
91
|
} else {
|
|
73
|
-
data = await $fetch("/api/workspace/settings");
|
|
92
|
+
data = assertWorkspaceSettingsResponse(await $fetch("/api/workspace/settings"));
|
|
74
93
|
}
|
|
75
94
|
if (import.meta.dev && embedConfig !== null && currentSettings) {
|
|
76
95
|
const hydratedSettings = {
|
|
@@ -109,7 +128,7 @@ export function useWorkspaceSettings() {
|
|
|
109
128
|
try {
|
|
110
129
|
const path = embedConfig !== null ? chatApi.path("/workspace/settings") : "/api/workspace/settings";
|
|
111
130
|
const options = embedConfig !== null ? chatApi.withChatHeaders({ method: "PATCH", body: updates }) : { method: "PATCH", body: updates };
|
|
112
|
-
const data = await $fetch(path, options);
|
|
131
|
+
const data = assertWorkspaceSettingsResponse(await $fetch(path, options));
|
|
113
132
|
settings.value = data;
|
|
114
133
|
return data;
|
|
115
134
|
} catch (e) {
|
|
@@ -34,6 +34,7 @@ const effectiveWorkspaceSettings = computed(() => {
|
|
|
34
34
|
return embedConfig?.workspaceSettings.value !== void 0 ? embedConfig.workspaceSettings.value : workspaceSettingsState.value;
|
|
35
35
|
});
|
|
36
36
|
const { closePanel: closeFilesPanel } = useConversationFilesPanel();
|
|
37
|
+
const { isOpen: isPreviewPanelOpen, panelWidth: previewPanelWidth, closePreview } = useFilePreviewPanel();
|
|
37
38
|
const activeConversationId = ref(
|
|
38
39
|
normalizeConversationId(props.conversationId) ?? normalizeConversationId(props.initialConversationId)
|
|
39
40
|
);
|
|
@@ -119,6 +120,7 @@ function resetConversation(shouldEmit = true) {
|
|
|
119
120
|
clearConversation();
|
|
120
121
|
activeTab.value = "chat";
|
|
121
122
|
closeFilesPanel();
|
|
123
|
+
closePreview();
|
|
122
124
|
if (shouldEmit) {
|
|
123
125
|
updateConversationId(null);
|
|
124
126
|
} else {
|
|
@@ -130,6 +132,9 @@ async function openConversation(nextConversationId) {
|
|
|
130
132
|
if (!normalizedConversationId) {
|
|
131
133
|
return;
|
|
132
134
|
}
|
|
135
|
+
if (activeConversationId.value !== normalizedConversationId) {
|
|
136
|
+
closePreview();
|
|
137
|
+
}
|
|
133
138
|
updateConversationId(normalizedConversationId);
|
|
134
139
|
await ensureConversationLoaded(normalizedConversationId);
|
|
135
140
|
}
|
|
@@ -150,6 +155,7 @@ watch(() => props.conversationId, async (nextConversationId) => {
|
|
|
150
155
|
return;
|
|
151
156
|
}
|
|
152
157
|
syncConversationId(normalizedConversationId);
|
|
158
|
+
closePreview();
|
|
153
159
|
await ensureConversationLoaded(normalizedConversationId);
|
|
154
160
|
});
|
|
155
161
|
onMounted(async () => {
|
|
@@ -640,6 +646,14 @@ async function handleDelete() {
|
|
|
640
646
|
</div>
|
|
641
647
|
</TelaModal>
|
|
642
648
|
</div>
|
|
649
|
+
|
|
650
|
+
<div
|
|
651
|
+
v-if="isPreviewPanelOpen"
|
|
652
|
+
h-full flex-shrink-0 pt-64px
|
|
653
|
+
:style="{ width: `${previewPanelWidth}px` }"
|
|
654
|
+
>
|
|
655
|
+
<FilePreviewPanel />
|
|
656
|
+
</div>
|
|
643
657
|
</div>
|
|
644
658
|
</template>
|
|
645
659
|
|
|
@@ -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
|
+
});
|
|
@@ -1,87 +1,8 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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
|
-
|
|
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
|
});
|
|
@@ -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
|
+
}
|