- Início
- Documentation
- Nativos
- Internos de Shell, PTY, Processo e Teclas do Natives
Internos de Shell, PTY, Processo e Teclas do Natives
Este documento cobre as primitivas de execução/processo/terminal em @f5-sales-demo/pi-natives: shell, pty, ps e keys, utilizando os termos de arquitetura de docs/natives-architecture.md.
Arquivos de implementação
Seção intitulada “Arquivos de implementação”crates/pi-natives/src/shell.rscrates/pi-natives/src/shell/windows.rs(apenas Windows)crates/pi-natives/src/pty.rscrates/pi-natives/src/ps.rscrates/pi-natives/src/keys.rscrates/pi-natives/src/task.rs(comportamento de cancelamento compartilhado usado por shell/pty)packages/natives/src/shell/index.tspackages/natives/src/shell/types.tspackages/natives/src/pty/index.tspackages/natives/src/pty/types.tspackages/natives/src/ps/index.tspackages/natives/src/ps/types.tspackages/natives/src/keys/index.tspackages/natives/src/keys/types.tspackages/natives/src/bindings.ts
Responsabilidade das camadas
Seção intitulada “Responsabilidade das camadas”- Camada de wrapper/API TS (
packages/natives/src/*): pontos de entrada tipados, superfície de cancelamento (timeoutMs,AbortSignal) e ergonomia JS. - Camada do módulo Rust N-API (
crates/pi-natives/src/*): execução de processos shell/PTY, travessia/terminação de árvore de processos e análise de sequências de teclas. - Portão de validação (
native.ts, nível de arquitetura): garante que as exportações necessárias (Shell,executeShell,PtySession,killTree,listDescendants, auxiliares de teclas) existam antes que os wrappers sejam utilizados.
Subsistema Shell (shell)
Seção intitulada “Subsistema Shell (shell)”Modelo de API
Seção intitulada “Modelo de API”Dois modos de execução são expostos:
- Execução única via
executeShell(options, onChunk?). - Sessão persistente via
new Shell(options?)e depoisshell.run(...)repetidamente.
Ambos transmitem a saída através de um callback threadsafe e retornam { exitCode?, cancelled, timedOut }.
Criação de sessão e modelo de ambiente
Seção intitulada “Criação de sessão e modelo de ambiente”O Rust cria brush_core::Shell com:
- modo não interativo,
do_not_inherit_env: true,- reconstrução explícita do ambiente a partir do env do host,
- lista de exclusão para variáveis sensíveis ao shell (
PS1,PWD,SHLVL, exportações de funções bash, etc.).
Comportamento do ambiente da sessão:
ShellOptions.sessionEnvé aplicado uma vez na criação da sessão.ShellRunOptions.envtem escopo de comando (EnvironmentScope::Command) e é removido após cada execução.PATHé mesclado de forma especial no Windows com deduplicação case-insensitive.
Enriquecimento de PATH apenas no Windows (shell/windows.rs): caminhos descobertos do Git-for-Windows (cmd, bin, usr/bin) são adicionados se presentes e ainda não incluídos.
Ciclo de vida em tempo de execução e transições de estado
Seção intitulada “Ciclo de vida em tempo de execução e transições de estado”O shell persistente (Shell.run) utiliza esta máquina de estados:
- Idle/Não inicializado:
session: None. - Em execução: o primeiro
run()cria a sessão de forma lazy, armazena o tokencurrent_abort, executa o comando. - Concluído + keepalive: se o fluxo de controle da execução é
Normal,current_aborté limpo e a sessão é reutilizada. - Concluído + teardown: se o fluxo de controle está relacionado a loop/script/saída do shell (
BreakLoop,ContinueLoop,ReturnFromFunctionOrScript,ExitShell), a sessão é descartada (session: None). - Cancelado/Tempo esgotado: a tarefa de execução é cancelada, espera graciosa (2s), depois abort forçado; a sessão é descartada.
- Erro: a sessão é descartada.
O shell de execução única (executeShell) sempre cria e descarta uma sessão nova por chamada.
Comportamento de streaming/saída
Seção intitulada “Comportamento de streaming/saída”- Stdout/stderr são roteados para um pipe compartilhado e lidos concorrentemente.
- O leitor decodifica UTF-8 incrementalmente; sequências de bytes inválidas emitem chunks de substituição
U+FFFD. - Após a conclusão do processo, o dreno de saída possui guardas de idle/máximo (
250msidle,2smáximo) para evitar travamento em jobs em background mantendo descritores abertos.
Cancelamento, timeout e jobs em background
Seção intitulada “Cancelamento, timeout e jobs em background”CancelTokené construído a partir detimeoutMseAbortSignalopcional.- No cancelamento/timeout, o token de cancelamento do shell é acionado, depois a tarefa recebe uma janela graciosa de 2s antes do abort forçado.
- Se o cancelamento ocorre, jobs em background são terminados (
TERM, depoisKILLcom atraso) usando metadados de jobs do brush.
Comportamento de Shell.abort():
- aborta apenas o comando atualmente em execução para aquela instância de
Shell, - retorna sucesso sem efeito quando nada está em execução.
Comportamento de falha
Seção intitulada “Comportamento de falha”Erros comumente expostos incluem:
- falhas de inicialização da sessão (
Failed to initialize shell), - erros de cwd (
Failed to set cwd), - falhas de set/pop de env,
- falhas de fonte de snapshot,
- falhas de criação/clone de pipe,
- falha de execução (
Shell execution failed: ...), - falhas do wrapper de tarefa (
Shell execution task failed: ...).
Flags de cancelamento em nível de resultado:
- timeout ->
exitCode: undefined,timedOut: true. - sinal de abort ->
exitCode: undefined,cancelled: true.
Subsistema PTY (pty)
Seção intitulada “Subsistema PTY (pty)”Modelo de API
Seção intitulada “Modelo de API”new PtySession() expõe:
start(options, onChunk?) -> Promise<{ exitCode?, cancelled, timedOut }>write(data)resize(cols, rows)kill()
Ciclo de vida em tempo de execução e transições de estado
Seção intitulada “Ciclo de vida em tempo de execução e transições de estado”Máquina de estados do PtySession:
- Idle:
core: None. - Reservado:
start()instala o canal de controle sincronamente (core: Some) antes do trabalho assíncrono começar, entãowrite/resize/killse tornam imediatamente válidos. - Em execução: loop PTY bloqueante trata estado do filho, eventos do leitor, heartbeat de cancelamento e mensagens de controle.
- Terminal fechado: saída do filho + conclusão do leitor.
- Finalizado:
coreé sempre redefinido paraNoneapós a conclusão da tarefa start (sucesso ou erro).
Guarda de concorrência:
- iniciar enquanto já está em execução retorna
PTY session already running.
Padrões de spawn/attach/write/read/terminate
Seção intitulada “Padrões de spawn/attach/write/read/terminate”- PTY é aberto via
portable_pty::native_pty_system().openpty(...). - O comando atualmente executa como
sh -lc <command>comcwde substituições de env opcionais. write()envia bytes brutos para o stdin do PTY.resize()limita as dimensões (cols 20..400,rows 5..200) e chama o redimensionamento do master.kill()marca a execução como cancelada e mata o processo filho.
Caminho de saída:
- thread de leitura dedicada lê o stream master,
- decodificação UTF-8 incremental com substituição
U+FFFDem bytes inválidos, - chunks encaminhados através de callback threadsafe N-API.
Semântica de cancelamento e timeout
Seção intitulada “Semântica de cancelamento e timeout”timeoutMseAbortSignalalimentam umCancelToken.- O loop chama
ct.heartbeat()periodicamente; abort aciona kill do filho. - A classificação de timeout é baseada em string (substring
"Timeout"no erro do heartbeat).
Comportamento de falha
Seção intitulada “Comportamento de falha”Superfícies de erro incluem:
- falha de alocação/abertura do PTY,
- falha de spawn do PTY,
- falha de aquisição do writer/reader,
- falhas de status/espera do filho,
- envenenamento de lock,
- desconexão do canal de controle (
PTY session is no longer available).
Falhas de chamadas de controle quando não está em execução:
write/resize/killretornamPTY session is not running.
Subsistema de árvore de processos (ps)
Seção intitulada “Subsistema de árvore de processos (ps)”Modelo de API
Seção intitulada “Modelo de API”killTree(pid, signal) -> numberlistDescendants(pid) -> number[]
O wrapper TS também registra a integração nativa de kill-tree nos utilitários compartilhados via setNativeKillTree(native.killTree).
Implementação específica por plataforma
Seção intitulada “Implementação específica por plataforma”- Linux: lê recursivamente
/proc/<pid>/task/<pid>/children. - macOS: usa
libprocproc_listchildpids. - Windows: captura snapshot da tabela de processos com
CreateToolhelp32Snapshot, constrói mapa pai->filhos, termina comOpenProcess(PROCESS_TERMINATE)+TerminateProcess.
Comportamento do kill-tree
Seção intitulada “Comportamento do kill-tree”- Descendentes são coletados recursivamente.
- A ordem de kill é de baixo para cima (descendentes mais profundos primeiro) para reduzir re-parenteamento de órfãos.
- O pid raiz é morto por último.
- O valor de retorno é a contagem de terminações bem-sucedidas.
Comportamento de sinal:
- POSIX: o
signalfornecido é passado parakill. - Windows:
signalé ignorado; a terminação é incondicional.
Comportamento de falha
Seção intitulada “Comportamento de falha”Este módulo é intencionalmente não-lançador na superfície de API:
- branches de árvore de processos ausentes/inacessíveis são ignorados,
- falhas de kill por pid são contadas como malsucedidas (não como erros),
- ausência de resultado tipicamente produz
[]delistDescendantse0dekillTree.
Subsistema de análise de teclas (keys)
Seção intitulada “Subsistema de análise de teclas (keys)”Modelo de API
Seção intitulada “Modelo de API”Auxiliares expostos:
parseKey(data, kittyProtocolActive)matchesKey(data, keyId, kittyProtocolActive)parseKittySequence(data)matchesKittySequence(data, expectedCodepoint, expectedModifier)matchesLegacySequence(data, keyName)
Modelo de análise
Seção intitulada “Modelo de análise”O parser combina:
- mapeamentos diretos de byte único (
enter,tab,ctrl+<letra>, ASCII imprimível), - busca de sequência de escape legada O(1) (mapa PHF),
- análise xterm
modifyOtherKeys, - análise do protocolo Kitty (
CSI u,CSI ~,CSI 1;...<letra>), - normalização para IDs de tecla (
ctrl+c,shift+tab,pageUp,f5, etc.).
Tratamento de modificadores:
- apenas bits de shift/alt/ctrl são comparados para correspondência de teclas,
- bits de lock são mascarados antes das comparações.
Comportamento de layout:
- o fallback de layout base é intencionalmente restrito para que layouts remapeados não criem correspondências falsas para letras/símbolos ASCII.
Comportamento de falha
Seção intitulada “Comportamento de falha”- Sequências não reconhecidas ou inválidas produzem
nullnas funções de análise. - Funções de correspondência retornam
falseem caso de falha de análise ou incompatibilidade. - Nenhuma superfície de erro lançado para entrada de tecla malformada.
Mapeamento API do wrapper JS ↔ exportação Rust
Seção intitulada “Mapeamento API do wrapper JS ↔ exportação Rust”Shell + PTY + Processo
Seção intitulada “Shell + PTY + Processo”| API do wrapper TS | Exportação Rust N-API | Notas |
|---|---|---|
executeShell(options, onChunk?) | executeShell (execute_shell) | Execução de shell única |
new Shell(options?) | classe Shell | Sessão de shell persistente |
shell.run(options, onChunk?) | Shell::run | Reutiliza sessão em fluxo de controle keepalive |
shell.abort() | Shell::abort | Aborta execução ativa daquela instância de shell |
new PtySession() | classe PtySession | Sessão PTY com estado |
pty.start(options, onChunk?) | PtySession::start | Execução PTY interativa |
pty.write(data) | PtySession::write | Passagem direta de stdin bruto |
pty.resize(cols, rows) | PtySession::resize | Dimensões de terminal limitadas |
pty.kill() | PtySession::kill | Força o kill do filho PTY ativo |
killTree(pid, signal) | killTree (kill_tree) | Terminação de árvore de processos filhos primeiro |
listDescendants(pid) | listDescendants (list_descendants) | Listagem recursiva de descendentes |
| API do wrapper TS | Exportação Rust N-API | Notas |
|---|---|---|
matchesKittySequence(data, cp, mod) | matchesKittySequence (matches_kitty_sequence) | Correspondência de codepoint+modificador Kitty |
parseKey(data, kittyProtocolActive) | parseKey (parse_key) | Parser de key-id normalizado |
matchesLegacySequence(data, keyName) | matchesLegacySequence (matches_legacy_sequence) | Verificação exata no mapa de sequência legada |
parseKittySequence(data) | parseKittySequence (parse_kitty_sequence) | Resultado de análise Kitty estruturado |
matchesKey(data, keyId, kittyProtocolActive) | matchesKey (matches_key) | Correspondedor de tecla de alto nível |
Notas sobre limpeza de sessões abandonadas e finalização
Seção intitulada “Notas sobre limpeza de sessões abandonadas e finalização”- Sessão persistente de Shell: se uma execução é cancelada/tempo esgotado/erro/fluxo de controle não-keepalive, o Rust descarta explicitamente o estado interno da sessão. Execuções normais bem-sucedidas mantêm a sessão para reutilização.
- Sessão PTY:
coreé sempre limpo apósstart()terminar, incluindo caminhos de falha. - Nenhum contrato explícito de kill dirigido por finalizador JS é exposto pelos wrappers; a limpeza está primariamente vinculada aos caminhos de conclusão/cancelamento da execução. Chamadores devem usar
timeoutMs,AbortSignal,shell.abort()oupty.kill()para teardown determinístico.