Exec
sdk.exec запускает исполняемые файлы на машине пользователя. Для этого требуется разрешение exec в манифесте — без него каждый вызов завершается ошибкой. Если разрешение объявлено, перед первым использованием пользователю показывается запрос (см. Permissions).
{ "contributes": { "permissions": ["exec"] } }
sdk.exec работает только в десктопном приложении — в веб-приложении каждый вызов завершается ошибкой. Каждая команда и её вывод также появляются в терминале задач, названным по id вашего расширения (терминал не открывается автоматически), если только пользователь не установил terminal.commandOutput в off.
execute — выполнить до конца
execute(executable: string, args?: readonly string[], options?: ExtensionExecOptions): Promise<ExtensionExecResult>
| Опция | Тип | По умолчанию | Описание |
| --- | --- | --- | --- |
| cwd | string | рабочая директория приложения | Рабочая директория. |
| env | Record<string, string> | — | Дополнительные переменные окружения поверх окружения приложения. На macOS и Linux PATH берётся из login shell пользователя. |
| timeout | number | без таймаута | Миллисекунды. При превышении процесс завершается, а вызов отклоняется. |
| maxBuffer | number | 10 МБ | Максимум байт, собираемых на поток (stdout, stderr). При превышении процесс завершается, а вызов отклоняется. |
| shell | string \| boolean | без shell | true запускает через shell по умолчанию; строка задаёт shell. |
| encoding | BufferEncoding | 'utf8' | Как декодируется вывод. |
Результат имеет тип ExtensionExecResult: { stdout, stderr, exitCode, duration }, duration указывается в миллисекундах. Ненулевой код выхода не приводит к отклонению вызова, нужно проверять exitCode вручную. Вызов отклоняется в случае, если:
- разрешение
execне объявлено или было отклонено; - приложение не десктопное;
- процесс не удаётся запустить (например, исполняемый файл не найден);
- превышен
timeoutилиmaxBuffer; - процесс завершён сигналом (в том числе через
kill()); - исполняемый файл или аргумент содержит сегмент пути
...
const log = await sdk.window.createLogOutputChannel('My Extension')
const result = await sdk.exec.execute('git', ['status', '--porcelain'], { encoding: 'utf-8' })
if (result.exitCode === 0) {
log?.info(result.stdout)
}
ExtensionExecOptions · ExtensionExecResult
interface ExtensionExecOptions {
cwd?: string
env?: Record<string, string>
timeout?: number // мс
maxBuffer?: number // байт на поток
shell?: string | boolean
encoding?: BufferEncoding
}
interface ExtensionExecResult {
stdout: string
stderr: string
exitCode: number
duration: number // мс
}
start — длительный процесс в потоковом режиме
start(executable: string, args?: readonly string[], options?: ExtensionExecOptions): ExtensionExecHandle
kill(pid: number): Promise<void>
start сразу возвращает дескриптор и передаёт вывод по мере его появления. Он принимает те же опции и завершается по тем же причинам, что и execute, ошибки передаются через свойство result.
| Элемент | Тип | Описание |
| --- | --- | --- |
| executionId | SdkExecExecutionId | Идентификатор этого запуска, доступен сразу. |
| pid | Promise<number> | Выполняется после старта процесса. Не выполняется никогда, если процесс не запустился. |
| onStdout / onStderr | Event<string> | Чанки вывода, декодированные по encoding. |
| result | Promise<SdkExecStartResponse> | { executionId, stdout, stderr, exitCode, duration } после завершения процесса. Отклоняется так же, как execute. |
| kill() | Promise<void> | Отправляет SIGTERM. До старта процесса отклоняется; после его завершения ничего не делает. Если процесс завершился из-за сигнала, result отклоняется. |
| dispose() | void | Отсоединяет только слушателей вывода — процесс продолжает работать, а result всё равно завершится. Слушатели также освобождаются, когда result завершается. |
sdk.exec.kill(pid) отправляет SIGTERM процессу, запущенному приложением. pid, который приложение не запускало, игнорируется.
const output = await sdk.window.createOutputChannel('npm install')
const handle = sdk.exec.start('npm', ['install'])
const sub = handle.onStdout((chunk) => output?.append(chunk))
const result = await handle.result
sub.dispose()
handle.dispose()
Процессы не завершаются при деактивации расширения — сохраняйте дескрипторы и завершайте процессы в deactivate:
import type { ExtensionExecHandle } from '@fragmenta/extension'
let server: ExtensionExecHandle | undefined
export function activate() {
server = sdk.exec.start('node', ['server.js'])
}
export async function deactivate() {
await server?.kill().catch(() => {}) // отклоняется, если процесс так и не запустился
}
ExtensionExecHandle · SdkExecStartResponse
type SdkExecExecutionId = string // branded-строка
interface ExtensionExecHandle {
readonly executionId: SdkExecExecutionId
readonly pid: Promise<number>
readonly onStdout: Event<string>
readonly onStderr: Event<string>
readonly result: Promise<SdkExecStartResponse>
kill(): Promise<void>
dispose(): void
}
interface SdkExecStartResponse {
executionId: SdkExecExecutionId
stdout: string
stderr: string
exitCode: number
duration: number // мс
}