import * as posixPath from 'node:path/posix'; import * as win32Path from 'node:path/win32'; import { Emitter } from '#/_base/event'; import { IHostEnvironment } from '#/os/interface/hostEnvironment'; import { IHostFileSystem } from '#/os/interface/hostFileSystem'; import { IHostProcessService } from '#/os/interface/hostProcess'; import { IHostTerminalService } from '#/os/interface/terminal'; import type { Runtime, RuntimeCapability, RuntimePath, RuntimeStatus } from './runtime'; import type { RuntimeProviderAttachment, RuntimeProviderContext, RuntimeProviderFactory } from './runtimeProvider'; import type { RuntimeProviderHost } from './runtimeUnitHost'; let nextGeneration = 1; export class LocalRuntime implements Runtime { readonly identity; readonly capabilities: ReadonlySet; readonly environment; readonly path: RuntimePath; readonly workspace: Runtime['workspace']; readonly fs; readonly process; readonly terminal; private currentStatus: RuntimeStatus = 'ready'; private readonly statusEmitter = new Emitter(); readonly onDidChangeStatus = this.statusEmitter.event; constructor( workspaceId: string, environment: IHostEnvironment, fs: IHostFileSystem | undefined, process: IHostProcessService | undefined, terminal: IHostTerminalService | undefined, ) { this.identity = { workspaceId, runtimeId: 'local', generation: `local-${nextGeneration++}` }; const capabilities = new Set(); if (fs !== undefined) capabilities.add('fs'); if (process !== undefined) capabilities.add('process'); if (terminal !== undefined) capabilities.add('terminal'); this.capabilities = capabilities; this.environment = { osKind: environment.osKind, osArch: environment.osArch, osVersion: environment.osVersion, shellName: environment.shellName, shellPath: environment.shellPath, pathClass: environment.pathClass, homeDir: environment.homeDir, }; const path = environment.pathClass === 'win32' ? win32Path : posixPath; this.path = { separator: path.sep as '/' | '\\', delimiter: path.delimiter as ':' | ';', isAbsolute: (p) => path.isAbsolute(p), join: (...paths) => path.join(...paths), relative: (from, to) => path.relative(from, to), resolve: (...paths) => path.resolve(...paths), basename: (p) => path.basename(p), dirname: (p) => path.dirname(p), }; this.workspace = { mapRoots: (roots) => ({ workDir: path.resolve(roots.workDir), additionalDirs: roots.additionalDirs?.map((root) => path.resolve(root)), }), }; this.fs = fs; this.process = process; this.terminal = terminal; } get status(): RuntimeStatus { return this.currentStatus; } dispose(): void { this.currentStatus = 'disposed'; this.statusEmitter.fire('disposed'); this.statusEmitter.dispose(); } } export class LocalRuntimeProviderFactory implements RuntimeProviderFactory { readonly id = 'local'; readonly imports = { root: [ IHostEnvironment, IHostFileSystem, IHostProcessService, IHostTerminalService, ], imports: [], local: [], }; async attach(context: RuntimeProviderContext, host: RuntimeProviderHost): Promise { const handle = host.registerRuntime(new LocalRuntime( context.id, host.get(IHostEnvironment), host.get(IHostFileSystem), host.get(IHostProcessService), host.get(IHostTerminalService), )); return { dispose: () => handle.remove() }; } }