[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
176
electron/plugins/pluginManager.cjs
Normal file
176
electron/plugins/pluginManager.cjs
Normal file
@@ -0,0 +1,176 @@
|
||||
"use strict";
|
||||
|
||||
class PluginManager {
|
||||
constructor(options) {
|
||||
this.database = options.database;
|
||||
this.packageStore = options.packageStore;
|
||||
this.runtimeSupervisor = options.runtimeSupervisor;
|
||||
this.contributionService = options.contributionService ?? {
|
||||
initialize: () => this.runtimeSupervisor.startEnabled(),
|
||||
onPluginDisabled() {},
|
||||
onPluginEnabled: (pluginId) => this.runtimeSupervisor.start(pluginId),
|
||||
};
|
||||
this.beforeClose = options.beforeClose ?? null;
|
||||
this.initialized = false;
|
||||
this.initializePromise = null;
|
||||
this.mutationTail = Promise.resolve();
|
||||
this.shuttingDown = false;
|
||||
this.shutdownPromise = null;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.initializePromise ??= this.#initialize();
|
||||
return this.initializePromise;
|
||||
}
|
||||
|
||||
async #initialize() {
|
||||
await this.packageStore.initialize();
|
||||
await this.contributionService.initialize();
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
async #ready() {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
#mutate(operation) {
|
||||
if (this.shuttingDown) return Promise.reject(new Error("Plugin manager is shutting down"));
|
||||
const result = this.mutationTail.then(operation, operation);
|
||||
this.mutationTail = result.then(() => undefined, () => undefined);
|
||||
return result;
|
||||
}
|
||||
|
||||
async list() {
|
||||
await this.#ready();
|
||||
return this.database.listPlugins();
|
||||
}
|
||||
|
||||
install(archivePath, options) {
|
||||
return this.#mutate(async () => {
|
||||
await this.#ready();
|
||||
let stoppedPlugin = null;
|
||||
let plugin;
|
||||
try {
|
||||
plugin = await this.packageStore.install(archivePath, {
|
||||
enable: options?.enable === true,
|
||||
beforeActivate: async ({ pluginId, previousPlugin }) => {
|
||||
if (!previousPlugin?.enabled) return;
|
||||
this.database.setEnabled(pluginId, false);
|
||||
this.contributionService.onPluginDisabled(pluginId);
|
||||
stoppedPlugin = { pluginId, version: previousPlugin.activeVersion };
|
||||
await this.runtimeSupervisor.stop(pluginId);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (stoppedPlugin) {
|
||||
const current = this.database.getActivePlugin(stoppedPlugin.pluginId);
|
||||
if (current?.activeVersion === stoppedPlugin.version) {
|
||||
this.database.setEnabled(stoppedPlugin.pluginId, true);
|
||||
try {
|
||||
await this.contributionService.onPluginEnabled(stoppedPlugin.pluginId);
|
||||
} catch {
|
||||
this.database.setEnabled(stoppedPlugin.pluginId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
if (plugin?.enabled) {
|
||||
try {
|
||||
await this.contributionService.onPluginEnabled(plugin.id);
|
||||
} catch (error) {
|
||||
this.database.setEnabled(plugin.id, false);
|
||||
this.contributionService.onPluginDisabled(plugin.id);
|
||||
if (stoppedPlugin && stoppedPlugin.version !== plugin.activeVersion) {
|
||||
try {
|
||||
this.database.setActiveVersion(stoppedPlugin.pluginId, stoppedPlugin.version, {
|
||||
enabled: true,
|
||||
expectedActiveVersion: plugin.activeVersion,
|
||||
});
|
||||
await this.contributionService.onPluginEnabled(stoppedPlugin.pluginId);
|
||||
} catch {
|
||||
const restored = this.database.getActivePlugin(stoppedPlugin.pluginId);
|
||||
if (restored?.activeVersion === stoppedPlugin.version && restored.enabled) {
|
||||
this.database.setEnabled(stoppedPlugin.pluginId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return plugin;
|
||||
});
|
||||
}
|
||||
|
||||
setEnabled(pluginId, enabled) {
|
||||
return this.#mutate(async () => {
|
||||
await this.#ready();
|
||||
if (enabled) {
|
||||
const plugin = this.database.getActivePlugin(pluginId);
|
||||
if (plugin?.runtime?.quarantinedAt != null) {
|
||||
this.database.clearQuarantine(pluginId, plugin.activeVersion);
|
||||
}
|
||||
this.database.setEnabled(pluginId, true);
|
||||
try { await this.contributionService.onPluginEnabled(pluginId); }
|
||||
catch (error) {
|
||||
this.database.setEnabled(pluginId, false);
|
||||
this.contributionService.onPluginDisabled(pluginId);
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
this.database.setEnabled(pluginId, false);
|
||||
this.contributionService.onPluginDisabled(pluginId);
|
||||
await this.runtimeSupervisor.stop(pluginId);
|
||||
}
|
||||
return this.database.getActivePlugin(pluginId);
|
||||
});
|
||||
}
|
||||
|
||||
restart(pluginId) {
|
||||
return this.#mutate(async () => {
|
||||
await this.#ready();
|
||||
await this.runtimeSupervisor.restart(pluginId);
|
||||
return this.database.getActivePlugin(pluginId);
|
||||
});
|
||||
}
|
||||
|
||||
uninstall(pluginId) {
|
||||
return this.#mutate(async () => {
|
||||
await this.#ready();
|
||||
const plugin = this.database.getActivePlugin(pluginId);
|
||||
if (plugin) {
|
||||
this.database.setEnabled(pluginId, false);
|
||||
this.contributionService.onPluginDisabled(pluginId);
|
||||
}
|
||||
await this.runtimeSupervisor.stop(pluginId);
|
||||
return this.packageStore.uninstall(pluginId);
|
||||
});
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
this.shutdownPromise ??= this.#shutdown();
|
||||
return this.shutdownPromise;
|
||||
}
|
||||
|
||||
async #shutdown() {
|
||||
this.shuttingDown = true;
|
||||
let supervisorError;
|
||||
const supervisorShutdown = Promise.resolve()
|
||||
.then(() => this.runtimeSupervisor.shutdown())
|
||||
.catch((error) => { supervisorError = error; });
|
||||
await this.mutationTail;
|
||||
if (this.initializePromise) {
|
||||
try { await this.initializePromise; } catch {}
|
||||
}
|
||||
await supervisorShutdown;
|
||||
const errors = supervisorError ? [supervisorError] : [];
|
||||
try { await this.beforeClose?.(); }
|
||||
catch (error) { errors.push(error); }
|
||||
try { this.database.close(); }
|
||||
catch (error) { errors.push(error); }
|
||||
if (errors.length === 1) throw errors[0];
|
||||
if (errors.length > 1) throw new AggregateError(errors, "Plugin host shutdown failed");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { PluginManager };
|
||||
Reference in New Issue
Block a user