Skip to content

Commit 8c138c0

Browse files
committed
feat: added action to reload extension in case of error
1 parent 3420fee commit 8c138c0

14 files changed

Lines changed: 138 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- avoid showing password dialog on start up in some cases
1313
- added getOpenProjects to Extension Context API
1414
- correctly showing Updated Files in repos without any commits
15+
- added action to reload extension in case of error
1516

1617
## [0.75.0]
1718

packages/common/src/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export interface ApplicationAPI {
161161
installExtension: (extensionId: string, repositoryUrl: string, projectDir?: string) => Promise<ExtensionOperationResult>;
162162
uninstallExtension: (extensionId: string, projectDir?: string) => Promise<boolean>;
163163
updateExtension: (extensionId: string, repositoryUrl: string, projectDir?: string) => Promise<ExtensionOperationResult>;
164+
reloadExtension: (filePath: string, projectDir?: string) => Promise<boolean>;
164165
getExtensionUIComponents: (placement?: string, projectDir?: string, taskId?: string) => Promise<ExtensionUIComponent[]>;
165166
getUIExtensionData: (extensionId: string, componentId: string, projectDir?: string, taskId?: string) => Promise<unknown>;
166167
executeUIExtensionAction: (

packages/common/src/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,10 +497,13 @@
497497
"updating": "Updating...",
498498
"uninstall": "Uninstall",
499499
"uninstalling": "Uninstalling...",
500+
"reload": "Reload",
501+
"reloading": "Reloading...",
500502
"success": {
501503
"install": "Successfully installed {{name}}",
502504
"update": "Successfully updated {{name}}",
503505
"uninstall": "Successfully uninstalled {{name}}",
506+
"reload": "Successfully reloaded {{name}}",
504507
"saveSettings": "Successfully saved settings for {{name}}"
505508
},
506509
"errors": {
@@ -510,6 +513,7 @@
510513
"install": "Failed to install extension. Check logs for details.",
511514
"update": "Failed to update extension. Check logs for details.",
512515
"uninstall": "Failed to uninstall extension",
516+
"reload": "Failed to reload extension",
513517
"saveSettings": "Failed to save extension settings",
514518
"settingsNotAvailable": "Extension settings are not available",
515519
"repositoryExists": "This repository is already added",

packages/common/src/locales/ru.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,13 @@
491491
"updating": "Обновление...",
492492
"uninstall": "Удалить",
493493
"uninstalling": "Удаление...",
494+
"reload": "Перезагрузить",
495+
"reloading": "Перезагрузка...",
494496
"success": {
495497
"install": "«{{name}}» успешно установлен",
496498
"update": "«{{name}}» успешно обновлён",
497499
"uninstall": "«{{name}}» успешно удалён",
500+
"reload": "«{{name}}» успешно перезагружен",
498501
"saveSettings": "Настройки для «{{name}}» успешно сохранены"
499502
},
500503
"errors": {
@@ -504,6 +507,7 @@
504507
"install": "Не удалось установить расширение. Подробности см. в логах.",
505508
"update": "Не удалось обновить расширение. Подробности см. в логах.",
506509
"uninstall": "Не удалось удалить расширение",
510+
"reload": "Не удалось перезагрузить расширение",
507511
"saveSettings": "Не удалось сохранить настройки расширения",
508512
"settingsNotAvailable": "Настройки расширения недоступны",
509513
"repositoryExists": "Этот репозиторий уже добавлен",

packages/common/src/locales/zh.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,10 +496,13 @@
496496
"updating": "更新中...",
497497
"uninstall": "卸载",
498498
"uninstalling": "卸载中...",
499+
"reload": "重新加载",
500+
"reloading": "重新加载中...",
499501
"success": {
500502
"install": "成功安装 {{name}}",
501503
"update": "成功更新 {{name}}",
502504
"uninstall": "成功卸载 {{name}}",
505+
"reload": "成功重新加载 {{name}}",
503506
"saveSettings": "成功保存 {{name}} 的设置"
504507
},
505508
"errors": {
@@ -509,6 +512,7 @@
509512
"install": "安装扩展失败。请查看日志了解详情。",
510513
"update": "更新扩展失败。请查看日志了解详情。",
511514
"uninstall": "卸载扩展失败",
515+
"reload": "重新加载扩展失败",
512516
"saveSettings": "保存扩展设置失败",
513517
"settingsNotAvailable": "扩展设置不可用",
514518
"repositoryExists": "此仓库已添加",

src/main/events-handler.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,18 @@ export class EventsHandler {
12501250
return await this.extensionManager.uninstallExtension(extensionId, projectDir);
12511251
}
12521252

1253+
async reloadExtension(filePath: string, projectDir?: string): Promise<boolean> {
1254+
let project: Project | undefined = undefined;
1255+
if (projectDir) {
1256+
project = this.projectManager.getProject(projectDir);
1257+
if (!project) {
1258+
throw new Error('Project not found');
1259+
}
1260+
}
1261+
1262+
return await this.extensionManager.reloadExtension(filePath, project);
1263+
}
1264+
12531265
async updateExtension(extensionId: string, repositoryUrl: string, projectDir?: string): Promise<ExtensionOperationResult> {
12541266
let project: Project | undefined = undefined;
12551267
if (projectDir) {

src/main/extensions/extension-manager.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,36 @@ export class ExtensionManager {
623623
logger.debug(`[Extensions] Unloaded extension: ${filePath}`);
624624
}
625625

626+
async reloadExtension(filePath: string, project?: Project): Promise<boolean> {
627+
const extension = this.findExtensionByPath(filePath);
628+
if (!extension) {
629+
logger.warn(`[Extensions] Cannot reload: extension not found at ${filePath}`);
630+
return false;
631+
}
632+
633+
logger.info(`[Extensions] Reloading extension: ${extension.metadata.name} (${filePath})`);
634+
635+
const hadUIComponents = extension.instance.getUIComponents !== undefined;
636+
637+
await this.unloadExtension(filePath);
638+
const { success, hasUIComponents } = await this.loadAndInitializeExtension(filePath, project);
639+
640+
if (success && (hasUIComponents || hadUIComponents)) {
641+
this.eventManager.sendExtensionUIRefresh({
642+
projectDir: project?.baseDir,
643+
reloadComponents: true,
644+
});
645+
}
646+
647+
const providers = this.getProviders(project);
648+
if (providers.length > 0) {
649+
this.modelManager.registerExtensionProviders(providers);
650+
}
651+
652+
this.debouncedNotifyListeners();
653+
return success;
654+
}
655+
626656
private findExtensionByPath(filePath: string): LoadedExtension | undefined {
627657
const extensions = this.registry.getExtensions();
628658
return extensions.find((ext) => ext.filePath === filePath);

src/main/ipc-handlers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ export const setupIpcHandlers = (eventsHandler: EventsHandler, serverController:
328328
return await eventsHandler.updateExtension(extensionId, repositoryUrl, projectDir);
329329
});
330330

331+
ipcMain.handle('reload-extension', async (_, filePath: string, projectDir?: string) => {
332+
return await eventsHandler.reloadExtension(filePath, projectDir);
333+
});
334+
331335
ipcMain.handle('get-extension-ui-components', (_, placement?: string, projectDir?: string, taskId?: string) => {
332336
return eventsHandler.getUIComponents(placement, projectDir, taskId);
333337
});

src/main/server/rest-api/extensions-api.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ const UpdateExtensionSchema = z.object({
3636
projectDir: z.string().optional(),
3737
});
3838

39+
const ReloadExtensionSchema = z.object({
40+
filePath: z.string().min(1, 'File path is required'),
41+
projectDir: z.string().optional(),
42+
});
43+
3944
export class ExtensionsApi extends BaseApi {
4045
constructor(private readonly eventsHandler: EventsHandler) {
4146
super();
@@ -147,6 +152,26 @@ export class ExtensionsApi extends BaseApi {
147152
}),
148153
);
149154

155+
// Reload extension
156+
router.post(
157+
'/extensions/reload',
158+
this.handleRequest(async (req, res) => {
159+
const parsed = this.validateRequest(ReloadExtensionSchema, req.body, res);
160+
if (!parsed) {
161+
return;
162+
}
163+
164+
const { filePath, projectDir } = parsed;
165+
const success = await this.eventsHandler.reloadExtension(filePath, projectDir);
166+
167+
if (success) {
168+
res.status(200).json({ message: 'Extension reloaded successfully', success: true });
169+
} else {
170+
res.status(500).json({ message: 'Failed to reload extension', success: false });
171+
}
172+
}),
173+
);
174+
150175
// Get extension UI components
151176
router.get(
152177
'/extensions/ui-components',

src/preload/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ const api: ApplicationAPI = {
134134
uninstallExtension: (extensionId: string, projectDir?: string) => ipcRenderer.invoke('uninstall-extension', extensionId, projectDir),
135135
updateExtension: (extensionId: string, repositoryUrl: string, projectDir?: string) =>
136136
ipcRenderer.invoke('update-extension', extensionId, repositoryUrl, projectDir),
137+
reloadExtension: (filePath: string, projectDir?: string) => ipcRenderer.invoke('reload-extension', filePath, projectDir),
137138
getExtensionUIComponents: (placement?: string, projectDir?: string, taskId?: string) =>
138139
ipcRenderer.invoke('get-extension-ui-components', placement, projectDir, taskId),
139140
getUIExtensionData: (extensionId: string, componentId: string, projectDir?: string, taskId?: string) =>

0 commit comments

Comments
 (0)