refactor(ai): extract assistant into an official module (#749)

* feat(modules): expose host data boundaries

* feat(modules): add frontend extension surfaces

* refactor(ai): remove assistant from core

* chore(ai): prepare the module extraction

* fix(modules): load extension styles after the host bundle

* chore(modules): lock SDK 3.3.0
This commit is contained in:
Darko Gjorgjijoski
2026-08-05 22:10:21 +02:00
committed by GitHub
parent 99ae7def75
commit 0b9ae9ea00
111 changed files with 1137 additions and 8952 deletions
@@ -15,10 +15,9 @@ import InstallationLayout from '@/scripts/layouts/InstallationLayout.vue'
* 4. DatabaseView (/installation/database)
* 5. DomainView (/installation/domain)
* 6. MailView (/installation/mail)
* 7. AiView (/installation/ai) — optional, skippable
* 8. AccountView (/installation/account)
* 9. CompanyView (/installation/company)
* 10. PreferencesView (/installation/preferences)
* 7. AccountView (/installation/account)
* 8. CompanyView (/installation/company)
* 9. PreferencesView (/installation/preferences)
*
* Each child view owns its own next() function and calls router.push() to
* the next step by route name. There is no event-based step coordination —
@@ -91,15 +90,6 @@ export const installationRoutes: RouteRecordRaw[] = [
isInstallation: true,
},
},
{
path: 'ai',
name: 'installation.ai',
component: () => import('./views/AiView.vue'),
meta: {
title: 'settings.ai.installer_title',
isInstallation: true,
},
},
{
path: 'account',
name: 'installation.account',
@@ -1,100 +0,0 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { installClient } from '@/scripts/api/install-client'
import type {
AiConfig,
AiDriverOption,
AiDriversResponse,
} from '@/scripts/types/ai-config'
import AiConfigurationForm from '@/scripts/features/company/settings/components/AiConfigurationForm.vue'
import { useInstallationFeedback } from '../use-installation-feedback'
const router = useRouter()
const { isSuccessfulResponse, showRequestError, showResponseError } = useInstallationFeedback()
const isSaving = ref(false)
const isFetchingInitialData = ref(false)
const configData = ref<AiConfig | null>(null)
const drivers = ref<AiDriverOption[]>([])
onMounted(loadData)
async function loadData(): Promise<void> {
isFetchingInitialData.value = true
try {
const { data } = await installClient.get<{
config: AiConfig
drivers: AiDriversResponse['ai_drivers']
}>('/api/v1/installation/ai/config')
configData.value = data.config
drivers.value = data.drivers
} catch (error: unknown) {
showRequestError(error)
} finally {
isFetchingInitialData.value = false
}
}
async function saveAi(value: AiConfig): Promise<void> {
isSaving.value = true
try {
const { data } = await installClient.post('/api/v1/installation/ai/config', value)
if (!isSuccessfulResponse(data)) {
showResponseError(data)
return
}
await router.push({ name: 'installation.account' })
} catch (error: unknown) {
showRequestError(error)
} finally {
isSaving.value = false
}
}
async function skipStep(): Promise<void> {
// Persist the disabled default so bootstrap sees an explicit ai_enabled=NO
// (rather than a missing key that defaults to NO anyway — we want the value
// in storage so tests / repeated installer runs behave predictably).
await saveAi({
ai_enabled: 'NO',
ai_driver: 'openrouter',
ai_api_key: '',
ai_base_url: '',
ai_chat_enabled: 'NO',
ai_chat_model: '',
ai_text_generation_enabled: 'NO',
ai_text_generation_model: '',
})
}
</script>
<template>
<BaseWizardStep
:title="$t('settings.ai.installer_title')"
:description="$t('settings.ai.installer_description')"
>
<div v-if="configData">
<AiConfigurationForm
:config-data="configData"
:drivers="drivers"
:is-saving="isSaving"
:is-fetching-initial-data="isFetchingInitialData"
@submit-data="saveAi"
/>
<div class="mt-6">
<BaseButton
variant="primary-outline"
type="button"
:disabled="isSaving"
@click="skipStep"
>
{{ $t('general.skip') }}
</BaseButton>
</div>
</div>
</BaseWizardStep>
</template>
@@ -51,7 +51,7 @@ async function saveMailConfig(value: MailConfig): Promise<void> {
...value,
}
await router.push({ name: 'installation.ai' })
await router.push({ name: 'installation.account' })
} catch (error: unknown) {
showRequestError(error)
} finally {