mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-28 05:55:17 -07:00
refactor: make baseTimestamp optional in addItem and remove redundant calls (#16471)
This commit is contained in:
@@ -87,20 +87,17 @@ describe('aboutCommand', () => {
|
||||
|
||||
await aboutCommand.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ABOUT,
|
||||
cliVersion: 'test-version',
|
||||
osVersion: 'test-os',
|
||||
sandboxEnv: 'no sandbox',
|
||||
modelVersion: 'test-model',
|
||||
selectedAuthType: 'test-auth',
|
||||
gcpProject: 'test-gcp-project',
|
||||
ideClient: 'test-ide',
|
||||
userEmail: 'test-email@example.com',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ABOUT,
|
||||
cliVersion: 'test-version',
|
||||
osVersion: 'test-os',
|
||||
sandboxEnv: 'no sandbox',
|
||||
modelVersion: 'test-model',
|
||||
selectedAuthType: 'test-auth',
|
||||
gcpProject: 'test-gcp-project',
|
||||
ideClient: 'test-ide',
|
||||
userEmail: 'test-email@example.com',
|
||||
});
|
||||
});
|
||||
|
||||
it('should show the correct sandbox environment variable', async () => {
|
||||
@@ -115,7 +112,6 @@ describe('aboutCommand', () => {
|
||||
expect.objectContaining({
|
||||
sandboxEnv: 'gemini-sandbox',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -132,7 +128,6 @@ describe('aboutCommand', () => {
|
||||
expect.objectContaining({
|
||||
sandboxEnv: 'sandbox-exec (test-profile)',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -159,7 +154,6 @@ describe('aboutCommand', () => {
|
||||
gcpProject: 'test-gcp-project',
|
||||
ideClient: '',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ export const aboutCommand: SlashCommand = {
|
||||
userEmail,
|
||||
};
|
||||
|
||||
context.ui.addItem(aboutItem, Date.now());
|
||||
context.ui.addItem(aboutItem);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ describe('agentsCommand', () => {
|
||||
type: MessageType.AGENTS_LIST,
|
||||
agents: mockAgents,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ const agentsListCommand: SlashCommand = {
|
||||
agents,
|
||||
};
|
||||
|
||||
context.ui.addItem(agentsListItem, Date.now());
|
||||
context.ui.addItem(agentsListItem);
|
||||
|
||||
return;
|
||||
},
|
||||
@@ -65,13 +65,10 @@ const agentsRefreshCommand: SlashCommand = {
|
||||
};
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Refreshing agent registry...',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: 'Refreshing agent registry...',
|
||||
});
|
||||
|
||||
await agentRegistry.reload();
|
||||
|
||||
|
||||
@@ -127,22 +127,19 @@ describe('chatCommand', () => {
|
||||
|
||||
await listCommand?.action?.(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: 'chat_list',
|
||||
chats: [
|
||||
{
|
||||
name: 'test1',
|
||||
mtime: date1.toISOString(),
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
mtime: date2.toISOString(),
|
||||
},
|
||||
],
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: 'chat_list',
|
||||
chats: [
|
||||
{
|
||||
name: 'test1',
|
||||
mtime: date1.toISOString(),
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
mtime: date2.toISOString(),
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('save subcommand', () => {
|
||||
|
||||
@@ -81,7 +81,7 @@ const listCommand: SlashCommand = {
|
||||
chats: chatDetails,
|
||||
};
|
||||
|
||||
context.ui.addItem(item, Date.now());
|
||||
context.ui.addItem(item);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -94,7 +94,6 @@ describe('directoryCommand', () => {
|
||||
'/home/user/project1',
|
||||
)}\n- ${path.normalize('/home/user/project2')}`,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -121,7 +120,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide at least one path to add.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -135,7 +133,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added directories:\n- ${newPath}`,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -151,7 +148,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added directories:\n- ${newPath1}\n- ${newPath2}`,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -168,7 +164,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: `Error adding '${newPath}': ${error.message}`,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -191,7 +186,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: `The following directories are already in the workspace:\n- ${existingPath}`,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockWorkspaceContext.addDirectory).not.toHaveBeenCalledWith(
|
||||
existingPath,
|
||||
@@ -218,7 +212,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added directories:\n- ${validPath}`,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
@@ -226,7 +219,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: `Error adding '${invalidPath}': ${error.message}`,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -317,7 +309,6 @@ describe('directoryCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: expect.stringContaining('explicitly untrusted'),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,18 +22,18 @@ import type { Config } from '@google/gemini-cli-core';
|
||||
|
||||
async function finishAddingDirectories(
|
||||
config: Config,
|
||||
addItem: (itemData: Omit<HistoryItem, 'id'>, baseTimestamp: number) => number,
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp?: number,
|
||||
) => number,
|
||||
added: string[],
|
||||
errors: string[],
|
||||
) {
|
||||
if (!config) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -41,13 +41,10 @@ async function finishAddingDirectories(
|
||||
if (config.shouldLoadMemoryFromIncludeDirectories()) {
|
||||
await refreshServerHierarchicalMemory(config);
|
||||
}
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added GEMINI.md files from the following directories if there are:\n- ${added.join('\n- ')}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added GEMINI.md files from the following directories if there are:\n- ${added.join('\n- ')}`,
|
||||
});
|
||||
} catch (error) {
|
||||
errors.push(`Error refreshing memory: ${(error as Error).message}`);
|
||||
}
|
||||
@@ -57,17 +54,14 @@ async function finishAddingDirectories(
|
||||
if (gemini) {
|
||||
await gemini.addDirectoryContext();
|
||||
}
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added directories:\n- ${added.join('\n- ')}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully added directories:\n- ${added.join('\n- ')}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
addItem({ type: MessageType.ERROR, text: errors.join('\n') }, Date.now());
|
||||
addItem({ type: MessageType.ERROR, text: errors.join('\n') });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,13 +106,10 @@ export const directoryCommand: SlashCommand = {
|
||||
const [...rest] = args.split(' ');
|
||||
|
||||
if (!config) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -136,13 +127,10 @@ export const directoryCommand: SlashCommand = {
|
||||
.split(',')
|
||||
.filter((p) => p);
|
||||
if (pathsToAdd.length === 0) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide at least one path to add.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide at least one path to add.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -164,15 +152,12 @@ export const directoryCommand: SlashCommand = {
|
||||
}
|
||||
|
||||
if (alreadyAdded.length > 0) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `The following directories are already in the workspace:\n- ${alreadyAdded.join(
|
||||
'\n- ',
|
||||
)}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `The following directories are already in the workspace:\n- ${alreadyAdded.join(
|
||||
'\n- ',
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (pathsToProcess.length === 0) {
|
||||
@@ -262,25 +247,19 @@ export const directoryCommand: SlashCommand = {
|
||||
services: { config },
|
||||
} = context;
|
||||
if (!config) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const workspaceContext = config.getWorkspaceContext();
|
||||
const directories = workspaceContext.getDirectories();
|
||||
const directoryList = directories.map((dir) => `- ${dir}`).join('\n');
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Current workspace directories:\n${directoryList}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Current workspace directories:\n${directoryList}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -148,13 +148,10 @@ describe('extensionsCommand', () => {
|
||||
if (!command.action) throw new Error('Action not defined');
|
||||
await command.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
});
|
||||
});
|
||||
|
||||
it('should show a message if no extensions are installed', async () => {
|
||||
@@ -163,13 +160,10 @@ describe('extensionsCommand', () => {
|
||||
if (!command.action) throw new Error('Action not defined');
|
||||
await command.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -244,26 +238,20 @@ describe('extensionsCommand', () => {
|
||||
|
||||
it('should show usage if no args are provided', async () => {
|
||||
await updateAction(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions update <extension-names>|--all',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions update <extension-names>|--all',
|
||||
});
|
||||
});
|
||||
|
||||
it('should show a message if no extensions are installed', async () => {
|
||||
mockGetExtensions.mockReturnValue([]);
|
||||
await updateAction(mockContext, 'ext-one');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should inform user if there are no extensions to update with --all', async () => {
|
||||
@@ -276,13 +264,10 @@ describe('extensionsCommand', () => {
|
||||
);
|
||||
|
||||
await updateAction(mockContext, '--all');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions to update.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions to update.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should call setPendingItem and addItem in a finally block on success', async () => {
|
||||
@@ -310,13 +295,10 @@ describe('extensionsCommand', () => {
|
||||
extensions: expect.any(Array),
|
||||
});
|
||||
expect(mockContext.ui.setPendingItem).toHaveBeenCalledWith(null);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
});
|
||||
});
|
||||
|
||||
it('should call setPendingItem and addItem in a finally block on failure', async () => {
|
||||
@@ -329,20 +311,14 @@ describe('extensionsCommand', () => {
|
||||
extensions: expect.any(Array),
|
||||
});
|
||||
expect(mockContext.ui.setPendingItem).toHaveBeenCalledWith(null);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Something went wrong',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Something went wrong',
|
||||
});
|
||||
});
|
||||
|
||||
it('should update a single extension by name', async () => {
|
||||
@@ -403,13 +379,10 @@ describe('extensionsCommand', () => {
|
||||
extensions: expect.any(Array),
|
||||
});
|
||||
expect(mockContext.ui.setPendingItem).toHaveBeenCalledWith(null);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.EXTENSIONS_LIST,
|
||||
extensions: expect.any(Array),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -430,13 +403,10 @@ describe('extensionsCommand', () => {
|
||||
await exploreAction(mockContext, '');
|
||||
|
||||
const extensionsUrl = 'https://geminicli.com/extensions/';
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Opening extensions page in your browser: ${extensionsUrl}`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Opening extensions page in your browser: ${extensionsUrl}`,
|
||||
});
|
||||
|
||||
expect(open).toHaveBeenCalledWith(extensionsUrl);
|
||||
});
|
||||
@@ -449,13 +419,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
await exploreAction(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `View available extensions at ${extensionsUrl}`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `View available extensions at ${extensionsUrl}`,
|
||||
});
|
||||
|
||||
// Ensure 'open' was not called in the sandbox
|
||||
expect(open).not.toHaveBeenCalled();
|
||||
@@ -468,13 +435,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
await exploreAction(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Would open extensions page in your browser: ${extensionsUrl} (skipped in test environment)`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Would open extensions page in your browser: ${extensionsUrl} (skipped in test environment)`,
|
||||
});
|
||||
|
||||
// Ensure 'open' was not called in test environment
|
||||
expect(open).not.toHaveBeenCalled();
|
||||
@@ -488,13 +452,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
await exploreAction(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to open browser. Check out the extensions gallery at ${extensionsUrl}`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to open browser. Check out the extensions gallery at ${extensionsUrl}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -549,13 +510,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
it('should show usage if no extension name is provided', async () => {
|
||||
await installAction!(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions install <source>',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions install <source>',
|
||||
});
|
||||
expect(mockInstallExtension).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -572,20 +530,14 @@ describe('extensionsCommand', () => {
|
||||
source: packageName,
|
||||
type: 'git',
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Installing extension from "${packageName}"...`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${packageName}" installed successfully.`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Installing extension from "${packageName}"...`,
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${packageName}" installed successfully.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error message on installation failure', async () => {
|
||||
@@ -603,25 +555,19 @@ describe('extensionsCommand', () => {
|
||||
source: packageName,
|
||||
type: 'git',
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to install extension from "${packageName}": ${errorMessage}`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to install extension from "${packageName}": ${errorMessage}`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error message for invalid source', async () => {
|
||||
const invalidSource = 'a;b';
|
||||
await installAction!(mockContext, invalidSource);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Invalid source: ${invalidSource}`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: `Invalid source: ${invalidSource}`,
|
||||
});
|
||||
expect(mockInstallExtension).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -640,13 +586,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
it('should show usage if no extension is provided', async () => {
|
||||
await linkAction!(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions link <source>',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions link <source>',
|
||||
});
|
||||
expect(mockInstallExtension).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -661,20 +604,14 @@ describe('extensionsCommand', () => {
|
||||
source: packageName,
|
||||
type: 'link',
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Linking extension from "${packageName}"...`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${packageName}" linked successfully.`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Linking extension from "${packageName}"...`,
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${packageName}" linked successfully.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error message on linking failure', async () => {
|
||||
@@ -690,13 +627,10 @@ describe('extensionsCommand', () => {
|
||||
source: packageName,
|
||||
type: 'link',
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to link extension from "${packageName}": ${errorMessage}`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to link extension from "${packageName}": ${errorMessage}`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error message for invalid source', async () => {
|
||||
@@ -723,13 +657,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
it('should show usage if no extension name is provided', async () => {
|
||||
await uninstallAction!(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions uninstall <extension-name>',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions uninstall <extension-name>',
|
||||
});
|
||||
expect(mockUninstallExtension).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -737,20 +668,14 @@ describe('extensionsCommand', () => {
|
||||
const extensionName = 'test-extension';
|
||||
await uninstallAction!(mockContext, extensionName);
|
||||
expect(mockUninstallExtension).toHaveBeenCalledWith(extensionName, false);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Uninstalling extension "${extensionName}"...`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${extensionName}" uninstalled successfully.`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Uninstalling extension "${extensionName}"...`,
|
||||
});
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${extensionName}" uninstalled successfully.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error message on uninstallation failure', async () => {
|
||||
@@ -760,13 +685,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
await uninstallAction!(mockContext, extensionName);
|
||||
expect(mockUninstallExtension).toHaveBeenCalledWith(extensionName, false);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to uninstall extension "${extensionName}": ${errorMessage}`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to uninstall extension "${extensionName}": ${errorMessage}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -785,13 +707,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
it('should show usage if no extension name is provided', async () => {
|
||||
await enableAction!(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions enable <extension> [--scope=<user|workspace|session>]',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions enable <extension> [--scope=<user|workspace|session>]',
|
||||
});
|
||||
});
|
||||
|
||||
it('should call enableExtension with the provided scope', async () => {
|
||||
@@ -840,13 +759,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
it('should show usage if no extension name is provided', async () => {
|
||||
await disableAction!(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions disable <extension> [--scope=<user|workspace|session>]',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions disable <extension> [--scope=<user|workspace|session>]',
|
||||
});
|
||||
});
|
||||
|
||||
it('should call disableExtension with the provided scope', async () => {
|
||||
@@ -912,13 +828,10 @@ describe('extensionsCommand', () => {
|
||||
|
||||
await restartAction!(mockContext, '--all');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
});
|
||||
});
|
||||
|
||||
it('restarts all active extensions when --all is provided', async () => {
|
||||
@@ -939,14 +852,12 @@ describe('extensionsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Restarting 2 extensions...',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: MessageType.INFO,
|
||||
text: '2 extensions restarted successfully.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.dispatchExtensionStateUpdate).toHaveBeenCalledWith({
|
||||
type: 'RESTARTED',
|
||||
@@ -986,7 +897,6 @@ describe('extensionsCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: "Extensions are not yet loaded, can't restart yet",
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockRestartExtension).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -999,7 +909,6 @@ describe('extensionsCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions restart <extension-names>|--all',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockRestartExtension).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -1019,7 +928,6 @@ describe('extensionsCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: 'Failed to restart some extensions:\n ext1: Failed to restart',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1038,7 +946,6 @@ describe('extensionsCommand', () => {
|
||||
type: MessageType.WARNING,
|
||||
text: 'Extension(s) not found or not active: ext2',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1056,7 +963,6 @@ describe('extensionsCommand', () => {
|
||||
type: MessageType.WARNING,
|
||||
text: 'Extension(s) not found or not active: ext2, ext3',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -37,13 +37,10 @@ function showMessageIfNoExtensions(
|
||||
extensions: unknown[],
|
||||
): boolean {
|
||||
if (extensions.length === 0) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions installed. Run `/extensions explore` to check out the gallery.',
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -63,7 +60,7 @@ async function listAction(context: CommandContext) {
|
||||
extensions,
|
||||
};
|
||||
|
||||
context.ui.addItem(historyItem, Date.now());
|
||||
context.ui.addItem(historyItem);
|
||||
}
|
||||
|
||||
function updateAction(context: CommandContext, args: string): Promise<void> {
|
||||
@@ -72,13 +69,10 @@ function updateAction(context: CommandContext, args: string): Promise<void> {
|
||||
const names = all ? null : updateArgs;
|
||||
|
||||
if (!all && names?.length === 0) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions update <extension-names>|--all',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions update <extension-names>|--all',
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -103,16 +97,13 @@ function updateAction(context: CommandContext, args: string): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
updateComplete.then((updateInfos) => {
|
||||
if (updateInfos.length === 0) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions to update.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: 'No extensions to update.',
|
||||
});
|
||||
}
|
||||
|
||||
context.ui.addItem(historyItem, Date.now());
|
||||
context.ui.addItem(historyItem);
|
||||
context.ui.setPendingItem(null);
|
||||
});
|
||||
|
||||
@@ -136,26 +127,20 @@ function updateAction(context: CommandContext, args: string): Promise<void> {
|
||||
(extension) => extension.name === name,
|
||||
);
|
||||
if (!extension) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Extension ${name} not found.`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Extension ${name} not found.`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
resolveUpdateComplete!([]);
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: getErrorMessage(error),
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: getErrorMessage(error),
|
||||
});
|
||||
}
|
||||
return updateComplete.then((_) => {});
|
||||
}
|
||||
@@ -166,13 +151,10 @@ async function restartAction(
|
||||
): Promise<void> {
|
||||
const extensionLoader = context.services.config?.getExtensionLoader();
|
||||
if (!extensionLoader) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: "Extensions are not yet loaded, can't restart yet",
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: "Extensions are not yet loaded, can't restart yet",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -185,13 +167,10 @@ async function restartAction(
|
||||
const all = restartArgs.length === 1 && restartArgs[0] === '--all';
|
||||
const names = all ? null : restartArgs;
|
||||
if (!all && names?.length === 0) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions restart <extension-names>|--all',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Usage: /extensions restart <extension-names>|--all',
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -208,15 +187,10 @@ async function restartAction(
|
||||
!extensionsToRestart.some((extension) => extension.name === name),
|
||||
);
|
||||
if (notFound.length > 0) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.WARNING,
|
||||
text: `Extension(s) not found or not active: ${notFound.join(
|
||||
', ',
|
||||
)}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.WARNING,
|
||||
text: `Extension(s) not found or not active: ${notFound.join(', ')}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +206,7 @@ async function restartAction(
|
||||
text: `Restarting ${extensionsToRestart.length} extension${s}...`,
|
||||
color: theme.text.primary,
|
||||
};
|
||||
context.ui.addItem(restartingMessage, Date.now());
|
||||
context.ui.addItem(restartingMessage);
|
||||
|
||||
const results = await Promise.allSettled(
|
||||
extensionsToRestart.map(async (extension) => {
|
||||
@@ -259,13 +233,10 @@ async function restartAction(
|
||||
return `${extensionName}: ${getErrorMessage(failure.reason)}`;
|
||||
})
|
||||
.join('\n ');
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to restart some extensions:\n ${errorMessages}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to restart some extensions:\n ${errorMessages}`,
|
||||
});
|
||||
} else {
|
||||
const infoItem: HistoryItemInfo = {
|
||||
type: MessageType.INFO,
|
||||
@@ -273,7 +244,7 @@ async function restartAction(
|
||||
icon: emptyIcon,
|
||||
color: theme.text.primary,
|
||||
};
|
||||
context.ui.addItem(infoItem, Date.now());
|
||||
context.ui.addItem(infoItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,42 +253,30 @@ async function exploreAction(context: CommandContext) {
|
||||
|
||||
// Only check for NODE_ENV for explicit test mode, not for unit test framework
|
||||
if (process.env['NODE_ENV'] === 'test') {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Would open extensions page in your browser: ${extensionsUrl} (skipped in test environment)`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Would open extensions page in your browser: ${extensionsUrl} (skipped in test environment)`,
|
||||
});
|
||||
} else if (
|
||||
process.env['SANDBOX'] &&
|
||||
process.env['SANDBOX'] !== 'sandbox-exec'
|
||||
) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `View available extensions at ${extensionsUrl}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `View available extensions at ${extensionsUrl}`,
|
||||
});
|
||||
} else {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Opening extensions page in your browser: ${extensionsUrl}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Opening extensions page in your browser: ${extensionsUrl}`,
|
||||
});
|
||||
try {
|
||||
await open(extensionsUrl);
|
||||
} catch (_error) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to open browser. Check out the extensions gallery at ${extensionsUrl}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to open browser. Check out the extensions gallery at ${extensionsUrl}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,13 +305,10 @@ function getEnableDisableContext(
|
||||
(parts.length === 3 && parts[1] === '--scope') // --scope <scope>
|
||||
)
|
||||
) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions ${context.invocation?.name} <extension> [--scope=<user|workspace|session>]`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions ${context.invocation?.name} <extension> [--scope=<user|workspace|session>]`,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
let scope: SettingScope;
|
||||
@@ -372,13 +328,10 @@ function getEnableDisableContext(
|
||||
scope = SettingScope.Session;
|
||||
break;
|
||||
default:
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Unsupported scope ${parts[2]}, should be one of "user", "workspace", or "session"`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Unsupported scope ${parts[2]}, should be one of "user", "workspace", or "session"`,
|
||||
});
|
||||
debugLogger.error();
|
||||
return null;
|
||||
}
|
||||
@@ -410,13 +363,10 @@ async function disableAction(context: CommandContext, args: string) {
|
||||
const { names, scope, extensionManager } = enableContext;
|
||||
for (const name of names) {
|
||||
await extensionManager.disableExtension(name, scope);
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${name}" disabled for the scope "${scope}"`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${name}" disabled for the scope "${scope}"`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,13 +377,10 @@ async function enableAction(context: CommandContext, args: string) {
|
||||
const { names, scope, extensionManager } = enableContext;
|
||||
for (const name of names) {
|
||||
await extensionManager.enableExtension(name, scope);
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${name}" enabled for the scope "${scope}"`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${name}" enabled for the scope "${scope}"`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,13 +395,10 @@ async function installAction(context: CommandContext, args: string) {
|
||||
|
||||
const source = args.trim();
|
||||
if (!source) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions install <source>`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions install <source>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -473,45 +417,33 @@ async function installAction(context: CommandContext, args: string) {
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Invalid source: ${source}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Invalid source: ${source}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Installing extension from "${source}"...`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Installing extension from "${source}"...`,
|
||||
});
|
||||
|
||||
try {
|
||||
const installMetadata = await inferInstallMetadata(source);
|
||||
const extension =
|
||||
await extensionLoader.installOrUpdateExtension(installMetadata);
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${extension.name}" installed successfully.`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${extension.name}" installed successfully.`,
|
||||
});
|
||||
} catch (error) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to install extension from "${source}": ${getErrorMessage(
|
||||
error,
|
||||
)}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to install extension from "${source}": ${getErrorMessage(
|
||||
error,
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,49 +458,37 @@ async function linkAction(context: CommandContext, args: string) {
|
||||
|
||||
const sourceFilepath = args.trim();
|
||||
if (!sourceFilepath) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions link <source>`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions link <source>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (/[;&|`'"]/.test(sourceFilepath)) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Source file path contains disallowed characters: ${sourceFilepath}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Source file path contains disallowed characters: ${sourceFilepath}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await stat(sourceFilepath);
|
||||
} catch (error) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Invalid source: ${sourceFilepath}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Invalid source: ${sourceFilepath}`,
|
||||
});
|
||||
debugLogger.error(
|
||||
`Failed to stat path "${sourceFilepath}": ${getErrorMessage(error)}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Linking extension from "${sourceFilepath}"...`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Linking extension from "${sourceFilepath}"...`,
|
||||
});
|
||||
|
||||
try {
|
||||
const installMetadata: ExtensionInstallMetadata = {
|
||||
@@ -577,23 +497,17 @@ async function linkAction(context: CommandContext, args: string) {
|
||||
};
|
||||
const extension =
|
||||
await extensionLoader.installOrUpdateExtension(installMetadata);
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${extension.name}" linked successfully.`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${extension.name}" linked successfully.`,
|
||||
});
|
||||
} catch (error) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to link extension from "${sourceFilepath}": ${getErrorMessage(
|
||||
error,
|
||||
)}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to link extension from "${sourceFilepath}": ${getErrorMessage(
|
||||
error,
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,43 +522,31 @@ async function uninstallAction(context: CommandContext, args: string) {
|
||||
|
||||
const name = args.trim();
|
||||
if (!name) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions uninstall <extension-name>`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Usage: /extensions uninstall <extension-name>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Uninstalling extension "${name}"...`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Uninstalling extension "${name}"...`,
|
||||
});
|
||||
|
||||
try {
|
||||
await extensionLoader.uninstallExtension(name, false);
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${name}" uninstalled successfully.`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Extension "${name}" uninstalled successfully.`,
|
||||
});
|
||||
} catch (error) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to uninstall extension "${name}": ${getErrorMessage(
|
||||
error,
|
||||
)}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to uninstall extension "${name}": ${getErrorMessage(
|
||||
error,
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ describe('helpCommand', () => {
|
||||
type: MessageType.HELP,
|
||||
timestamp: expect.any(Date),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,6 @@ export const helpCommand: SlashCommand = {
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
context.ui.addItem(helpItem, Date.now());
|
||||
context.ui.addItem(helpItem);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -109,7 +109,6 @@ describe('hooksCommand', () => {
|
||||
expect.objectContaining({
|
||||
type: MessageType.HOOKS_LIST,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -155,7 +154,6 @@ describe('hooksCommand', () => {
|
||||
type: MessageType.HOOKS_LIST,
|
||||
hooks: [],
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -179,7 +177,6 @@ describe('hooksCommand', () => {
|
||||
type: MessageType.HOOKS_LIST,
|
||||
hooks: [],
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -208,7 +205,6 @@ describe('hooksCommand', () => {
|
||||
type: MessageType.HOOKS_LIST,
|
||||
hooks: mockHooks,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ async function panelAction(
|
||||
hooks: allHooks,
|
||||
};
|
||||
|
||||
context.ui.addItem(hooksListItem, Date.now());
|
||||
context.ui.addItem(hooksListItem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -231,7 +231,6 @@ describe('mcpCommand', () => {
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -246,7 +245,6 @@ describe('mcpCommand', () => {
|
||||
type: MessageType.MCP_STATUS,
|
||||
showDescriptions: true,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -261,7 +259,6 @@ describe('mcpCommand', () => {
|
||||
type: MessageType.MCP_STATUS,
|
||||
showDescriptions: false,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,19 +91,16 @@ const authCommand: SlashCommand = {
|
||||
// The authentication process will discover OAuth requirements automatically
|
||||
|
||||
const displayListener = (message: string) => {
|
||||
context.ui.addItem({ type: 'info', text: message }, Date.now());
|
||||
context.ui.addItem({ type: 'info', text: message });
|
||||
};
|
||||
|
||||
appEvents.on(AppEvent.OauthDisplayMessage, displayListener);
|
||||
|
||||
try {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `Starting OAuth authentication for MCP server '${serverName}'...`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: 'info',
|
||||
text: `Starting OAuth authentication for MCP server '${serverName}'...`,
|
||||
});
|
||||
|
||||
// Import dynamically to avoid circular dependencies
|
||||
const { MCPOAuthProvider } = await import('@google/gemini-cli-core');
|
||||
@@ -122,24 +119,18 @@ const authCommand: SlashCommand = {
|
||||
appEvents,
|
||||
);
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `✅ Successfully authenticated with MCP server '${serverName}'!`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: 'info',
|
||||
text: `✅ Successfully authenticated with MCP server '${serverName}'!`,
|
||||
});
|
||||
|
||||
// Trigger tool re-discovery to pick up authenticated server
|
||||
const mcpClientManager = config.getMcpClientManager();
|
||||
if (mcpClientManager) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `Restarting MCP server '${serverName}'...`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: 'info',
|
||||
text: `Restarting MCP server '${serverName}'...`,
|
||||
});
|
||||
await mcpClientManager.restartServer(serverName);
|
||||
}
|
||||
// Update the client with the new tools
|
||||
@@ -279,7 +270,7 @@ const listAction = async (
|
||||
showSchema,
|
||||
};
|
||||
|
||||
context.ui.addItem(mcpStatusItem, Date.now());
|
||||
context.ui.addItem(mcpStatusItem);
|
||||
};
|
||||
|
||||
const listCommand: SlashCommand = {
|
||||
@@ -335,13 +326,10 @@ const refreshCommand: SlashCommand = {
|
||||
};
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'Restarting MCP servers...',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: 'info',
|
||||
text: 'Restarting MCP servers...',
|
||||
});
|
||||
|
||||
await mcpClientManager.restart();
|
||||
|
||||
|
||||
@@ -91,7 +91,6 @@ describe('skillsCommand', () => {
|
||||
],
|
||||
showDescriptions: true,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -120,7 +119,6 @@ describe('skillsCommand', () => {
|
||||
],
|
||||
showDescriptions: true,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -132,7 +130,6 @@ describe('skillsCommand', () => {
|
||||
expect.objectContaining({
|
||||
showDescriptions: false,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -229,7 +226,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Skill "skill1" disabled by adding it to the disabled list in project (/workspace) settings. Use "/skills reload" for it to take effect.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -258,7 +254,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Skill "skill1" enabled by removing it from the disabled list in project (/workspace) and user (/user/settings.json) settings. Use "/skills reload" for it to take effect.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -298,7 +293,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Skill "skill1" enabled by removing it from the disabled list in project (/workspace) and user (/user/settings.json) settings. Use "/skills reload" for it to take effect.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -313,7 +307,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: 'Skill "non-existent" not found.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -359,7 +352,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Agent skills reloaded successfully.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -385,7 +377,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Agent skills reloaded successfully. 1 newly available skill.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -409,7 +400,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Agent skills reloaded successfully. 1 skill no longer available.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -434,7 +424,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.INFO,
|
||||
text: 'Agent skills reloaded successfully. 1 newly available skill and 1 skill no longer available.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -451,7 +440,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve configuration.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -477,7 +465,6 @@ describe('skillsCommand', () => {
|
||||
type: MessageType.ERROR,
|
||||
text: 'Failed to reload skills: Reload failed',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,13 +39,10 @@ async function listAction(
|
||||
|
||||
const skillManager = context.services.config?.getSkillManager();
|
||||
if (!skillManager) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve skill manager.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve skill manager.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,7 +63,7 @@ async function listAction(
|
||||
showDescriptions: useShowDescriptions,
|
||||
};
|
||||
|
||||
context.ui.addItem(skillsListItem, Date.now());
|
||||
context.ui.addItem(skillsListItem);
|
||||
}
|
||||
|
||||
async function disableAction(
|
||||
@@ -75,25 +72,19 @@ async function disableAction(
|
||||
): Promise<void | SlashCommandActionReturn> {
|
||||
const skillName = args.trim();
|
||||
if (!skillName) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide a skill name to disable.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide a skill name to disable.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const skillManager = context.services.config?.getSkillManager();
|
||||
const skill = skillManager?.getSkill(skillName);
|
||||
if (!skill) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Skill "${skillName}" not found.`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Skill "${skillName}" not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,13 +102,10 @@ async function disableAction(
|
||||
feedback += ' Use "/skills reload" for it to take effect.';
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: feedback,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: feedback,
|
||||
});
|
||||
}
|
||||
|
||||
async function enableAction(
|
||||
@@ -126,13 +114,10 @@ async function enableAction(
|
||||
): Promise<void | SlashCommandActionReturn> {
|
||||
const skillName = args.trim();
|
||||
if (!skillName) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide a skill name to enable.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Please provide a skill name to enable.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -146,13 +131,10 @@ async function enableAction(
|
||||
feedback += ' Use "/skills reload" for it to take effect.';
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: feedback,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
text: feedback,
|
||||
});
|
||||
}
|
||||
|
||||
async function reloadAction(
|
||||
@@ -160,13 +142,10 @@ async function reloadAction(
|
||||
): Promise<void | SlashCommandActionReturn> {
|
||||
const config = context.services.config;
|
||||
if (!config) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve configuration.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve configuration.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -226,27 +205,21 @@ async function reloadAction(
|
||||
successText += ` ${details.join(' and ')}.`;
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: successText,
|
||||
icon: '✓ ',
|
||||
color: 'green',
|
||||
} as HistoryItemInfo,
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: 'info',
|
||||
text: successText,
|
||||
icon: '✓ ',
|
||||
color: 'green',
|
||||
} as HistoryItemInfo);
|
||||
} catch (error) {
|
||||
clearTimeout(pendingTimeout);
|
||||
if (pendingItemSet) {
|
||||
context.ui.setPendingItem(null);
|
||||
}
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to reload skills: ${error instanceof Error ? error.message : String(error)}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: `Failed to reload skills: ${error instanceof Error ? error.message : String(error)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,13 +37,10 @@ describe('statsCommand', () => {
|
||||
const expectedDuration = formatDuration(
|
||||
endTime.getTime() - startTime.getTime(),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.STATS,
|
||||
duration: expectedDuration,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.STATS,
|
||||
duration: expectedDuration,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch and display quota if config is available', async () => {
|
||||
@@ -62,7 +59,6 @@ describe('statsCommand', () => {
|
||||
expect.objectContaining({
|
||||
quotas: mockQuota,
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -75,12 +71,9 @@ describe('statsCommand', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
modelSubCommand.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.MODEL_STATS,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.MODEL_STATS,
|
||||
});
|
||||
});
|
||||
|
||||
it('should display tool stats when using the "tools" subcommand', () => {
|
||||
@@ -92,11 +85,8 @@ describe('statsCommand', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
toolsSubCommand.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.TOOL_STATS,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.TOOL_STATS,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,13 +17,10 @@ async function defaultSessionView(context: CommandContext) {
|
||||
const now = new Date();
|
||||
const { sessionStartTime } = context.session.stats;
|
||||
if (!sessionStartTime) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Session start time is unavailable, cannot calculate stats.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Session start time is unavailable, cannot calculate stats.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const wallDuration = now.getTime() - sessionStartTime.getTime();
|
||||
@@ -40,7 +37,7 @@ async function defaultSessionView(context: CommandContext) {
|
||||
}
|
||||
}
|
||||
|
||||
context.ui.addItem(statsItem, Date.now());
|
||||
context.ui.addItem(statsItem);
|
||||
}
|
||||
|
||||
export const statsCommand: SlashCommand = {
|
||||
@@ -68,12 +65,9 @@ export const statsCommand: SlashCommand = {
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
action: (context: CommandContext) => {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.MODEL_STATS,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.MODEL_STATS,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -82,12 +76,9 @@ export const statsCommand: SlashCommand = {
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
action: (context: CommandContext) => {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.TOOL_STATS,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.TOOL_STATS,
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -40,13 +40,10 @@ describe('toolsCommand', () => {
|
||||
if (!toolsCommand.action) throw new Error('Action not defined');
|
||||
await toolsCommand.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should display "No tools available" when none are found', async () => {
|
||||
@@ -63,14 +60,11 @@ describe('toolsCommand', () => {
|
||||
if (!toolsCommand.action) throw new Error('Action not defined');
|
||||
await toolsCommand.action(mockContext, '');
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.TOOLS_LIST,
|
||||
tools: [],
|
||||
showDescriptions: false,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith({
|
||||
type: MessageType.TOOLS_LIST,
|
||||
tools: [],
|
||||
showDescriptions: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should list tools without descriptions by default', async () => {
|
||||
|
||||
@@ -27,13 +27,10 @@ export const toolsCommand: SlashCommand = {
|
||||
|
||||
const toolRegistry = context.services.config?.getToolRegistry();
|
||||
if (!toolRegistry) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
context.ui.addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,6 +48,6 @@ export const toolsCommand: SlashCommand = {
|
||||
showDescriptions: useShowDescriptions,
|
||||
};
|
||||
|
||||
context.ui.addItem(toolsListItem, Date.now());
|
||||
context.ui.addItem(toolsListItem);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
MultiFolderTrustChoice,
|
||||
type MultiFolderTrustDialogProps,
|
||||
} from './MultiFolderTrustDialog.js';
|
||||
import { vi } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import {
|
||||
TrustLevel,
|
||||
type LoadedTrustedFolders,
|
||||
@@ -213,13 +213,10 @@ describe('MultiFolderTrustDialog', () => {
|
||||
onSelect(MultiFolderTrustChoice.YES);
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenCalledWith({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
});
|
||||
expect(mockOnComplete).toHaveBeenCalled();
|
||||
expect(mockFinishAddingDirectories).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -31,13 +31,16 @@ export interface MultiFolderTrustDialogProps {
|
||||
config: Config,
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp: number,
|
||||
baseTimestamp?: number,
|
||||
) => number,
|
||||
added: string[],
|
||||
errors: string[],
|
||||
) => Promise<void>;
|
||||
config: Config;
|
||||
addItem: (itemData: Omit<HistoryItem, 'id'>, baseTimestamp: number) => number;
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp?: number,
|
||||
) => number;
|
||||
}
|
||||
|
||||
export const MultiFolderTrustDialog: React.FC<MultiFolderTrustDialogProps> = ({
|
||||
@@ -95,13 +98,10 @@ export const MultiFolderTrustDialog: React.FC<MultiFolderTrustDialogProps> = ({
|
||||
setSubmitted(true);
|
||||
|
||||
if (!config) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
});
|
||||
onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -780,7 +780,6 @@ describe('useGeminiStream', () => {
|
||||
'Agent execution stopped: Stop reason from hook',
|
||||
),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
// Ensure we do NOT call back to the API
|
||||
expect(mockSendMessageStream).not.toHaveBeenCalled();
|
||||
@@ -1085,13 +1084,10 @@ describe('useGeminiStream', () => {
|
||||
|
||||
// Verify cancellation message is added
|
||||
await waitFor(() => {
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Request cancelled.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenCalledWith({
|
||||
type: MessageType.INFO,
|
||||
text: 'Request cancelled.',
|
||||
});
|
||||
});
|
||||
|
||||
// Verify state is reset
|
||||
@@ -1194,7 +1190,6 @@ describe('useGeminiStream', () => {
|
||||
expect.objectContaining({
|
||||
text: 'Request cancelled.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1330,7 +1325,6 @@ describe('useGeminiStream', () => {
|
||||
expect.objectContaining({
|
||||
text: 'Request cancelled.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1995,13 +1989,10 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: 'info',
|
||||
text: expectedMessage,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenCalledWith({
|
||||
type: 'info',
|
||||
text: expectedMessage,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -2644,13 +2635,10 @@ describe('useGeminiStream', () => {
|
||||
expect(result.current.loopDetectionConfirmationRequest).toBeNull();
|
||||
|
||||
// Verify appropriate message was added
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'Loop detection has been disabled for this session. Retrying request...',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenCalledWith({
|
||||
type: 'info',
|
||||
text: 'Loop detection has been disabled for this session. Retrying request...',
|
||||
});
|
||||
|
||||
// Verify that the request was retried
|
||||
await waitFor(() => {
|
||||
@@ -2707,13 +2695,10 @@ describe('useGeminiStream', () => {
|
||||
expect(result.current.loopDetectionConfirmationRequest).toBeNull();
|
||||
|
||||
// Verify appropriate message was added
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'A potential loop was detected. This can happen due to repetitive tool calls or other model behavior. The request has been halted.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenCalledWith({
|
||||
type: 'info',
|
||||
text: 'A potential loop was detected. This can happen due to repetitive tool calls or other model behavior. The request has been halted.',
|
||||
});
|
||||
|
||||
// Verify that the request was NOT retried
|
||||
expect(mockSendMessageStream).toHaveBeenCalledTimes(1);
|
||||
@@ -2750,13 +2735,10 @@ describe('useGeminiStream', () => {
|
||||
expect(result.current.loopDetectionConfirmationRequest).toBeNull();
|
||||
|
||||
// Verify first message was added
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'A potential loop was detected. This can happen due to repetitive tool calls or other model behavior. The request has been halted.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenCalledWith({
|
||||
type: 'info',
|
||||
text: 'A potential loop was detected. This can happen due to repetitive tool calls or other model behavior. The request has been halted.',
|
||||
});
|
||||
|
||||
// Second loop detection - set up fresh mock for second call
|
||||
mockSendMessageStream.mockReturnValueOnce(
|
||||
@@ -2800,13 +2782,10 @@ describe('useGeminiStream', () => {
|
||||
expect(result.current.loopDetectionConfirmationRequest).toBeNull();
|
||||
|
||||
// Verify second message was added
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'Loop detection has been disabled for this session. Retrying request...',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenCalledWith({
|
||||
type: 'info',
|
||||
text: 'Loop detection has been disabled for this session. Retrying request...',
|
||||
});
|
||||
|
||||
// Verify that the request was retried
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -149,7 +149,6 @@ export const useGeminiStream = (
|
||||
mapTrackedToolCallsToDisplay(
|
||||
completedToolCallsFromScheduler as TrackedToolCall[],
|
||||
),
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
// Clear the live-updating display now that the final state is in history.
|
||||
@@ -248,10 +247,7 @@ export const useGeminiStream = (
|
||||
prevActiveShellPtyIdRef.current !== null &&
|
||||
activeShellPtyId === null
|
||||
) {
|
||||
addItem(
|
||||
{ type: MessageType.INFO, text: 'Request cancelled.' },
|
||||
Date.now(),
|
||||
);
|
||||
addItem({ type: MessageType.INFO, text: 'Request cancelled.' });
|
||||
setIsResponding(false);
|
||||
}
|
||||
prevActiveShellPtyIdRef.current = activeShellPtyId;
|
||||
@@ -351,12 +347,9 @@ export const useGeminiStream = (
|
||||
}
|
||||
return tool;
|
||||
});
|
||||
addItem(
|
||||
{ ...toolGroup, tools: updatedTools } as HistoryItemWithoutId,
|
||||
Date.now(),
|
||||
);
|
||||
addItem({ ...toolGroup, tools: updatedTools } as HistoryItemWithoutId);
|
||||
} else {
|
||||
addItem(pendingHistoryItemRef.current, Date.now());
|
||||
addItem(pendingHistoryItemRef.current);
|
||||
}
|
||||
}
|
||||
setPendingHistoryItem(null);
|
||||
@@ -368,13 +361,10 @@ export const useGeminiStream = (
|
||||
// If shell is active, we delay this message to ensure correct ordering
|
||||
// (Shell item first, then Info message).
|
||||
if (!activeShellPtyId) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Request cancelled.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.INFO,
|
||||
text: 'Request cancelled.',
|
||||
});
|
||||
setIsResponding(false);
|
||||
}
|
||||
}
|
||||
@@ -719,32 +709,26 @@ export const useGeminiStream = (
|
||||
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
|
||||
setPendingHistoryItem(null);
|
||||
}
|
||||
return addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text:
|
||||
`IMPORTANT: This conversation exceeded the compress threshold. ` +
|
||||
`A compressed context will be sent for future messages (compressed from: ` +
|
||||
`${eventValue?.originalTokenCount ?? 'unknown'} to ` +
|
||||
`${eventValue?.newTokenCount ?? 'unknown'} tokens).`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
return addItem({
|
||||
type: 'info',
|
||||
text:
|
||||
`IMPORTANT: This conversation exceeded the compress threshold. ` +
|
||||
`A compressed context will be sent for future messages (compressed from: ` +
|
||||
`${eventValue?.originalTokenCount ?? 'unknown'} to ` +
|
||||
`${eventValue?.newTokenCount ?? 'unknown'} tokens).`,
|
||||
});
|
||||
},
|
||||
[addItem, pendingHistoryItemRef, setPendingHistoryItem],
|
||||
);
|
||||
|
||||
const handleMaxSessionTurnsEvent = useCallback(
|
||||
() =>
|
||||
addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text:
|
||||
`The session has reached the maximum number of turns: ${config.getMaxSessionTurns()}. ` +
|
||||
`Please update this limit in your setting.json file.`,
|
||||
},
|
||||
Date.now(),
|
||||
),
|
||||
addItem({
|
||||
type: 'info',
|
||||
text:
|
||||
`The session has reached the maximum number of turns: ${config.getMaxSessionTurns()}. ` +
|
||||
`Please update this limit in your setting.json file.`,
|
||||
}),
|
||||
[addItem, config],
|
||||
);
|
||||
|
||||
@@ -764,13 +748,10 @@ export const useGeminiStream = (
|
||||
' Please try reducing the size of your message or use the `/compress` command to compress the chat history.';
|
||||
}
|
||||
|
||||
addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: 'info',
|
||||
text,
|
||||
});
|
||||
},
|
||||
[addItem, onCancelSubmit, config],
|
||||
);
|
||||
@@ -1041,13 +1022,10 @@ export const useGeminiStream = (
|
||||
.getGeminiClient()
|
||||
.getLoopDetectionService()
|
||||
.disableForSession();
|
||||
addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `Loop detection has been disabled for this session. Retrying request...`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: 'info',
|
||||
text: `Loop detection has been disabled for this session. Retrying request...`,
|
||||
});
|
||||
|
||||
if (lastQueryRef.current && lastPromptIdRef.current) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
@@ -1058,13 +1036,10 @@ export const useGeminiStream = (
|
||||
);
|
||||
}
|
||||
} else {
|
||||
addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: `A potential loop was detected. This can happen due to repetitive tool calls or other model behavior. The request has been halted.`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: 'info',
|
||||
text: `A potential loop was detected. This can happen due to repetitive tool calls or other model behavior. The request has been halted.`,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1215,13 +1190,10 @@ export const useGeminiStream = (
|
||||
);
|
||||
|
||||
if (stopExecutionTool && stopExecutionTool.response.error) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Agent execution stopped: ${stopExecutionTool.response.error.message}`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.INFO,
|
||||
text: `Agent execution stopped: ${stopExecutionTool.response.error.message}`,
|
||||
});
|
||||
setIsResponding(false);
|
||||
|
||||
const callIdsToMarkAsSubmitted = geminiTools.map(
|
||||
@@ -1240,13 +1212,10 @@ export const useGeminiStream = (
|
||||
// If the turn was cancelled via the imperative escape key flow,
|
||||
// the cancellation message is added there. We check the ref to avoid duplication.
|
||||
if (!turnCancelledRef.current) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Request cancelled.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.INFO,
|
||||
text: 'Request cancelled.',
|
||||
});
|
||||
}
|
||||
setIsResponding(false);
|
||||
|
||||
|
||||
@@ -200,4 +200,23 @@ describe('useHistoryManager', () => {
|
||||
expect(result.current.history[1].text).toBe('Gemini response');
|
||||
expect(result.current.history[2].text).toBe('Message 1');
|
||||
});
|
||||
|
||||
it('should use Date.now() as default baseTimestamp if not provided', () => {
|
||||
const { result } = renderHook(() => useHistory());
|
||||
const before = Date.now();
|
||||
const itemData: Omit<HistoryItem, 'id'> = {
|
||||
type: 'user',
|
||||
text: 'Default timestamp test',
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(itemData);
|
||||
});
|
||||
const after = Date.now();
|
||||
|
||||
expect(result.current.history).toHaveLength(1);
|
||||
// ID should be >= before + 1 (since counter starts at 0 and increments to 1)
|
||||
expect(result.current.history[0].id).toBeGreaterThanOrEqual(before + 1);
|
||||
expect(result.current.history[0].id).toBeLessThanOrEqual(after + 1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface UseHistoryManagerReturn {
|
||||
history: HistoryItem[];
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp: number,
|
||||
baseTimestamp?: number,
|
||||
isResuming?: boolean,
|
||||
) => number; // Returns the generated ID
|
||||
updateItem: (
|
||||
@@ -56,7 +56,7 @@ export function useHistory({
|
||||
const addItem = useCallback(
|
||||
(
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp: number,
|
||||
baseTimestamp: number = Date.now(),
|
||||
isResuming: boolean = false,
|
||||
): number => {
|
||||
const id = getNextMessageId(baseTimestamp);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import type { Mock } from 'vitest';
|
||||
import { renderHook } from '../../test-utils/render.js';
|
||||
import { waitFor } from '../../test-utils/async.js';
|
||||
@@ -132,7 +132,6 @@ describe('useIncludeDirsTrust', () => {
|
||||
expect.objectContaining({
|
||||
text: expect.stringContaining("Error adding '/dir2': Test error"),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(
|
||||
mockConfig.clearPendingIncludeDirectories,
|
||||
|
||||
@@ -18,18 +18,18 @@ import { MessageType, type HistoryItem } from '../types.js';
|
||||
|
||||
async function finishAddingDirectories(
|
||||
config: Config,
|
||||
addItem: (itemData: Omit<HistoryItem, 'id'>, baseTimestamp: number) => number,
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp?: number,
|
||||
) => number,
|
||||
added: string[],
|
||||
errors: string[],
|
||||
) {
|
||||
if (!config) {
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
addItem({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Configuration is not available.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ async function finishAddingDirectories(
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
addItem({ type: MessageType.ERROR, text: errors.join('\n') }, Date.now());
|
||||
addItem({ type: MessageType.ERROR, text: errors.join('\n') });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user