/** The official Electron profile is owned by DSH's in-process plugin manager. */ import { randomUUID } from 'node:crypto' import { progress, TARGET_RE, type DesktopPluginRuntime, type InstallResult } from './dsh-cli.ts' interface ManagedResult { application: string error?: unknown packageResult?: { exitCode: number | null; output?: string } } export interface OfficialPluginManagerLike { installBundle(spec: string, options?: { requestId?: string }): Promise removeBundle(name: string): Promise cancelInstall(requestId: string): Promise } /** * What a user can actually do when the market cannot perform an operation on * the official Desktop profile: the app's own Plugins page runs the same * managed pipeline. Bilingual, because it is read in the operations panel. */ export const OFFICIAL_PAGE_HINT = '官方桌面客户端的这个插件操作需要在「设置 → 插件」里完成;市场无法通过命令行修改桌面端的 profile。 / On the official desktop app, do this from Settings → Plugins; the market cannot change the desktop profile through the command line.' function failure(message: string, exitCode = 1): InstallResult { return { exitCode, timedOut: false, stdout: '', stderr: message, cancelled: false } } function detail(error: unknown): string { if (typeof error === 'string') return error if (error instanceof Error) return error.message if (error === undefined) return 'plugin manager did not explain the failure' return JSON.stringify(error) } /** Never fall back to `dsh plugin --profile desktop`: that CLI is forbidden. */ export function createOfficialDesktopRuntime( managerLookup: () => OfficialPluginManagerLike | undefined, profileName: string, // Unused by the manager bridge — it takes the profile by NAME — and optional // because an explicitly configured `profile: desktop` may be the only thing // that names this profile, with no launcher directory to go with it (#744). _profileDirectory?: string, ): DesktopPluginRuntime { let disposed = false let active: { requestId?: string; cancelled: boolean; done: Promise } | undefined const runPlugin: DesktopPluginRuntime['runPlugin'] = (profile, argv) => { if (disposed) return Promise.resolve(failure('official desktop plugin runtime is disposed', 127)) if (profile !== profileName) return Promise.resolve(failure('refusing to change a different profile', 127)) if (active !== undefined) return Promise.resolve({ ...failure('another plugin operation is running', 127), busy: true }) const manager = managerLookup() if (manager === undefined || typeof manager.installBundle !== 'function' || typeof manager.removeBundle !== 'function' || typeof manager.cancelInstall !== 'function') { return Promise.resolve(failure(OFFICIAL_PAGE_HINT, 127)) } // Market sends pnpm argv. Only operations representable by the official // manager are accepted; silently dropping pnpm flags would change intent. const [command, target, ...extra] = argv if (extra.length !== 0 || typeof target !== 'string' || !TARGET_RE.test(target)) { // The market's own options are what usually lands here, and they are // spelled with a leading `-` — and they sit in the TARGET slot, because // the market writes `add