From c2eed441502cfdf733f85c6c074a7662c0f5817e Mon Sep 17 00:00:00 2001 From: perf3ct Date: Sat, 7 Jun 2025 20:15:11 +0000 Subject: [PATCH 01/14] fix(llm): make sure that we're referencing the correct Note ID when saving --- .../src/widgets/type_widgets/ai_chat.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/ai_chat.ts b/apps/client/src/widgets/type_widgets/ai_chat.ts index f733b499b..7f015d334 100644 --- a/apps/client/src/widgets/type_widgets/ai_chat.ts +++ b/apps/client/src/widgets/type_widgets/ai_chat.ts @@ -182,17 +182,30 @@ export default class AiChatTypeWidget extends TypeWidget { // Save chat data to the note async saveData(data: any) { - if (!this.note) { + // If we have a noteId in the data, that's the AI Chat note we should save to + // This happens when the chat panel is saving its conversation + const targetNoteId = data.noteId; + + // If no noteId in data, use the current note (for new chats) + const noteIdToUse = targetNoteId || this.note?.noteId; + + if (!noteIdToUse) { + console.warn("Cannot save AI Chat data: no note ID available"); return; } try { - console.log(`AiChatTypeWidget: Saving data for note ${this.note.noteId}`); + console.log(`AiChatTypeWidget: Saving data for note ${noteIdToUse} (current note: ${this.note?.noteId}, data.noteId: ${data.noteId})`); + + // Safety check: if we have both IDs and they don't match, warn about it + if (targetNoteId && this.note?.noteId && targetNoteId !== this.note.noteId) { + console.warn(`Note ID mismatch: saving to ${targetNoteId} but current note is ${this.note.noteId}`); + } // Format the data properly - this is the canonical format of the data const formattedData = { messages: data.messages || [], - noteId: this.note.noteId, // Always use the note's own ID + noteId: noteIdToUse, // Always preserve the correct note ID toolSteps: data.toolSteps || [], sources: data.sources || [], metadata: { @@ -201,8 +214,8 @@ export default class AiChatTypeWidget extends TypeWidget { } }; - // Save the data to the note - await server.put(`notes/${this.note.noteId}/data`, { + // Save the data to the correct note + await server.put(`notes/${noteIdToUse}/data`, { content: JSON.stringify(formattedData, null, 2) }); } catch (e) { From b6b88dff8607555b2bfc2db2e3c09b938e598290 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Sat, 7 Jun 2025 21:13:02 +0000 Subject: [PATCH 02/14] fix(server): increment SYNC_VERSION and APP_DB_VERSION for LLM embeddings removal --- apps/server/src/services/app_info.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/services/app_info.ts b/apps/server/src/services/app_info.ts index 7f70f8bcd..b3da2ac0e 100644 --- a/apps/server/src/services/app_info.ts +++ b/apps/server/src/services/app_info.ts @@ -3,8 +3,8 @@ import build from "./build.js"; import packageJson from "../../package.json" with { type: "json" }; import dataDir from "./data_dir.js"; -const APP_DB_VERSION = 231; -const SYNC_VERSION = 35; +const APP_DB_VERSION = 232; +const SYNC_VERSION = 36; const CLIPPER_PROTOCOL_VERSION = "1.0"; export default { From 414781936b1f37bf17a06b44f97b90bbc030e167 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Sat, 7 Jun 2025 23:36:53 +0000 Subject: [PATCH 03/14] fix(llm): always fetch the user's selected model --- .../pipeline/stages/model_selection_stage.ts | 113 +----------------- .../src/services/llm/providers/providers.ts | 22 +++- 2 files changed, 23 insertions(+), 112 deletions(-) diff --git a/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts b/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts index b51e59658..7b1276b91 100644 --- a/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts +++ b/apps/server/src/services/llm/pipeline/stages/model_selection_stage.ts @@ -111,19 +111,13 @@ export class ModelSelectionStage extends BasePipelineStage { - try { - // Use the same logic as the main process method - const { getValidModelConfig, getSelectedProvider } = await import('../../config/configuration_helpers.js'); - const selectedProvider = await getSelectedProvider(); - if (!selectedProvider) { - throw new Error('No AI provider is selected. Please select a provider in your AI settings.'); - } - - // Check if the provider is available through the service manager - if (!aiServiceManager.isProviderAvailable(selectedProvider)) { - throw new Error(`Selected provider ${selectedProvider} is not available`); - } - - // Try to get a valid model config - const modelConfig = await getValidModelConfig(selectedProvider); - - if (!modelConfig) { - throw new Error(`No default model configured for provider ${selectedProvider}. Please configure a default model in your AI settings.`); - } - - // Set provider metadata - if (!input.options.providerMetadata) { - input.options.providerMetadata = { - provider: selectedProvider as 'openai' | 'anthropic' | 'ollama' | 'local', - modelId: modelConfig.model - }; - } - - log.info(`Selected default model ${modelConfig.model} from provider ${selectedProvider}`); - return modelConfig.model; - } catch (error) { - log.error(`Error determining default model: ${error}`); - throw error; // Don't provide fallback defaults, let the error propagate - } - } /** * Get estimated context window for Ollama models @@ -283,48 +225,5 @@ export class ModelSelectionStage extends BasePipelineStage { - try { - log.info(`Getting default model for provider ${provider} using AI service manager`); - - // Use the existing AI service manager instead of duplicating API calls - const service = await aiServiceManager.getInstance().getService(provider); - - if (!service || !service.isAvailable()) { - log.info(`Provider ${provider} service is not available`); - return null; - } - // Check if the service has a method to get available models - if (typeof (service as any).getAvailableModels === 'function') { - try { - const models = await (service as any).getAvailableModels(); - if (models && models.length > 0) { - // Use the first available model - no hardcoded preferences - const selectedModel = models[0]; - - // Import server-side options to update the default model - const optionService = (await import('../../../options.js')).default; - const optionKey = `${provider}DefaultModel` as const; - - await optionService.setOption(optionKey, selectedModel); - log.info(`Set default ${provider} model to: ${selectedModel}`); - return selectedModel; - } - } catch (modelError) { - log.error(`Error fetching models from ${provider} service: ${modelError}`); - } - } - - log.info(`Provider ${provider} does not support dynamic model fetching`); - return null; - } catch (error) { - log.error(`Error getting default model for provider ${provider}: ${error}`); - return null; - } - } } diff --git a/apps/server/src/services/llm/providers/providers.ts b/apps/server/src/services/llm/providers/providers.ts index 8575fa6aa..5416d9366 100644 --- a/apps/server/src/services/llm/providers/providers.ts +++ b/apps/server/src/services/llm/providers/providers.ts @@ -26,7 +26,11 @@ export function getOpenAIOptions( } const baseUrl = options.getOption('openaiBaseUrl') || PROVIDER_CONSTANTS.OPENAI.BASE_URL; - const modelName = opts.model || options.getOption('openaiDefaultModel') || PROVIDER_CONSTANTS.OPENAI.DEFAULT_MODEL; + const modelName = opts.model || options.getOption('openaiDefaultModel'); + + if (!modelName) { + throw new Error('No OpenAI model configured. Please set a default model in your AI settings.'); + } // Create provider metadata const providerMetadata: ModelMetadata = { @@ -87,7 +91,11 @@ export function getAnthropicOptions( } const baseUrl = options.getOption('anthropicBaseUrl') || PROVIDER_CONSTANTS.ANTHROPIC.BASE_URL; - const modelName = opts.model || options.getOption('anthropicDefaultModel') || PROVIDER_CONSTANTS.ANTHROPIC.DEFAULT_MODEL; + const modelName = opts.model || options.getOption('anthropicDefaultModel'); + + if (!modelName) { + throw new Error('No Anthropic model configured. Please set a default model in your AI settings.'); + } // Create provider metadata const providerMetadata: ModelMetadata = { @@ -150,8 +158,12 @@ export async function getOllamaOptions( throw new Error('Ollama API URL is not configured'); } - // Get the model name - no prefix handling needed now - let modelName = opts.model || options.getOption('ollamaDefaultModel') || 'llama3'; + // Get the model name - no defaults, must be configured by user + let modelName = opts.model || options.getOption('ollamaDefaultModel'); + + if (!modelName) { + throw new Error('No Ollama model configured. Please set a default model in your AI settings.'); + } // Create provider metadata const providerMetadata: ModelMetadata = { @@ -249,4 +261,4 @@ async function getOllamaModelContextWindow(modelName: string): Promise { log.info(`Error getting context window for model ${modelName}: ${error}`); return MODEL_CAPABILITIES['default'].contextWindowTokens; // Default fallback } -} \ No newline at end of file +} From c6062f453a289060513b799dd9e5bd20e7ca9ede Mon Sep 17 00:00:00 2001 From: perf3ct Date: Sat, 7 Jun 2025 23:57:35 +0000 Subject: [PATCH 04/14] fix(llm): changing providers works now --- .../options/ai_settings/ai_settings_widget.ts | 2 +- .../src/services/llm/ai_service_manager.ts | 239 ++++++++---------- .../llm/config/configuration_helpers.ts | 133 ++++++++-- .../llm/config/configuration_manager.ts | 22 +- apps/server/src/services/options.ts | 20 ++ 5 files changed, 244 insertions(+), 172 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts index 583f24f9c..3359f0c37 100644 --- a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts +++ b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts @@ -48,7 +48,7 @@ export default class AiSettingsWidget extends OptionsWidget { if (optionName === 'aiEnabled') { try { const isEnabled = value === 'true'; - + if (isEnabled) { toastService.showMessage(t("ai_llm.ai_enabled") || "AI features enabled"); } else { diff --git a/apps/server/src/services/llm/ai_service_manager.ts b/apps/server/src/services/llm/ai_service_manager.ts index cbb0a8755..bd47b4327 100644 --- a/apps/server/src/services/llm/ai_service_manager.ts +++ b/apps/server/src/services/llm/ai_service_manager.ts @@ -40,8 +40,8 @@ interface NoteContext { } export class AIServiceManager implements IAIServiceManager { - private services: Partial> = {}; - + private currentService: AIService | null = null; + private currentProvider: ServiceProviders | null = null; private initialized = false; constructor() { @@ -50,9 +50,8 @@ export class AIServiceManager implements IAIServiceManager { log.error(`Error initializing LLM tools during AIServiceManager construction: ${error.message || String(error)}`); }); - // Set up event listener for provider changes - this.setupProviderChangeListener(); - + // Removed complex provider change listener - we'll read options fresh each time + this.initialized = true; } @@ -140,15 +139,15 @@ export class AIServiceManager implements IAIServiceManager { */ async getOrCreateAnyService(): Promise { this.ensureInitialized(); - + // Get the selected provider using the new configuration system const selectedProvider = await this.getSelectedProviderAsync(); - - + + if (!selectedProvider) { throw new Error('No AI provider is selected. Please select a provider (OpenAI, Anthropic, or Ollama) in your AI settings.'); } - + try { const service = await this.getOrCreateChatProvider(selectedProvider); if (service) { @@ -166,7 +165,7 @@ export class AIServiceManager implements IAIServiceManager { */ isAnyServiceAvailable(): boolean { this.ensureInitialized(); - + // Check if we have the selected provider available return this.getAvailableProviders().length > 0; } @@ -174,43 +173,37 @@ export class AIServiceManager implements IAIServiceManager { /** * Get list of available providers */ - getAvailableProviders(): ServiceProviders[] { + getAvailableProviders(): ServiceProviders[] { this.ensureInitialized(); - + const allProviders: ServiceProviders[] = ['openai', 'anthropic', 'ollama']; const availableProviders: ServiceProviders[] = []; - + for (const providerName of allProviders) { - // Use a sync approach - check if we can create the provider - const service = this.services[providerName]; - if (service && service.isAvailable()) { - availableProviders.push(providerName); - } else { - // For providers not yet created, check configuration to see if they would be available - try { - switch (providerName) { - case 'openai': - if (options.getOption('openaiApiKey')) { - availableProviders.push(providerName); - } - break; - case 'anthropic': - if (options.getOption('anthropicApiKey')) { - availableProviders.push(providerName); - } - break; - case 'ollama': - if (options.getOption('ollamaBaseUrl')) { - availableProviders.push(providerName); - } - break; - } - } catch (error) { - // Ignore configuration errors, provider just won't be available + // Check configuration to see if provider would be available + try { + switch (providerName) { + case 'openai': + if (options.getOption('openaiApiKey') || options.getOption('openaiBaseUrl')) { + availableProviders.push(providerName); + } + break; + case 'anthropic': + if (options.getOption('anthropicApiKey')) { + availableProviders.push(providerName); + } + break; + case 'ollama': + if (options.getOption('ollamaBaseUrl')) { + availableProviders.push(providerName); + } + break; } + } catch (error) { + // Ignore configuration errors, provider just won't be available } } - + return availableProviders; } @@ -234,11 +227,11 @@ export class AIServiceManager implements IAIServiceManager { // Get the selected provider const selectedProvider = await this.getSelectedProviderAsync(); - + if (!selectedProvider) { throw new Error('No AI provider is selected. Please select a provider in your AI settings.'); } - + // Check if the selected provider is available const availableProviders = this.getAvailableProviders(); if (!availableProviders.includes(selectedProvider)) { @@ -379,47 +372,68 @@ export class AIServiceManager implements IAIServiceManager { } /** - * Get or create a chat provider on-demand with inline validation + * Clear the current provider (forces recreation on next access) + */ + public clearCurrentProvider(): void { + this.currentService = null; + this.currentProvider = null; + log.info('Cleared current provider - will be recreated on next access'); + } + + /** + * Get or create the current provider instance - only one instance total */ private async getOrCreateChatProvider(providerName: ServiceProviders): Promise { - // Return existing provider if already created - if (this.services[providerName]) { - return this.services[providerName]; + // If provider type changed, clear the old one + if (this.currentProvider && this.currentProvider !== providerName) { + log.info(`Provider changed from ${this.currentProvider} to ${providerName}, clearing old service`); + this.currentService = null; + this.currentProvider = null; } - // Create and validate provider on-demand + // Return existing service if it matches and is available + if (this.currentService && this.currentProvider === providerName && this.currentService.isAvailable()) { + return this.currentService; + } + + // Clear invalid service + if (this.currentService) { + this.currentService = null; + this.currentProvider = null; + } + + // Create new service for the requested provider try { let service: AIService | null = null; - + switch (providerName) { case 'openai': { const apiKey = options.getOption('openaiApiKey'); const baseUrl = options.getOption('openaiBaseUrl'); if (!apiKey && !baseUrl) return null; - + service = new OpenAIService(); - // Validate by checking if it's available if (!service.isAvailable()) { throw new Error('OpenAI service not available'); } break; } - + case 'anthropic': { const apiKey = options.getOption('anthropicApiKey'); if (!apiKey) return null; - + service = new AnthropicService(); if (!service.isAvailable()) { throw new Error('Anthropic service not available'); } break; } - + case 'ollama': { const baseUrl = options.getOption('ollamaBaseUrl'); if (!baseUrl) return null; - + service = new OllamaService(); if (!service.isAvailable()) { throw new Error('Ollama service not available'); @@ -427,9 +441,12 @@ export class AIServiceManager implements IAIServiceManager { break; } } - + if (service) { - this.services[providerName] = service; + // Cache the new service + this.currentService = service; + this.currentProvider = providerName; + log.info(`Created and cached new ${providerName} service`); return service; } } catch (error: any) { @@ -630,28 +647,47 @@ export class AIServiceManager implements IAIServiceManager { * Check if a specific provider is available */ isProviderAvailable(provider: string): boolean { - return this.services[provider as ServiceProviders]?.isAvailable() ?? false; + // Check if this is the current provider and if it's available + if (this.currentProvider === provider && this.currentService) { + return this.currentService.isAvailable(); + } + + // For other providers, check configuration + try { + switch (provider) { + case 'openai': + return !!(options.getOption('openaiApiKey') || options.getOption('openaiBaseUrl')); + case 'anthropic': + return !!options.getOption('anthropicApiKey'); + case 'ollama': + return !!options.getOption('ollamaBaseUrl'); + default: + return false; + } + } catch { + return false; + } } /** * Get metadata about a provider */ getProviderMetadata(provider: string): ProviderMetadata | null { - const service = this.services[provider as ServiceProviders]; - if (!service) { - return null; + // Only return metadata if this is the current active provider + if (this.currentProvider === provider && this.currentService) { + return { + name: provider, + capabilities: { + chat: true, + streaming: true, + functionCalling: provider === 'openai' // Only OpenAI has function calling + }, + models: ['default'], // Placeholder, could be populated from the service + defaultModel: 'default' + }; } - return { - name: provider, - capabilities: { - chat: true, - streaming: true, - functionCalling: provider === 'openai' // Only OpenAI has function calling - }, - models: ['default'], // Placeholder, could be populated from the service - defaultModel: 'default' - }; + return null; } @@ -665,67 +701,8 @@ export class AIServiceManager implements IAIServiceManager { return String(error); } - /** - * Set up event listener for provider changes - */ - private setupProviderChangeListener(): void { - // List of AI-related options that should trigger service recreation - const aiRelatedOptions = [ - 'aiEnabled', - 'aiSelectedProvider', - 'openaiApiKey', - 'openaiBaseUrl', - 'openaiDefaultModel', - 'anthropicApiKey', - 'anthropicBaseUrl', - 'anthropicDefaultModel', - 'ollamaBaseUrl', - 'ollamaDefaultModel' - ]; - - eventService.subscribe(['entityChanged'], async ({ entityName, entity }) => { - if (entityName === 'options' && entity && aiRelatedOptions.includes(entity.name)) { - log.info(`AI-related option '${entity.name}' changed, recreating LLM services`); - - // Special handling for aiEnabled toggle - if (entity.name === 'aiEnabled') { - const isEnabled = entity.value === 'true'; - - if (isEnabled) { - log.info('AI features enabled, initializing AI service'); - // Initialize the AI service - await this.initialize(); - } else { - log.info('AI features disabled, clearing providers'); - // Clear chat providers - this.services = {}; - } - } else { - // For other AI-related options, recreate services on-demand - await this.recreateServices(); - } - } - }); - } - - /** - * Recreate LLM services when provider settings change - */ - private async recreateServices(): Promise { - try { - log.info('Recreating LLM services due to configuration change'); - - // Clear configuration cache first - clearConfigurationCache(); - - // Clear existing chat providers (they will be recreated on-demand) - this.services = {}; - - log.info('LLM services recreated successfully'); - } catch (error) { - log.error(`Error recreating LLM services: ${this.handleError(error)}`); - } - } + // Removed complex event listener and cache invalidation logic + // Services will be created fresh when needed by reading current options } diff --git a/apps/server/src/services/llm/config/configuration_helpers.ts b/apps/server/src/services/llm/config/configuration_helpers.ts index d48504bd2..9f92164a2 100644 --- a/apps/server/src/services/llm/config/configuration_helpers.ts +++ b/apps/server/src/services/llm/config/configuration_helpers.ts @@ -1,4 +1,3 @@ -import configurationManager from './configuration_manager.js'; import optionService from '../../options.js'; import log from '../../log.js'; import type { @@ -13,7 +12,7 @@ import type { */ /** - * Get the selected AI provider + * Get the selected AI provider - always fresh from options */ export async function getSelectedProvider(): Promise { const providerOption = optionService.getOption('aiSelectedProvider'); @@ -25,38 +24,100 @@ export async function getSelectedProvider(): Promise { * Parse a model identifier (handles "provider:model" format) */ export function parseModelIdentifier(modelString: string): ModelIdentifier { - return configurationManager.parseModelIdentifier(modelString); + if (!modelString) { + return { + modelId: '', + fullIdentifier: '' + }; + } + + const parts = modelString.split(':'); + + if (parts.length === 1) { + // No provider prefix, just model name + return { + modelId: modelString, + fullIdentifier: modelString + }; + } + + // Check if first part is a known provider + const potentialProvider = parts[0].toLowerCase(); + const knownProviders: ProviderType[] = ['openai', 'anthropic', 'ollama']; + + if (knownProviders.includes(potentialProvider as ProviderType)) { + // Provider prefix format + const provider = potentialProvider as ProviderType; + const modelId = parts.slice(1).join(':'); // Rejoin in case model has colons + + return { + provider, + modelId, + fullIdentifier: modelString + }; + } + + // Not a provider prefix, treat whole string as model name + return { + modelId: modelString, + fullIdentifier: modelString + }; } /** * Create a model configuration from a model string */ export function createModelConfig(modelString: string, defaultProvider?: ProviderType): ModelConfig { - return configurationManager.createModelConfig(modelString, defaultProvider); + const identifier = parseModelIdentifier(modelString); + const provider = identifier.provider || defaultProvider || 'openai'; // fallback to openai if no provider specified + + return { + provider, + modelId: identifier.modelId, + displayName: identifier.fullIdentifier + }; } /** - * Get the default model for a specific provider + * Get the default model for a specific provider - always fresh from options */ export async function getDefaultModelForProvider(provider: ProviderType): Promise { - const config = await configurationManager.getAIConfig(); - return config.defaultModels[provider]; // This can now be undefined + const optionKey = `${provider}DefaultModel` as const; + return optionService.getOption(optionKey) || undefined; } /** - * Get provider settings for a specific provider + * Get provider settings for a specific provider - always fresh from options */ export async function getProviderSettings(provider: ProviderType) { - const config = await configurationManager.getAIConfig(); - return config.providerSettings[provider]; + switch (provider) { + case 'openai': + return { + apiKey: optionService.getOption('openaiApiKey'), + baseUrl: optionService.getOption('openaiBaseUrl'), + defaultModel: optionService.getOption('openaiDefaultModel') + }; + case 'anthropic': + return { + apiKey: optionService.getOption('anthropicApiKey'), + baseUrl: optionService.getOption('anthropicBaseUrl'), + defaultModel: optionService.getOption('anthropicDefaultModel') + }; + case 'ollama': + return { + baseUrl: optionService.getOption('ollamaBaseUrl'), + defaultModel: optionService.getOption('ollamaDefaultModel') + }; + default: + return {}; + } } /** - * Check if AI is enabled + * Check if AI is enabled - always fresh from options */ export async function isAIEnabled(): Promise { - const config = await configurationManager.getAIConfig(); - return config.enabled; + return optionService.getOptionBool('aiEnabled'); } /** @@ -82,7 +143,7 @@ export async function isProviderConfigured(provider: ProviderType): Promise { const selectedProvider = await getSelectedProvider(); - + if (!selectedProvider) { return null; // No provider selected } @@ -95,17 +156,51 @@ export async function getAvailableSelectedProvider(): Promise { const selectedProvider = await getSelectedProvider(); - + if (!selectedProvider) { return null; // No provider selected } diff --git a/apps/server/src/services/llm/config/configuration_manager.ts b/apps/server/src/services/llm/config/configuration_manager.ts index 8dfd8df05..6b879eed6 100644 --- a/apps/server/src/services/llm/config/configuration_manager.ts +++ b/apps/server/src/services/llm/config/configuration_manager.ts @@ -21,11 +21,6 @@ import type { */ export class ConfigurationManager { private static instance: ConfigurationManager | null = null; - private cachedConfig: AIConfig | null = null; - private lastConfigUpdate: number = 0; - - // Cache for 5 minutes to avoid excessive option reads - private static readonly CACHE_DURATION = 5 * 60 * 1000; private constructor() {} @@ -37,14 +32,9 @@ export class ConfigurationManager { } /** - * Get the complete AI configuration + * Get the complete AI configuration - always fresh, no caching */ public async getAIConfig(): Promise { - const now = Date.now(); - if (this.cachedConfig && (now - this.lastConfigUpdate) < ConfigurationManager.CACHE_DURATION) { - return this.cachedConfig; - } - try { const config: AIConfig = { enabled: await this.getAIEnabled(), @@ -53,8 +43,6 @@ export class ConfigurationManager { providerSettings: await this.getProviderSettings() }; - this.cachedConfig = config; - this.lastConfigUpdate = now; return config; } catch (error) { log.error(`Error loading AI configuration: ${error}`); @@ -263,14 +251,6 @@ export class ConfigurationManager { return result; } - /** - * Clear cached configuration (force reload on next access) - */ - public clearCache(): void { - this.cachedConfig = null; - this.lastConfigUpdate = 0; - } - // Private helper methods private async getAIEnabled(): Promise { diff --git a/apps/server/src/services/options.ts b/apps/server/src/services/options.ts index f6e575c19..1cc67df5a 100644 --- a/apps/server/src/services/options.ts +++ b/apps/server/src/services/options.ts @@ -82,6 +82,26 @@ function setOption(name: T, value: string | OptionDefinit } else { createOption(name, value, false); } + + // Clear current AI provider when AI-related options change + const aiOptions = [ + 'aiSelectedProvider', 'openaiApiKey', 'openaiBaseUrl', 'openaiDefaultModel', + 'anthropicApiKey', 'anthropicBaseUrl', 'anthropicDefaultModel', + 'ollamaBaseUrl', 'ollamaDefaultModel' + ]; + + if (aiOptions.includes(name)) { + // Import dynamically to avoid circular dependencies + setImmediate(async () => { + try { + const aiServiceManager = (await import('./llm/ai_service_manager.js')).default; + aiServiceManager.getInstance().clearCurrentProvider(); + console.log(`Cleared AI provider after ${name} option changed`); + } catch (error) { + console.log(`Could not clear AI provider: ${error}`); + } + }); + } } /** From cc089c245eadc4380a0e8a6d312cdc125d608e0f Mon Sep 17 00:00:00 2001 From: perf3ct Date: Sun, 8 Jun 2025 00:02:40 +0000 Subject: [PATCH 05/14] fix(llm): save the chat to a specific note, not the currently active note --- .../src/widgets/llm_chat/llm_chat_panel.ts | 136 ++++++++++++++++-- 1 file changed, 124 insertions(+), 12 deletions(-) diff --git a/apps/client/src/widgets/llm_chat/llm_chat_panel.ts b/apps/client/src/widgets/llm_chat/llm_chat_panel.ts index be401031f..3187018cf 100644 --- a/apps/client/src/widgets/llm_chat/llm_chat_panel.ts +++ b/apps/client/src/widgets/llm_chat/llm_chat_panel.ts @@ -350,6 +350,115 @@ export default class LlmChatPanel extends BasicWidget { } } + /** + * Save current chat data to a specific note ID + */ + async saveCurrentDataToSpecificNote(targetNoteId: string | null) { + if (!this.onSaveData || !targetNoteId) { + console.warn('Cannot save chat data: no saveData callback or no targetNoteId available'); + return; + } + + try { + // Extract current tool execution steps if any exist + const toolSteps = extractInChatToolSteps(this.noteContextChatMessages); + + // Get tool executions from both UI and any cached executions in metadata + let toolExecutions: Array<{ + id: string; + name: string; + arguments: any; + result: any; + error?: string; + timestamp: string; + }> = []; + + // First include any tool executions already in metadata (from streaming events) + if (this.metadata?.toolExecutions && Array.isArray(this.metadata.toolExecutions)) { + toolExecutions = [...this.metadata.toolExecutions]; + console.log(`Including ${toolExecutions.length} tool executions from metadata`); + } + + // Also extract any visible tool steps from the UI + const extractedExecutions = toolSteps.map(step => { + // Parse tool execution information + if (step.type === 'tool-execution') { + try { + const content = JSON.parse(step.content); + return { + id: content.toolCallId || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, + name: content.tool || 'unknown', + arguments: content.args || {}, + result: content.result || {}, + error: content.error, + timestamp: new Date().toISOString() + }; + } catch (e) { + // If we can't parse it, create a basic record + return { + id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, + name: 'unknown', + arguments: {}, + result: step.content, + timestamp: new Date().toISOString() + }; + } + } else if (step.type === 'result' && step.name) { + // Handle result steps with a name + return { + id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, + name: step.name, + arguments: {}, + result: step.content, + timestamp: new Date().toISOString() + }; + } + return { + id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, + name: 'unknown', + arguments: {}, + result: 'Unrecognized tool step', + timestamp: new Date().toISOString() + }; + }); + + // Merge the tool executions, keeping only unique IDs + const existingIds = new Set(toolExecutions.map((t: {id: string}) => t.id)); + for (const exec of extractedExecutions) { + if (!existingIds.has(exec.id)) { + toolExecutions.push(exec); + existingIds.add(exec.id); + } + } + + const dataToSave = { + messages: this.messages, + noteId: targetNoteId, + chatNoteId: targetNoteId, // For backward compatibility + toolSteps: toolSteps, + // Add sources if we have them + sources: this.sources || [], + // Add metadata + metadata: { + model: this.metadata?.model || undefined, + provider: this.metadata?.provider || undefined, + temperature: this.metadata?.temperature || 0.7, + lastUpdated: new Date().toISOString(), + // Add tool executions + toolExecutions: toolExecutions + } + }; + + console.log(`Saving chat data to specific note ${targetNoteId}, ${toolSteps.length} tool steps, ${this.sources?.length || 0} sources, ${toolExecutions.length} tool executions`); + + // Save the data to the note attribute via the callback + // This is the ONLY place we should save data, letting the container widget handle persistence + await this.onSaveData(dataToSave); + } catch (error) { + console.error('Error saving chat data to specific note:', error); + } + } + /** * Load saved chat data from the note attribute */ @@ -867,8 +976,8 @@ export default class LlmChatPanel extends BasicWidget { this.showSources(postResponse.sources); } - // Process the assistant response - this.processAssistantResponse(postResponse.content, postResponse); + // Process the assistant response with original chat note ID + this.processAssistantResponse(postResponse.content, postResponse, this.noteId); hideLoadingIndicator(this.loadingIndicator); return true; @@ -884,7 +993,7 @@ export default class LlmChatPanel extends BasicWidget { /** * Process an assistant response - add to UI and save */ - private async processAssistantResponse(content: string, fullResponse?: any) { + private async processAssistantResponse(content: string, fullResponse?: any, originalChatNoteId?: string | null) { // Add the response to the chat UI this.addMessageToChat('assistant', content); @@ -910,8 +1019,8 @@ export default class LlmChatPanel extends BasicWidget { ]; } - // Save to note - this.saveCurrentData().catch(err => { + // Save to note - use original chat note ID if provided + this.saveCurrentDataToSpecificNote(originalChatNoteId || this.noteId).catch(err => { console.error("Failed to save assistant response to note:", err); }); } @@ -936,12 +1045,15 @@ export default class LlmChatPanel extends BasicWidget { timestamp: string; }> = []; + // Store the original chat note ID to ensure we save to the correct note even if user switches + const originalChatNoteId = this.noteId; + return setupStreamingResponse( this.noteId, messageParams, // Content update handler (content: string, isDone: boolean = false) => { - this.updateStreamingUI(content, isDone); + this.updateStreamingUI(content, isDone, originalChatNoteId); // Update session data with additional metadata when streaming is complete if (isDone) { @@ -1067,13 +1179,13 @@ export default class LlmChatPanel extends BasicWidget { /** * Update the UI with streaming content */ - private updateStreamingUI(assistantResponse: string, isDone: boolean = false) { + private updateStreamingUI(assistantResponse: string, isDone: boolean = false, originalChatNoteId?: string | null) { // Track if we have a streaming message in progress const hasStreamingMessage = !!this.noteContextChatMessages.querySelector('.assistant-message.streaming'); - + // Create a new message element or use the existing streaming one let assistantMessageEl: HTMLElement; - + if (hasStreamingMessage) { // Use the existing streaming message assistantMessageEl = this.noteContextChatMessages.querySelector('.assistant-message.streaming')!; @@ -1103,7 +1215,7 @@ export default class LlmChatPanel extends BasicWidget { if (isDone) { // Remove the streaming class to mark this message as complete assistantMessageEl.classList.remove('streaming'); - + // Apply syntax highlighting formatCodeBlocks($(assistantMessageEl as HTMLElement)); @@ -1118,8 +1230,8 @@ export default class LlmChatPanel extends BasicWidget { timestamp: new Date() }); - // Save the updated message list - this.saveCurrentData(); + // Save the updated message list to the original chat note + this.saveCurrentDataToSpecificNote(originalChatNoteId || this.noteId); } // Scroll to bottom From cf9f59304c9806d797a91f108d0cf813c17567e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 01:53:26 +0000 Subject: [PATCH 06/14] chore(deps): update dependency @types/cookie-parser to v1.4.9 --- apps/server/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index 9e662763c..c24805f73 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -12,7 +12,7 @@ "@types/better-sqlite3": "7.6.13", "@types/cls-hooked": "4.3.9", "@types/compression": "1.8.0", - "@types/cookie-parser": "1.4.8", + "@types/cookie-parser": "1.4.9", "@types/debounce": "1.2.4", "@types/ejs": "3.1.5", "@types/escape-html": "1.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 077fdef78..ac33defca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -498,8 +498,8 @@ importers: specifier: 1.8.0 version: 1.8.0 '@types/cookie-parser': - specifier: 1.4.8 - version: 1.4.8(@types/express@5.0.2) + specifier: 1.4.9 + version: 1.4.9(@types/express@5.0.2) '@types/debounce': specifier: 1.2.4 version: 1.2.4 @@ -4382,8 +4382,8 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/cookie-parser@1.4.8': - resolution: {integrity: sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==} + '@types/cookie-parser@1.4.9': + resolution: {integrity: sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==} peerDependencies: '@types/express': '*' @@ -17443,7 +17443,7 @@ snapshots: dependencies: '@types/node': 22.15.30 - '@types/cookie-parser@1.4.8(@types/express@5.0.2)': + '@types/cookie-parser@1.4.9(@types/express@5.0.2)': dependencies: '@types/express': 5.0.2 From a3c655ffc57a348cd656a3396339912ff46f7881 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 01:55:20 +0000 Subject: [PATCH 07/14] chore(deps): update dependency @types/express-session to v1.18.2 --- apps/server/package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index 9e662763c..a18485382 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -17,7 +17,7 @@ "@types/ejs": "3.1.5", "@types/escape-html": "1.0.4", "@types/express-http-proxy": "1.6.6", - "@types/express-session": "1.18.1", + "@types/express-session": "1.18.2", "@types/fs-extra": "11.0.4", "@types/html": "1.0.4", "@types/ini": "4.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 077fdef78..5bb12bd6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -513,8 +513,8 @@ importers: specifier: 1.6.6 version: 1.6.6 '@types/express-session': - specifier: 1.18.1 - version: 1.18.1 + specifier: 1.18.2 + version: 1.18.2 '@types/fs-extra': specifier: 11.0.4 version: 11.0.4 @@ -4523,8 +4523,8 @@ packages: '@types/express-serve-static-core@5.0.6': resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} - '@types/express-session@1.18.1': - resolution: {integrity: sha512-S6TkD/lljxDlQ2u/4A70luD8/ZxZcrU5pQwI1rVXCiaVIywoFgbA+PIUNDjPhQpPdK0dGleLtYc/y7XWBfclBg==} + '@types/express-session@1.18.2': + resolution: {integrity: sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg==} '@types/express@4.17.22': resolution: {integrity: sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==} @@ -17612,7 +17612,7 @@ snapshots: '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - '@types/express-session@1.18.1': + '@types/express-session@1.18.2': dependencies: '@types/express': 5.0.2 @@ -17812,7 +17812,7 @@ snapshots: '@types/session-file-store@1.2.5': dependencies: '@types/express': 5.0.2 - '@types/express-session': 1.18.1 + '@types/express-session': 1.18.2 '@types/sinonjs__fake-timers@8.1.5': {} From f83d33754e291a762ce1c1eef0d6deefc4e96b7a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 01:56:48 +0000 Subject: [PATCH 08/14] chore(deps): update dependency @types/mime-types to v3.0.1 --- apps/server/package.json | 2 +- pnpm-lock.yaml | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index 9e662763c..0c72afdf8 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -23,7 +23,7 @@ "@types/ini": "4.1.1", "@types/js-yaml": "4.0.9", "@types/jsdom": "21.1.7", - "@types/mime-types": "3.0.0", + "@types/mime-types": "3.0.1", "@types/multer": "1.4.12", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.16.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 077fdef78..998cc9c48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -531,8 +531,8 @@ importers: specifier: 21.1.7 version: 21.1.7 '@types/mime-types': - specifier: 3.0.0 - version: 3.0.0 + specifier: 3.0.1 + version: 3.0.1 '@types/multer': specifier: 1.4.12 version: 1.4.12 @@ -4616,6 +4616,9 @@ packages: '@types/mime-types@3.0.0': resolution: {integrity: sha512-9gFWMsVgEtbsD6yY/2z8pAtnZhdRKl4Q9xmKQJy5gv0fMpzJeeWtQyd7WpdhaIbRSwPCfnjXOsNMcoQvu5giGg==} + '@types/mime-types@3.0.1': + resolution: {integrity: sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -17722,6 +17725,8 @@ snapshots: '@types/mime-types@3.0.0': {} + '@types/mime-types@3.0.1': {} + '@types/mime@1.3.5': {} '@types/minimatch@5.1.2': {} From 858801502d0cdce589b1d145407f72a6aa34d061 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 01:58:09 +0000 Subject: [PATCH 09/14] chore(deps): update dependency @types/serve-static to v1.15.8 --- apps/server/package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index 9e662763c..934f315e6 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -29,7 +29,7 @@ "@types/sanitize-html": "2.16.0", "@types/sax": "1.2.7", "@types/serve-favicon": "2.5.7", - "@types/serve-static": "1.15.7", + "@types/serve-static": "1.15.8", "@types/session-file-store": "1.2.5", "@types/stream-throttle": "0.1.4", "@types/supertest": "6.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 077fdef78..d06138804 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -549,8 +549,8 @@ importers: specifier: 2.5.7 version: 2.5.7 '@types/serve-static': - specifier: 1.15.7 - version: 1.15.7 + specifier: 1.15.8 + version: 1.15.8 '@types/session-file-store': specifier: 1.2.5 version: 1.2.5 @@ -4693,8 +4693,8 @@ packages: '@types/serve-index@1.9.4': resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} '@types/session-file-store@1.2.5': resolution: {integrity: sha512-xjIyh40IznXLrvbAY/nmxu5cMcPcE3ZoDrSDvd02m6p8UjUgOtZAGI7Os5DDd6THuxClLWNhFo/awy1tYp64Bg==} @@ -17621,13 +17621,13 @@ snapshots: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 5.0.6 '@types/qs': 6.14.0 - '@types/serve-static': 1.15.7 + '@types/serve-static': 1.15.8 '@types/express@5.0.2': dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 5.0.6 - '@types/serve-static': 1.15.7 + '@types/serve-static': 1.15.8 '@types/fs-extra@11.0.4': dependencies: @@ -17803,10 +17803,10 @@ snapshots: dependencies: '@types/express': 5.0.2 - '@types/serve-static@1.15.7': + '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.15.21 + '@types/node': 22.15.30 '@types/send': 0.17.4 '@types/session-file-store@1.2.5': @@ -17846,7 +17846,7 @@ snapshots: '@types/swagger-ui-express@4.1.8': dependencies: '@types/express': 5.0.2 - '@types/serve-static': 1.15.7 + '@types/serve-static': 1.15.8 '@types/tmp@0.2.6': {} @@ -27464,7 +27464,7 @@ snapshots: '@types/express': 4.17.22 '@types/express-serve-static-core': 5.0.6 '@types/serve-index': 1.9.4 - '@types/serve-static': 1.15.7 + '@types/serve-static': 1.15.8 '@types/sockjs': 0.3.36 '@types/ws': 8.18.1 ansi-html-community: 0.0.8 From 29d9c9834a05b1c32ccfb062cd238c4def45af6b Mon Sep 17 00:00:00 2001 From: perf3ct Date: Sun, 8 Jun 2025 04:44:57 +0000 Subject: [PATCH 10/14] feat(llm): add warning to the top of LLM Chat Notes and LLM settings that the feature is experimental --- apps/client/src/translations/en/translation.json | 1 + apps/client/src/widgets/llm_chat/validation.ts | 4 ++++ .../options/ai_settings/ai_settings_widget.ts | 11 +++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 5d2e03ec5..44b50445b 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1260,6 +1260,7 @@ }, "create_new_ai_chat": "Create new AI Chat", "configuration_warnings": "There are some issues with your AI configuration. Please check your settings.", + "experimental_warning": "The LLM feature is currently experimental - you have been warned.", "selected_provider": "Selected Provider", "selected_provider_description": "Choose the AI provider for chat and completion features", "select_model": "Select model...", diff --git a/apps/client/src/widgets/llm_chat/validation.ts b/apps/client/src/widgets/llm_chat/validation.ts index 60aac7003..67a3bbcdc 100644 --- a/apps/client/src/widgets/llm_chat/validation.ts +++ b/apps/client/src/widgets/llm_chat/validation.ts @@ -2,6 +2,7 @@ * Validation functions for LLM Chat */ import options from "../../services/options.js"; +import { t } from "../../services/i18n.js"; /** * Validate providers configuration @@ -37,6 +38,9 @@ export async function validateProviders(validationWarning: HTMLElement): Promise // Check for configuration issues with providers in the precedence list const configIssues: string[] = []; + // Always add experimental warning as the first item + configIssues.push(t("ai_llm.experimental_warning")); + // Check each provider in the precedence list for proper configuration for (const provider of precedenceList) { if (provider === 'openai') { diff --git a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts index 583f24f9c..e5e97d385 100644 --- a/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts +++ b/apps/client/src/widgets/type_widgets/options/ai_settings/ai_settings_widget.ts @@ -203,6 +203,11 @@ export default class AiSettingsWidget extends OptionsWidget { // Get selected provider const selectedProvider = this.$widget.find('.ai-selected-provider').val() as string; + // Start with experimental warning + const allWarnings = [ + t("ai_llm.experimental_warning") + ]; + // Check for selected provider configuration const providerWarnings: string[] = []; if (selectedProvider === 'openai') { @@ -222,10 +227,8 @@ export default class AiSettingsWidget extends OptionsWidget { } } - // Combine all warnings - const allWarnings = [ - ...providerWarnings - ]; + // Add provider warnings to all warnings + allWarnings.push(...providerWarnings); // Show or hide warnings if (allWarnings.length > 0) { From 6734dae9568c65f70bc3d8f9e27df15d4c7a60fc Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 6 Jun 2025 18:25:31 +0300 Subject: [PATCH 11/14] chore(server): add serve-nodir --- apps/server/.serve-nodir.env | 3 +++ apps/server/package.json | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 apps/server/.serve-nodir.env diff --git a/apps/server/.serve-nodir.env b/apps/server/.serve-nodir.env new file mode 100644 index 000000000..3612d5d9b --- /dev/null +++ b/apps/server/.serve-nodir.env @@ -0,0 +1,3 @@ +TRILIUM_ENV=dev +TRILIUM_RESOURCE_DIR=./apps/server/dist +TRILIUM_PUBLIC_SERVER=http://localhost:4200 \ No newline at end of file diff --git a/apps/server/package.json b/apps/server/package.json index 90bdaa43c..90059e207 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -129,6 +129,23 @@ "runBuildTargetDependencies": false } }, + "serve-nodir": { + "executor": "@nx/js:node", + "dependsOn": [ + { + "projects": [ + "client" + ], + "target": "serve" + }, + "build-without-client" + ], + "continuous": true, + "options": { + "buildTarget": "server:build-without-client:development", + "runBuildTargetDependencies": false + } + }, "edit-integration-db": { "executor": "@nx/js:node", "dependsOn": [ From 5b154d2544d8fa9c3914e961111fff83b0f93b10 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 07:31:57 +0000 Subject: [PATCH 12/14] chore(deps): update dependency @types/multer to v1.4.13 --- apps/server/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index 0c72afdf8..863837fc1 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -24,7 +24,7 @@ "@types/js-yaml": "4.0.9", "@types/jsdom": "21.1.7", "@types/mime-types": "3.0.1", - "@types/multer": "1.4.12", + "@types/multer": "1.4.13", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.16.0", "@types/sax": "1.2.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 998cc9c48..7d2b6514e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -534,8 +534,8 @@ importers: specifier: 3.0.1 version: 3.0.1 '@types/multer': - specifier: 1.4.12 - version: 1.4.12 + specifier: 1.4.13 + version: 1.4.13 '@types/safe-compare': specifier: 1.1.2 version: 1.1.2 @@ -4628,8 +4628,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/multer@1.4.12': - resolution: {integrity: sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==} + '@types/multer@1.4.13': + resolution: {integrity: sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==} '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} @@ -17733,7 +17733,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/multer@1.4.12': + '@types/multer@1.4.13': dependencies: '@types/express': 5.0.2 From 3f1007e9049f09247f4d639b173bc1f7600e9405 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 07:36:36 +0000 Subject: [PATCH 13/14] chore(deps): update dependency @types/express to v5.0.3 --- pnpm-lock.yaml | 66 ++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7c0d1028..956522f85 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,7 +77,7 @@ importers: version: link:apps/server '@types/express': specifier: ^5.0.0 - version: 5.0.2 + version: 5.0.3 '@types/node': specifier: 22.15.30 version: 22.15.30 @@ -499,7 +499,7 @@ importers: version: 1.8.0 '@types/cookie-parser': specifier: 1.4.9 - version: 1.4.9(@types/express@5.0.2) + version: 1.4.9(@types/express@5.0.3) '@types/debounce': specifier: 1.2.4 version: 1.2.4 @@ -4349,8 +4349,8 @@ packages: '@types/better-sqlite3@7.6.13': resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} '@types/bonjour@3.5.13': resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} @@ -4526,11 +4526,11 @@ packages: '@types/express-session@1.18.2': resolution: {integrity: sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg==} - '@types/express@4.17.22': - resolution: {integrity: sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==} + '@types/express@4.17.23': + resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} - '@types/express@5.0.2': - resolution: {integrity: sha512-BtjL3ZwbCQriyb0DGw+Rt12qAXPiBTPs815lsUvtt1Grk0vLRMZNMUZ741d5rjk+UQOxfDiBZ3dxpX00vSkK3g==} + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} '@types/fs-extra@11.0.4': resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} @@ -4690,6 +4690,9 @@ packages: '@types/send@0.17.4': resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + '@types/serve-favicon@2.5.7': resolution: {integrity: sha512-z9TNUQXdQ+W/OJMP1e3KOYUZ99qJS4+ZfFOIrPGImcayqKoyifbJSEFkVq1MCKBbqjMZpjPj3B5ilrQAR2+TOw==} @@ -17398,7 +17401,7 @@ snapshots: dependencies: '@types/node': 22.15.21 - '@types/body-parser@1.19.5': + '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 '@types/node': 22.15.30 @@ -17434,7 +17437,7 @@ snapshots: '@types/compression@1.8.0': dependencies: - '@types/express': 4.17.22 + '@types/express': 5.0.3 '@types/node': 22.15.21 '@types/connect-history-api-fallback@1.5.4': @@ -17446,9 +17449,9 @@ snapshots: dependencies: '@types/node': 22.15.30 - '@types/cookie-parser@1.4.9(@types/express@5.0.2)': + '@types/cookie-parser@1.4.9(@types/express@5.0.3)': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 '@types/cookie@0.6.0': optional: true @@ -17606,29 +17609,29 @@ snapshots: '@types/express-http-proxy@1.6.6': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 '@types/express-serve-static-core@5.0.6': dependencies: '@types/node': 22.15.30 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 + '@types/send': 0.17.5 '@types/express-session@1.18.2': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 - '@types/express@4.17.22': + '@types/express@4.17.23': dependencies: - '@types/body-parser': 1.19.5 + '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 5.0.6 '@types/qs': 6.14.0 '@types/serve-static': 1.15.8 - '@types/express@5.0.2': + '@types/express@5.0.3': dependencies: - '@types/body-parser': 1.19.5 + '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 5.0.6 '@types/serve-static': 1.15.8 @@ -17735,7 +17738,7 @@ snapshots: '@types/multer@1.4.12': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 '@types/node-forge@1.3.11': dependencies: @@ -17800,13 +17803,18 @@ snapshots: '@types/mime': 1.3.5 '@types/node': 22.15.30 + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.15.30 + '@types/serve-favicon@2.5.7': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 '@types/serve-index@1.9.4': dependencies: - '@types/express': 5.0.2 + '@types/express': 4.17.23 '@types/serve-static@1.15.8': dependencies: @@ -17816,7 +17824,7 @@ snapshots: '@types/session-file-store@1.2.5': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 '@types/express-session': 1.18.2 '@types/sinonjs__fake-timers@8.1.5': {} @@ -17850,7 +17858,7 @@ snapshots: '@types/swagger-ui-express@4.1.8': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 '@types/serve-static': 1.15.8 '@types/tmp@0.2.6': {} @@ -17882,7 +17890,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.15.30 + '@types/node': 22.15.29 optional: true '@typescript-eslint/eslint-plugin@8.33.1(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)': @@ -21823,7 +21831,7 @@ snapshots: transitivePeerDependencies: - supports-color - http-proxy-middleware@2.0.9(@types/express@4.17.22): + http-proxy-middleware@2.0.9(@types/express@4.17.23): dependencies: '@types/http-proxy': 1.17.16 http-proxy: 1.18.1 @@ -21831,7 +21839,7 @@ snapshots: is-plain-obj: 3.0.0 micromatch: 4.0.8 optionalDependencies: - '@types/express': 4.17.22 + '@types/express': 4.17.23 transitivePeerDependencies: - debug @@ -27466,7 +27474,7 @@ snapshots: dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 - '@types/express': 4.17.22 + '@types/express': 4.17.23 '@types/express-serve-static-core': 5.0.6 '@types/serve-index': 1.9.4 '@types/serve-static': 1.15.8 @@ -27480,7 +27488,7 @@ snapshots: connect-history-api-fallback: 2.0.0 express: 4.21.2 graceful-fs: 4.2.11 - http-proxy-middleware: 2.0.9(@types/express@4.17.22) + http-proxy-middleware: 2.0.9(@types/express@4.17.23) ipaddr.js: 2.2.0 launch-editor: 2.10.0 open: 10.1.1 From e3d0a734598d354b9b7a344ce6e8b0c6a187c3e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 07:43:23 +0000 Subject: [PATCH 14/14] chore(deps): update dependency @types/compression to v1.8.1 --- apps/server/package.json | 2 +- pnpm-lock.yaml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index afd0598c1..a30e8cf5f 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -11,7 +11,7 @@ "@types/archiver": "6.0.3", "@types/better-sqlite3": "7.6.13", "@types/cls-hooked": "4.3.9", - "@types/compression": "1.8.0", + "@types/compression": "1.8.1", "@types/cookie-parser": "1.4.9", "@types/debounce": "1.2.4", "@types/ejs": "3.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7f9a6954..39b98c498 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -495,8 +495,8 @@ importers: specifier: 4.3.9 version: 4.3.9 '@types/compression': - specifier: 1.8.0 - version: 1.8.0 + specifier: 1.8.1 + version: 1.8.1 '@types/cookie-parser': specifier: 1.4.9 version: 1.4.9(@types/express@5.0.3) @@ -4373,8 +4373,8 @@ packages: '@types/color-name@1.1.5': resolution: {integrity: sha512-j2K5UJqGTxeesj6oQuGpMgifpT5k9HprgQd8D1Y0lOFqKHl3PJu5GMeS4Y5EgjS55AE6OQxf8mPED9uaGbf4Cg==} - '@types/compression@1.8.0': - resolution: {integrity: sha512-g4vmPIwbTii9dX1HVioHbOolubEaf4re4vDxuzpKrzz9uI7uarBExi9begX0cXyIB85jXZ5X2A/v8rsHZxSAPw==} + '@types/compression@1.8.1': + resolution: {integrity: sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==} '@types/connect-history-api-fallback@1.5.4': resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} @@ -17435,10 +17435,10 @@ snapshots: '@types/color-name@1.1.5': {} - '@types/compression@1.8.0': + '@types/compression@1.8.1': dependencies: '@types/express': 5.0.3 - '@types/node': 22.15.21 + '@types/node': 22.15.30 '@types/connect-history-api-fallback@1.5.4': dependencies: @@ -17814,7 +17814,7 @@ snapshots: '@types/serve-index@1.9.4': dependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/serve-static@1.15.8': dependencies: @@ -17890,7 +17890,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.15.29 + '@types/node': 22.15.30 optional: true '@typescript-eslint/eslint-plugin@8.33.1(@typescript-eslint/parser@8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)':