mirror of
https://github.com/n8n-io/n8n.git
synced 2026-08-29 01:39:24 +08:00
feat(core): Enable 'instance-ai' module by default and remove startup warning (#35670)
This commit is contained in:
@@ -30,7 +30,16 @@ describe('getModuleEntryUrl', () => {
|
||||
describe('eligibleModules', () => {
|
||||
it('should not include opt-in modules by default', () => {
|
||||
const eligible = Container.get(ModuleRegistry).eligibleModules;
|
||||
expect(eligible).not.toContain('instance-ai');
|
||||
expect(eligible).not.toContain('agents');
|
||||
});
|
||||
|
||||
it('should include instance-ai by default', () => {
|
||||
expect(Container.get(ModuleRegistry).eligibleModules).toContain('instance-ai');
|
||||
});
|
||||
|
||||
it('should allow opting out of a default module via env var', () => {
|
||||
process.env.N8N_DISABLED_MODULES = 'instance-ai';
|
||||
expect(Container.get(ModuleRegistry).eligibleModules).not.toContain('instance-ai');
|
||||
});
|
||||
|
||||
it('should consider a module ineligible if it was disabled via env var', () => {
|
||||
@@ -64,11 +73,12 @@ describe('eligibleModules', () => {
|
||||
'runtime-credentials',
|
||||
'mcp-registry',
|
||||
'workflow-reviews',
|
||||
'instance-ai',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should consider a module eligible if it was enabled via env var', () => {
|
||||
process.env.N8N_ENABLED_MODULES = 'instance-ai';
|
||||
process.env.N8N_ENABLED_MODULES = 'agents';
|
||||
expect(Container.get(ModuleRegistry).eligibleModules).toEqual([
|
||||
'insights',
|
||||
'external-secrets',
|
||||
@@ -100,6 +110,7 @@ describe('eligibleModules', () => {
|
||||
'mcp-registry',
|
||||
'workflow-reviews',
|
||||
'instance-ai',
|
||||
'agents',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ export class ModuleRegistry {
|
||||
'runtime-credentials',
|
||||
'mcp-registry',
|
||||
'workflow-reviews',
|
||||
'instance-ai',
|
||||
];
|
||||
|
||||
private readonly activeModules: string[] = [];
|
||||
|
||||
@@ -3,19 +3,9 @@ import type { ModuleInterface } from '@n8n/decorators';
|
||||
import { BackendModule, OnShutdown } from '@n8n/decorators';
|
||||
import { Container } from '@n8n/di';
|
||||
|
||||
const YELLOW = '\x1b[33m';
|
||||
const CLEAR = '\x1b[0m';
|
||||
const WARNING_MESSAGE =
|
||||
"[Instance AI] 'instance-ai' module is experimental, undocumented and subject to change. " +
|
||||
'Before its official release any features may become inaccessible at any point, ' +
|
||||
'and using the module could compromise the stability of your system. Use at your own risk!';
|
||||
|
||||
@BackendModule({ name: 'instance-ai', instanceTypes: ['main'] })
|
||||
export class InstanceAiModule implements ModuleInterface {
|
||||
async init() {
|
||||
const logger = Container.get(Logger).scoped('instance-ai');
|
||||
logger.warn(`${YELLOW}${WARNING_MESSAGE}${CLEAR}`);
|
||||
|
||||
const { InstanceCredentialBroker } = await import(
|
||||
'@/credentials/instance-credential-broker.js'
|
||||
);
|
||||
@@ -48,6 +38,7 @@ export class InstanceAiModule implements ModuleInterface {
|
||||
if (Container.get(GlobalConfig).instanceAi.durableLog) {
|
||||
const { InterruptedRunSweeper } = await import('./event-bus/interrupted-run-sweeper.js');
|
||||
const { InstanceAiService } = await import('./instance-ai.service.js');
|
||||
const logger = Container.get(Logger).scoped('instance-ai');
|
||||
const sweeper = Container.get(InterruptedRunSweeper);
|
||||
sweeper.setResumeHost(Container.get(InstanceAiService));
|
||||
void sweeper.sweep().catch((error: unknown) => {
|
||||
|
||||
@@ -239,7 +239,19 @@ export class n8nPage {
|
||||
this.clipboard = new ClipboardHelper(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the workflow overview. Goes there directly rather than via `/`,
|
||||
* because the root route lands users on the AI Assistant when the `instance-ai`
|
||||
* module is active. Use {@link goToRoot} to exercise that root routing itself.
|
||||
*/
|
||||
async goHome() {
|
||||
await this.page.goto('/home/workflows');
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the app root and let it decide where the user lands.
|
||||
*/
|
||||
async goToRoot() {
|
||||
await this.page.goto('/');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,19 @@ test.describe(
|
||||
annotation: [{ type: 'owner', description: 'Identity & Access' }],
|
||||
},
|
||||
() => {
|
||||
// Every signed-in role holds `instanceAi:message`, so the root route lands
|
||||
// them on the AI Assistant while the `instance-ai` module is active.
|
||||
const testCases = [
|
||||
{ role: 'default', expectedUrl: /\/workflow/, auth: '' },
|
||||
{ role: 'owner', expectedUrl: /\/workflow/, auth: '@auth:owner' },
|
||||
{ role: 'admin', expectedUrl: /\/workflow/, auth: '@auth:admin' },
|
||||
{ role: 'member', expectedUrl: /\/workflow/, auth: '@auth:member' },
|
||||
{ role: 'default', expectedUrl: /\/assistant/, auth: '' },
|
||||
{ role: 'owner', expectedUrl: /\/assistant/, auth: '@auth:owner' },
|
||||
{ role: 'admin', expectedUrl: /\/assistant/, auth: '@auth:admin' },
|
||||
{ role: 'member', expectedUrl: /\/assistant/, auth: '@auth:member' },
|
||||
{ role: 'none', expectedUrl: /\/signin/, auth: '@auth:none' },
|
||||
];
|
||||
|
||||
for (const { role, expectedUrl, auth } of testCases) {
|
||||
test(`${role} authentication ${auth}`, async ({ n8n }) => {
|
||||
await n8n.goHome();
|
||||
await n8n.goToRoot();
|
||||
await expect(n8n.page).toHaveURL(expectedUrl);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ test.describe(
|
||||
},
|
||||
() => {
|
||||
test('should login and logout @auth:none', async ({ n8n }) => {
|
||||
await n8n.goHome();
|
||||
await n8n.goToRoot();
|
||||
|
||||
await n8n.signIn.goto();
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ test.describe(
|
||||
},
|
||||
});
|
||||
|
||||
await n8n.goHome();
|
||||
await n8n.page.waitForURL('/home/chat'); // home is chat UI for chat users
|
||||
await n8n.goToRoot();
|
||||
await n8n.page.waitForURL('/home/chat'); // root is the chat UI for chat users
|
||||
|
||||
// Verify global credential is available and pre-selected
|
||||
await expect(n8n.chatHubChat.getModelSelectorButton()).toContainText(/claude/i); // pre-selected
|
||||
|
||||
Reference in New Issue
Block a user