Mac Developer Bridge

Dê ao ChatGPT um terminal real no seu Mac: shell, arquivos, sessões PTY reais, tarefas em segundo plano e histórico do Codex somente leitura via MCP.

Documentação

Mac Developer Bridge

Dê ao ChatGPT um terminal real no seu Mac.

CI License: MIT

O Mac Developer Bridge transforma uma conversa do ChatGPT na camada de raciocínio para o seu Mac real. Ele pode executar comandos de shell, editar arquivos, iniciar sessões interativas de terminal, gerenciar trabalhos de longa duração, ler threads do Codex armazenadas sem iniciar outra rodada do modelo Codex e, opcionalmente, operar suas abas reais do Chrome conectado em segundo plano, sem roubar o foco.

Mac Developer Bridge showing ChatGPT reasoning through MCP into shell, PTY sessions, Codex history, and a live Mac

Exemplo: "Encontre a sessão do Codex em que eu estava trabalhando ontem, inspecione o repositório ao vivo, corrija o CI, envie o resultado e me diga o que mudou."

Esse é o tipo de fluxo de trabalho para o qual este projeto foi criado.

[!WARNING] O Mac Developer Bridge deliberadamente dá a um cliente MCP as permissões efetivas do seu usuário macOS. Ele não é isolado em sandbox e não possui lista de permissões de comandos ou caminhos. Leia SECURITY.md antes de ativá-lo.

A ideia

O ChatGPT tem o raciocínio. O seu Mac tem o código-fonte, terminal, credenciais, ferramentas de build, serviços locais e trabalho em andamento. O Mac Developer Bridge conecta os dois via MCP sem adicionar outro modelo ou loop de agente no meio.

flowchart LR
    A[ChatGPT] -->|MCP| B[Mac Developer Bridge]
    B --> C[Shell, Git and local CLIs]
    B --> D[Filesystem]
    B --> E[Real PTY sessions]
    B --> F[Background jobs]
    B --> G[Stored Codex history]
    B --> H[Audit log and kill switch]

A ponte em si não faz nenhuma chamada de modelo da OpenAI. Ela expõe ferramentas locais determinísticas; o ChatGPT fornece o raciocínio. As ferramentas de histórico do Codex usam métodos codex app-server somente leitura e nunca chamam turn/start.

O que isso possibilita

  • Recuperar uma thread do Codex armazenada, inspecionar o repositório ao qual ela se refere e continuar o trabalho a partir do ChatGPT.
  • Executar testes, builds, Git, gerenciadores de pacotes, CLIs de banco de dados, AppleScript e outras ferramentas já instaladas no seu Mac.
  • Manter shells interativos e programas de terminal vivos através de um PTY real, em vez de fingir que o stdin é um terminal.
  • Iniciar trabalhos locais de longa duração, inspecionar seus logs depois e interromper todo o grupo de processos.
  • Ler e modificar arquivos em qualquer lugar que seu usuário macOS possa acessar.
  • Opcionalmente, operar páginas aprovadas no seu perfil real do Chrome conectado sem trazer o Chrome para o primeiro plano.

Isso é intencionalmente diferente de um agente de codificação local. Não há um segundo loop de raciocínio. O ChatGPT continua sendo o agente; o Mac é o ambiente de execução.

Início rápido

Para uma conta pessoal do ChatGPT, o aplicativo da barra de menus é o caminho mais fácil. Você precisa de macOS, Node.js 18+, cloudflared, um hostname/túnel e o modo Desenvolvedor do ChatGPT.

git clone https://github.com/alexanderradahl/mac-developer-bridge.git
cd mac-developer-bridge
./menubar/build.sh
open /Applications/MacDevBridge.app

Use Iniciar e depois Copiar configuração do ChatGPT no aplicativo da barra de menus. A configuração detalhada de OAuth e Cloudflare está em Conectando ao ChatGPT e DEPLOY.md.

Usuários de Workspace que têm acesso ao OpenAI Secure MCP Tunnel podem usar install.sh em vez disso. Veja Transports.

Quer ver o que pedir para ele fazer? Comece com os fluxos de trabalho de copiar e colar.

Se isso for útil, dê uma estrela no repositório para que outros desenvolvedores possam encontrá-lo. Se você construir algo interessante com ele, compartilhe o fluxo de trabalho exato em O que você está fazendo o ChatGPT fazer no seu Mac?.

Este é um projeto open-source independente e não é um produto oficial da OpenAI ou Cloudflare. OpenAI, ChatGPT, Codex e Cloudflare são marcas registradas de seus respectivos proprietários.

Open source

O Mac Developer Bridge é lançado sob a Licença MIT. Relatórios de bugs e pull requests focados são bem-vindos; veja CONTRIBUTING.md. Relatórios sensíveis à segurança devem seguir as orientações em SECURITY.md em vez de serem postados publicamente.

Capacidades

  • Comandos de shell arbitrários através de /bin/zsh -lc, sob o usuário macOS conectado
  • Trabalhos em segundo plano destacados com logs persistentes de stdout/stderr, inspeção de status e término de grupo de processos
  • Leitura, escrita, anexação, listagem, stat, cópia, movimentação, chmod, symlink, mkdir e exclusão recursiva de arquivos sem restrições
  • Aplicação de diff unificado através de git apply
  • Descoberta e leitura de threads do Codex armazenadas sem retomar uma thread ou iniciar uma rodada do modelo Codex
  • Recuperação paginada de turnos do Codex para históricos grandes demais para uma única resposta
  • Auditoria local em JSONL
  • Conectividade privada somente de saída através do OpenAI Secure MCP Tunnel, ou um front-end de loopback HTTP simples que o Cloudflare Tunnel publica via HTTPS
  • Persistência por usuário através de um LaunchAgent do macOS
  • Trava de desbloqueio com falha segura: bridge.mjs relê o arquivo de desbloqueio antes de cada chamada de ferramenta, então removê-lo recusa a próxima chamada e encerra — a menos que o processo tenha herdado MAC_DEV_BRIDGE_FULL_ACCESS_ACK, que ignora o arquivo completamente
  • Interruptor de desligamento local (scripts/disable.sh), que interrompe o front-end, a ponte, o host nativo opcional do Chrome em segundo plano, grupos de trabalhos shell_start destacados, sessões interativas de pty e servidores MCP filhos federados, verificando os mesmos alvos que sinalizou

Git, gerenciadores de pacotes, Vercel CLI, CLIs de banco de dados, AppleScript, CLIs de navegador, ferramentas de build e outros programas instalados permanecem acessíveis através de shell_exec; a ponte deliberadamente não mantém lista de permissões de comandos.

Ferramentas

FerramentaFinalidade
bridge_statusIdentidade de runtime, caminhos, contexto de permissões, shell, modo de auditoria, binário do Codex, política de foco e status do Chrome em segundo plano
chrome_workspace_statusInspecionar o grupo do Chrome MDB de propriedade da extensão, atividade de lease e pool reutilizável de abas em segundo plano; nenhuma concessão de site necessária
chatgpt_extension_statusInspecionar a extensão do ChatGPT instalada no Chrome, o registro do host nativo da OpenAI e o status ao vivo da ponte de página somente leitura sem modificar a extensão da OpenAI
chatgpt_conversation_startIniciar ou continuar experimentalmente uma conversa exata do ChatGPT através da ação de runtime de primeira parte da página conectada; sem digitação/clique na UI ou exportação de credenciais
chrome_workspace_setupProvisionar um alvo de pool MDB somente de crescimento de 1 a 32 abas; o padrão é oito, com criação adiada até o Chrome estar naturalmente em foco
chrome_tabsListar abas no perfil real do Chrome conectado sem ativar o Chrome; escopo limitado apenas quando Aprovações estritas está ativado
chrome_openAlugar uma aba ociosa do grupo persistente MDB e abrir uma URL sem criar uma nova aba
chrome_navigateNavegar em uma aba aprovada sem selecioná-la
chrome_snapshotLer texto visível e elementos interativos de uma aba aprovada
chrome_clickClicar em um elemento em uma aba aprovada sem trazer o Chrome para o primeiro plano
chrome_fillPreencher inputs, textareas, selects ou campos contenteditable em segundo plano
chrome_closeLiberar uma aba de workspace MDB de volta ao pool ocioso, ou fechar uma aba em segundo plano que não seja de workspace
shell_execExecutar qualquer comando de shell em primeiro plano, opcionalmente com cwd, env, stdin, timeout e limite de saída
shell_startIniciar um processo destacado de longa duração
shell_job_statusInspecionar estado de execução e caudas de log
shell_job_listListar metadados persistentes de trabalhos
shell_job_killSinalizar um grupo de processos em segundo plano
fs_readLer texto ou base64 com paginação por deslocamento
fs_writeSubstituição atômica, criação, anexação ou escrita binária
fs_listListagem de diretório recursiva ou não recursiva
fs_statMetadados lstat e alvo de symlink
fs_managemkdir, remover, mover, copiar, chmod ou symlink
apply_patchAplicar ou verificar um diff unificado com git apply
codex_thread_readLer uma thread do Codex armazenada sem retomá-la
codex_thread_listPesquisar e paginar threads do Codex armazenadas
codex_thread_turns_listPaginar turnos armazenados com itens completos, resumidos ou omitidos
audit_tailLer a cauda de auditoria local da ponte

Chrome em segundo plano sem roubar o foco

No macOS, a integração opcional do Navegador em Segundo Plano opera o mesmo perfil do Chrome conectado que você já usa, então as sessões de site existentes funcionam, mas a automação rotineira acontece através de uma pequena extensão local em vez de automação de UI via AppleScript ou seleção de página via Chrome DevTools Protocol. O host nativo é vinculado no momento da instalação ao perfil/conta do Chrome selecionado e recusa um perfil desconectado ou incompatível.

Isso é intencionalmente opcional porque o controle autenticado do navegador é poderoso. Instale o host nativo uma vez e depois carregue a extensão descompactada uma vez no Chrome:

./scripts/install-background-chrome.sh

Depois, no Chrome, abra chrome://extensions, ative o Modo desenvolvedor, escolha Carregar sem compactação e selecione o diretório chrome-extension/ deste repositório. O id esperado da extensão é pcebfblnmcappinbenkmddjdapaoajgm.

A extensão mantém um grupo de abas nativo do Chrome chamado MDB. Por padrão, ela tem como alvo oito abas ociosas de propriedade da extensão, com um máximo rígido de 32. Elas são criadas apenas enquanto a janela existente do Chrome MDB já está naturalmente em primeiro plano, e depois são alugadas e reutilizadas para trabalho rotineiro. O grupo é recolhido quando ocioso e se expande enquanto uma ou mais abas estão alugadas. Isso preserva o limite de não roubo de foco em torno de uma peculiaridade do macOS/Chrome medida neste projeto: até mesmo chrome.tabs.create({ active:false }) pode trazer o Chrome para o primeiro plano.

O pool se auto-repara e tem um alvo de capacidade persistente somente de crescimento. chrome_workspace_setup(pool_size=16) registra um alvo de 16 abas imediatamente. Se a janela do Chrome MDB já estiver em foco, as abas ausentes são criadas e agrupadas de uma vez; caso contrário, o status relata a contagem pendente e a extensão se expande no próximo foco natural do Chrome. Uma solicitação posterior menor nunca fecha abas existentes. Quando todas as abas atuais estão alugadas e nenhuma expansão está pendente, a pressão aumenta o alvo em quatro, até 32, e tenta a criação imediata apenas quando o Chrome já está em foco. O MDB nunca ativa o Chrome para satisfazer o provisionamento manual ou automático.

chrome_workspace_status é sem concessão porque apenas lê o estado local do workspace de propriedade da extensão. Ele relata os tamanhos atual e alvo do pool, o máximo de 32 abas, o passo de crescimento automático de quatro abas, capacidade pendente, metadados de idade/ociosidade do lease, o timeout de recuperação de ociosidade de 10 minutos e o orçamento de espera de lease de 20 segundos. chrome_workspace_setup também é sem concessão: o provisionamento é sempre aceito localmente, enquanto a criação real permanece adiada quando o Chrome não está em foco. Chamadores legados/internos de tabs.open são roteados para o mesmo caminho de lease de workspace.open, então não podem criar abas soltas fora de MDB. Quando todas as abas estão ocupadas, chrome_open primeiro provisiona ou cria capacidade onde for seguro, depois espera brevemente por uma liberação; leases abandonados são recuperados após 10 minutos sem atividade do navegador, enquanto cada navegação/captura/clique/preenchimento renova um lease ativo.

Acesso relaxado é o padrão. Trabalho HTTP/HTTPS normal, incluindo localhost e portas não padrão, através do perfil do Chrome MDB conectado não exige um comando de aprovação no terminal ou lista de permissões por site. Isso é intencional: o Mac Developer Bridge já expõe autoridade irrestrita de shell/arquivo como o usuário macOS conectado, e o padrão útil é que a execução do navegador corresponda a esse nível de confiança escolhido pelo operador, permanecendo em segundo plano.

A aprovação relaxada não relaxa o roteamento do Chrome. O controle direto do Chrome através de shell_exec/shell_start — AppleScript, JXA, execuções diretas do executável do Chrome ou open de shell de uma URL HTTP/HTTPS (incluindo open -g) — é sempre recusado com CHROME_BACKGROUND_REQUIRED, tanto no modo Relaxado quanto no Estrito. O trabalho no navegador deve usar as ferramentas chrome_* e o grupo gerenciado MDB. Isso mantém o comportamento de não roubo de foco estrutural em vez de depender de qual modo de aprovação está selecionado.

Se você quiser um fluxo de trabalho mais restrito de navegador/app, ative Aprovações estritas no aplicativo da barra de menus do Mac Developer Bridge. A alternância é ao vivo; nenhum reinício é necessário. No modo Estrito, as aprovações de chrome-background são aditivas e compartilhadas em cada sessão do ChatGPT conectada à ponte até que cada concessão expire:

./scripts/approve-personal-browser.sh \
  --provider chrome-background \
  --url-pattern 'https://www.producthunt.com/*' \
  --url-pattern 'https://www.reddit.com/*' \
  --ttl 900

Um fluxo de trabalho normal é:

  1. chrome_open uma URL aprovada em uma aba ociosa alugada do grupo MDB.
  2. chrome_snapshot para ler a página e obter seletores suficientemente estáveis para os controles visíveis.
  3. chrome_fill / chrome_click / chrome_navigate conforme necessário.
  4. chrome_close para retornar a aba do workspace à sua página de extensão ociosa e liberar o aluguel. A liberação do workspace é uma limpeza local/sem concessão, portanto, concessões de URL em modo Strict não podem prender um aluguel concluído.

A vinculação de perfil é sempre aplicada. No modo relaxado, a extensão permite sites HTTP/HTTPS normais sem uma concessão por site. No modo Strict, cada aprovação chrome-background é armazenada como seu próprio arquivo com modo 0600 em $DATA_DIR/chrome-background-grants/, expira após no máximo 15 minutos e é mesclada com outras aprovações ainda ativas. Arquivos expirados são removidos automaticamente e os padrões de URL são aplicados dentro do Chrome. Provedores federados de navegador pessoal mantêm seu comportamento separado de uso único.

chatgpt_extension_status é deliberadamente somente leitura. Ele relata a versão instalada da extensão ChatGPT do Chrome, o registro local do host nativo com.openai.codexextension e—quando uma aba chatgpt.com já está aberta—o status ao vivo retornado pela ponte de página da própria OpenAI. O MDB não modifica a extensão da OpenAI, não se adiciona à lista de permissões do host nativo da OpenAI, não expõe chamadas RPC privadas arbitrárias da OpenAI nem abre programaticamente o painel lateral do ChatGPT. A extensão atual do ChatGPT não declara externally_connectable; seu caminho de abertura do painel lateral também exige um gesto de usuário confiável.

Início experimental de conversa com ChatGPT

chatgpt_conversation_start é um experimento deliberadamente restrito para iniciar ou continuar uma conversa de consumidor com o ChatGPT a partir do Codex/Work Mode. Seu transporte padrão runtime aluga uma nova aba de fundo chatgpt.com, resolve a ação da loja de primeira parte montada do ChatGPT submitComposer por meio de uma impressão digital semântica fixa e envia o prompt como um text_action. Ele não digita nem clica no compositor. O próprio runtime do ChatGPT constrói a solicitação privada e seu material de requisitos/prova gerado pelo navegador. O MDB observa o fluxo inicial limitado/entrega renderizada, recarrega a conversa retornada exata e lê o id exato da mensagem do assistente persistida antes de retornar o texto. Esse recarregamento é verificação, não uma segunda submissão de modelo. A aba nunca é ativada e é liberada para o pool do MDB depois.

A ferramenta aceita prompt, transport opcional (runtime por padrão ou raw de diagnóstico explícito), model opcional (padrão gpt-5-6-pro), thinking_effort opcional, max_runtime_seconds opcional (30–3600), continue_in_work opcional, conversation_id exato opcional para continuação e um tab_id alugado existente opcional. O modelo de runtime selecionado deve reter o esforço suportado solicitado antes da submissão; continue_in_work aplica-se apenas a diagnósticos brutos. O transporte bruto retém a sonda de solicitação privada direta anterior e ainda pode falhar com CHATGPT_CONVERSATION_REQUIREMENTS_UNAVAILABLE. Nenhum transporte aceita autorização copiada, cookie, dispositivo, Sentinel, Turnstile, Arkose ou campos de prova, e nenhum os retorna ou persiste. Os registros de auditoria de prompt retêm apenas o comprimento em bytes e um prefixo curto de SHA-256.

Orquestradores locais podem invocar a mesma operação por meio de uma rota separada:

curl --fail-with-body \
  --request POST \
  --url http://127.0.0.1:8787/experimental/chatgpt/conversation \
  --header "Authorization: Bearer $MAC_DEV_BRIDGE_HTTP_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"prompt":"Start a new research conversation","transport":"runtime","model":"gpt-5-6-pro","thinking_effort":"standard"}'

# Continue the exact returned conversation later:
curl --fail-with-body \
  --request POST \
  --url http://127.0.0.1:8787/experimental/chatgpt/conversation \
  --header "Authorization: Bearer $MAC_DEV_BRIDGE_HTTP_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"prompt":"Continue the assigned work","conversation_id":"<returned-conversation-id>"}'

Essa rota aceita apenas uma conexão direta de loopback com o bearer estático do MDB. Ela rejeita credenciais OAuth e solicitações encaminhadas/tuneladas, retorna Cache-Control: no-store e encapsula a operação MCP exata em vez de expor o socket do host nativo do Chrome.

Modelo de navegador experimental do ChatGPT

O MDB também expõe um adaptador separado de bearer estático e loopback direto POST /v1/responses para os modelos explícitos do OpenCodex chatgpt-runtime/chatgpt-browser e chatgpt-runtime/chatgpt-sol. O primeiro usa o runtime de navegador padrão fixo. A entrada Sol ativa o modelo gpt-5-6-thinking montado do ChatGPT e mapeia os níveis de esforço do Codex como low -> low, medium -> standard, high -> high, xhigh -> max, max -> max e ultra -> max. O adaptador reconstrói cada turno sem estado a partir das instruções/entrada do Responses, apresenta ferramentas suportadas function e custom ao runtime do ChatGPT por meio de um protocolo de decisão JSON fixo e converte uma mensagem validada ou seleção de ferramenta de volta para a saída canônica JSON/SSE do Responses. O Codex permanece responsável por executar ferramentas locais e reproduzir seus resultados no próximo turno.

O Codex pode representar ferramentas de forma livre, como exec, como uma ferramenta personalizada ou como uma função fechada com uma string obrigatória input. O adaptador aceita apenas a variação exata do wrapper determinístico para essas duas formas equivalentes e rejeita chamadas mistas ou mais amplas. Antes de analisar a decisão JSON, o MDB verifica a mensagem exata do assistente persistida da conversa retornada do ChatGPT, para que fragmentos de renderização transitórios não possam acionar novas tentativas de transporte.

Este modelo é intencionalmente separado e experimental. Ele não é adicionado ao modelo padrão, combos, padrões de subagente ou cadeias de fallback automáticas. A declaração web_search hospedada na API do Codex é omitida para este provedor em vez de bloquear cada turno do Work mode; o ChatGPT ainda pode usar sua própria navegação de primeira parte de forma independente. Outras ferramentas hospedadas, imagens/áudio, persistência de resposta no servidor, rastros de raciocínio nativos e contabilidade confiável de tokens não são suportados. Texto simples não JSON é retornado como mensagem final e nunca pode invocar uma ferramenta; saída malformada semelhante a JSON/ferramenta, seleção de ferramenta desconhecida de propriedade do chamador, desvio de runtime, prompt superdimensionado ou submissão ambígua falha de forma fechada sem nova tentativa ou automação de UI.

Exemplo de solicitação direta:

curl --fail-with-body \
  --request POST \
  --url http://127.0.0.1:8787/v1/responses \
  --header "Authorization: Bearer $MAC_DEV_BRIDGE_HTTP_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"model":"chatgpt-browser","input":"Reply with exactly MDB_MODEL_OK","stream":false}'

O registro local do OpenCodex usa um provedor personalizado openai-responses apontado para http://127.0.0.1:8787, com allowPrivateNetwork: true, statelessResponses: true, o bearer estático do MDB como chave de API do provedor e ids de modelo personalizados chatgpt-browser e chatgpt-sol. Seus ids qualificados visíveis ao Codex são chatgpt-runtime/chatgpt-browser e chatgpt-runtime/chatgpt-sol. Ambas as linhas do catálogo usam a janela de 372 mil tokens e o limite de compactação automática de 334,8 mil de gpt-5.6-sol; o MDB permite até 4.000.000 de bytes UTF-8 para o prompt serializado do runtime do navegador, de modo que o valor maior do catálogo é respaldado por um limite real de transporte.

Para manter esses turnos experimentais fora do histórico geral do ChatGPT, coloque um id de Projeto em um arquivo de runtime privado:

{"projectId":"g-p-..."}

Salve-o como $DATA_DIR/chatgpt-runtime.json com modo 0600. Quando configurado, cada nova conversa de runtime de navegador /v1/responses aluga a página de primeira parte desse Projeto e submete somente após a rota carregada e o conversationMode montado identificarem o Projeto exato. Cookies capturados, autorização, Sentinel, dispositivo, sessão e campos de prova não são necessários nem aceitos.

O que o modo de fundo não promete: CAPTCHAs, diálogos de permissão nativos do navegador/SO, seletores de arquivos, downloads que exigem um gesto de usuário confiável, chaves de acesso e outra UI de segurança do navegador podem exigir uma etapa em primeiro plano/manual. A ponte relata essa limitação em vez de ativar silenciosamente o Chrome. Isso também é deliberadamente mais restrito do que JavaScript de página arbitrário ou captura de cabeçalhos de rede: a resolução de runtime é fixa, caminhos de módulo/scripts/seletores fornecidos pelo chamador são rejeitados e um runtime alterado ou ambíguo falha sem uma segunda submissão; consulte SECURITY.md.

Para remover a integração:

./scripts/uninstall-background-chrome.sh

Aplicativos de desktop e foco

Para aplicativos nativos do macOS, o MDB ainda prefere APIs com capacidade de fundo ou caminhos web, pois a automação por Accessibility/AppleScript de aplicativos como o Slack pode exigir que o aplicativo de destino se torne o primeiro plano. No modo relaxado padrão, o controle de aplicativos nativos não Chrome é permitido sem uma aprovação separada de terminal, então o MDB ainda pode concluir a tarefa quando uma interação de aplicativo em primeiro plano for genuinamente necessária. O Chrome é a exceção: como o MDB tem uma extensão de fundo dedicada com sessão iniciada, a automação direta da GUI do Chrome é sempre forçada de volta ao caminho do navegador MDB em vez de permitir roubar o foco.

Prefira, nesta ordem:

  1. uma API ou conector MCP para o serviço;
  2. o aplicativo web do serviço por meio do grupo Chrome MDB com sessão iniciada;
  3. automação de GUI de aplicativo nativo somente quando a interação em primeiro plano for genuinamente necessária.

Quando Aprovações Strict está habilitado, o controle de aplicativos nativos em primeiro plano é bloqueado, a menos que o operador crie uma concessão de uso único, com escopo de aplicativo:

./scripts/approve-foreground-gui.sh --app Slack --ttl 60

O modo Strict é opcional e desativado por padrão. A caixa de seleção na barra de menu o altera ao vivo.

Sessões de terminal interativas

Um pty real, alocado por lib/ptyhelper.pl (Perl central, sem dependência adicionada). Anunciado somente quando o auxiliar é executado neste host; caso contrário, as seis ferramentas estão ausentes em vez de quebradas.

FerramentaFinalidade
pty_startIniciar um programa em um terminal real e retornar um id de sessão
pty_readLer a transcrição a partir de um cursor de bytes, opcionalmente com long-polling
pty_writeEnviar teclas, incluindo caracteres de controle
pty_resizeAlterar o tamanho da janela, confirmado por uma leitura de volta do kernel
pty_signalEnviar sinal ao grupo de processos da sessão
pty_closeEncerrar a sessão e recuperá-la

Limites que serão visíveis no uso normal:

  • Comprimento de linha. Enquanto o terminal está no modo canônico — o padrão, e o que todo prompt interativo usa — a disciplina de linha descarta uma linha de entrada de 1024 bytes ou mais em vez de truncá-la. pty_write recusa tal escrita com PTY_WRITE_CANON_LIMIT em vez de relatar bytes que o programa nunca verá. Os bytes se acumulam entre chamadas até um \r ou \n, então o particionamento não o contorna. Envie linhas de no máximo 1023 bytes. Uma sessão que colocou seu terminal em modo bruto é verificada e permitida.
  • Concorrência. O limite de sessões é tomado, não apenas verificado, então chamadas pty_start concorrentes não podem excedê-lo.
  • Retenção. Cada sessão mantém os últimos MAC_DEV_BRIDGE_PTY_RING_BYTES de saída em um anel fixo; pty_read relata lostBytes quando um cursor fica para trás dele.
  • Contenção. Consulte SECURITY.md — pty_close relata leaderGroupGone, ttyProcessesKilled e uncontainedPids separadamente, e containmentVerified é verdadeiro somente quando nada sobreviveu.

Servidores MCP filhos federados

Se um registro de provedor estiver configurado, as ferramentas de cada provedor são anunciadas com um prefixo key__tool e encaminhadas. Não há provedor embutido: o registro é fornecido pelo operador. O modo de perfil de navegador pessoal exige uma concessão do operador por uso — consulte SECURITY.md.

Ambiente da ponte

Estes são lidos por bridge.mjs em ambos os transportes.

VariávelPadrãoFinalidade
MAC_DEV_BRIDGE_DATA_DIR~/Library/Application Support/MacDeveloperBridgeEstado, metadados de jobs, raízes de federação.
MAC_DEV_BRIDGE_LOG_DIR~/Library/Logs/MacDeveloperBridgeDiretório de logs.
MAC_DEV_BRIDGE_AUDIT_LOG$LOG_DIR/audit.jsonlCaminho do JSONL de auditoria.
MAC_DEV_BRIDGE_AUDIT_MODEmetadataoff, metadata ou full. full registra argumentos de ferramentas; veja a ressalva em SECURITY.md.
MAC_DEV_BRIDGE_UNLOCK_FILE$DATA_DIR/FULL_ACCESS_ENABLEDA trava de desbloqueio revogável. Releia antes de cada chamada de ferramenta.
MAC_DEV_BRIDGE_UNLOCK_RECHECK_MS3000Com que frequência a trava é relida enquanto uma sessão pty ou um filho federado existe e o cliente está silencioso. Limita por quanto tempo qualquer um pode sobreviver a um arquivo de desbloqueio removido.
MAC_DEV_BRIDGE_SHELLshell de loginShell usado para shell_exec/shell_start.
MAC_DEV_BRIDGE_DEFAULT_OUTPUT_BYTES1000000Limite padrão de saída por chamada.
MAC_DEV_BRIDGE_MAX_OUTPUT_BYTES8000000Teto que uma chamada pode solicitar.
MAC_DEV_BRIDGE_PTY_PERL/usr/bin/perlInterpretador para o auxiliar pty.
MAC_DEV_BRIDGE_PTY_HELPERlib/ptyhelper.pl ao lado de bridge.mjsCaminho do script auxiliar.
MAC_DEV_BRIDGE_PTY_MAX_SESSIONS8 (1–64)Limite de sessões ativas. kern.tty.ptmx_max é 511 em todo o sistema, então isso protege o Terminal.app do próprio operador, não apenas este processo.
MAC_DEV_BRIDGE_PTY_RING_BYTES262144 (4 KiB–4 MB)Retenção de saída por sessão. A retenção total é este valor vezes o limite de sessões.
MAC_DEV_BRIDGE_PTY_IDLE_TIMEOUT_MS900000 (1 s–1 h)Janela de recuperação ociosa, e um teto: pty_start pode solicitar uma mais curta, nunca uma mais longa. O valor efetivo de uma sessão ativa está em bridge_status.
MAC_DEV_BRIDGE_PTY_MAX_LIFETIME_MS28800000 (5 s–24 h)Teto rígido, aplicado mesmo em uma sessão em uso ativo.
MAC_DEV_BRIDGE_PTY_START_TIMEOUT_MS5000Quanto tempo pty_start espera pelo auxiliar para relatar um pty real.
MAC_DEV_BRIDGE_MCP_SERVERS—Caminho para um arquivo JSON de registro de provedores MCP filhos.
MAC_DEV_BRIDGE_MCP_SERVERS_JSON—O mesmo registro inline. Tem precedência.
MAC_DEV_BRIDGE_MCP_START_DEADLINE_MS15000 (1 s–120 s)Teto de tempo de parede para a inicialização inteira de um provedor — handshake, verificação de concessão e cada página de tools/list. Um provedor que exceder é abandonado em vez de segurar a superfície de ferramentas.
MAC_DEV_BRIDGE_MCP_PING_IDLE_MS30000Intervalo ocioso após o qual um filho federado é pingado; um filho que falhar no ping é tratado como travado e reiniciado.
MAC_DEV_BRIDGE_PERSONAL_APPROVAL_FILE$DATA_DIR/PERSONAL_BROWSER_APPROVEDCaminho legado/federado de concessão de navegador pessoal de uso único. Uma concessão legada de chrome-background aqui é importada para o pool compartilhado para compatibilidade retroativa.
MAC_DEV_BRIDGE_BACKGROUND_CHROME_GRANT_DIR$DATA_DIR/chrome-background-grantsDiretório de concessões aditivas e expiráveis de URLs de Chrome em segundo plano, compartilhadas entre todas as sessões e recarregadas após reinícios da ponte.
MAC_DEV_BRIDGE_SETTINGS_FILE$DATA_DIR/settings.jsonConfigurações do operador. strictApprovals assume o padrão de false quando o arquivo/chave está ausente. O app da barra de menus gerencia isso.
MAC_DEV_BRIDGE_FOREGROUND_GUI_APPROVAL_FILE$DATA_DIR/FOREGROUND_GUI_APPROVEDAprovação de GUI em primeiro plano, de uso único e restrita ao app, em modo estrito.
MAC_DEV_BRIDGE_CHROME_SOCKET$DATA_DIR/chrome-background.sockSocket Unix entre bridge.mjs e o host opcional de mensagens nativas do Chrome. Modo 0600 dentro do diretório de dados modo-0700.
MAC_DEV_BRIDGE_CHROME_NATIVE_PID_FILE$DATA_DIR/chrome-native-host.pidRegistro de PID usado pelo interruptor de segurança para o host nativo opcional do Chrome.
MAC_DEV_BRIDGE_FULL_ACCESS_ACK—Forma de ambiente do reconhecimento. Não revogável; veja abaixo.

O que "acesso total" significa

O servidor MCP roda com as permissões efetivas da conta macOS que o inicia. Ele não tem lista de permissões de caminhos, lista de permissões de comandos de shell, sandbox ou portão interno de aprovação por comando.

O macOS ainda aplica controles de privacidade TCC, Acesso Total ao Disco, ACLs, SIP, controles de acesso ao Keychain e autenticação sudo. Chamadas de shell MCP não interativas não fornecem magicamente uma senha de sudo ou uma interface de terminal. Configure sudo sem senha apenas quando você deliberadamente quiser essa escalada separada.

A ponte se recusa a iniciar até que exista um reconhecimento deliberado, e o re-verifica antes de cada chamada de ferramenta — então remover o arquivo de reconhecimento tanto impede futuros inícios quanto para uma ponte em execução na sua próxima chamada.

A forma de ambiente (MAC_DEV_BRIDGE_FULL_ACCESS_ACK) é deliberadamente não revogável dessa maneira: uma ponte que a herdou nunca lê o arquivo, então deletar o arquivo não a para. Os passos de Instalação abaixo exportam essa variável, então uma ponte iniciada de tal shell só pode ser parada parando o processo. O app da barra de menus a remove de seus filhos exatamente por esse motivo.

As permissões de ações do ChatGPT e o comportamento de confirmação são separados. O servidor MCP anuncia anotações de escrita e destrutivas honestamente e não pode contornar restrições impostas pelo produto ou workspace do ChatGPT.

Transportes

A ponte fala MCP sobre stdio. Dois transportes podem levá-la ao ChatGPT.

OpenAI Secure MCP Tunnel (install.sh, documentado abaixo) é somente de saída e não precisa de endpoint público. Requer o tipo de conexão Tunnel no diálogo de plugin do ChatGPT, que não está disponível em contas pessoais — a opção aparece, mas está desabilitada.

Cloudflare Tunnel + Server URL (mcp-http.mjs) é o fallback quando Tunnel não está disponível. mcp-http.mjs fica na frente da ponte com Streamable HTTP em 127.0.0.1:8787 atrás de OAuth 2.1 (e um bearer estático para outros clientes), e cloudflared o publica:

export MAC_DEV_BRIDGE_HTTP_TOKEN="$(openssl rand -hex 32)"
node mcp-http.mjs

O diálogo de plugin do ChatGPT oferece Autenticação: OAuth, No Auth ou Mixed — não há campo de chave de API/bearer. mcp-http.mjs portanto implementa um servidor de autorização OAuth 2.1 também, e é assim que você conecta o ChatGPT. Veja Conectando ao ChatGPT abaixo. O token bearer estático ainda funciona para qualquer cliente que possa enviar um cabeçalho Authorization: Bearer.

O host está fixado em loopback e o caminho para /mcp, deliberadamente — o único peer pretendido é cloudflared na mesma máquina.

Ambiente:

VariávelPadrãoFinalidade
MAC_DEV_BRIDGE_HTTP_TOKEN—Token bearer. Mínimo de 24 bytes, ASCII imprimível. Recusa-se a iniciar sem um.
MAC_DEV_BRIDGE_HTTP_TOKEN_FILE—Leia o token de um arquivo modo-0600 em vez disso, mantendo-o fora de ps eww. Tem precedência.
MAC_DEV_BRIDGE_HTTP_PORT8787Porta de loopback.
MAC_DEV_BRIDGE_HTTP_TIMEOUT_MS600000Teto por requisição, para chamadas longas de shell_exec.
MAC_DEV_BRIDGE_CHATGPT_RESPONSES_BROWSER_MODELgpt-5-6-proSubstituição de compatibilidade para a entrada fixa de chatgpt-browser. A entrada Sol sempre usa gpt-5-6-thinking.
MAC_DEV_BRIDGE_CHATGPT_RESPONSES_MAX_RUNTIME_SECONDS600Teto de runtime de navegador por turno para /v1/responses, limitado a 30–3600 segundos.
MAC_DEV_BRIDGE_CHATGPT_RUNTIME_CONFIG_FILE$DATA_DIR/chatgpt-runtime.jsonConfiguração JSON modo-0600 contendo o projectId opcional do ChatGPT.
MAC_DEV_BRIDGE_CHATGPT_RESPONSES_PROJECT_ID—Substituição direta opcional de Project-id. O arquivo de configuração privado é preferido para configuração local persistente.
MAC_DEV_BRIDGE_ENTRYbridge.mjs ao lado de mcp-http.mjsCostura somente para teste para substituir uma ponte simulada. Alterá-lo significa que scripts/disable.sh não reconhecerá o filho.
MAC_DEV_BRIDGE_PUBLIC_URLderivado de HostFixa o emissor OAuth. Fixe-o: Host é controlável pelo cliente, e o emissor deve corresponder ao que o cliente descobriu.
MAC_DEV_BRIDGE_OAUTH_CLIENT_IDgeradoO id de cliente colado no ChatGPT. Estável entre reinícios.
MAC_DEV_BRIDGE_OAUTH_REDIRECT_URIS—Callbacks extras de correspondência exata, separados por vírgula. Acrescenta aos embutidos.
MAC_DEV_BRIDGE_OAUTH_CLIENT_SECRET—Segundo fator opcional em /token, aplicado via client_secret_post ou client_secret_basic. Coloque o mesmo valor no campo OAuth Client Secret do ChatGPT. Removido dos ambientes filhos.
MAC_DEV_BRIDGE_BODY_IDLE_TIMEOUT_MS30000Descarta uma requisição cujo corpo estagna por esse tempo. Ocioso, não total, então um upload lento mas em progresso não é truncado.
MAC_DEV_BRIDGE_MAX_BUFFERED_BYTES100663296 (96 MiB)Orçamento global para corpos de requisição em buffer. Excedê-lo descarrega carga com um 503 retryable.

Entenda a diferença na exposição antes de escolher este. O transporte Tunnel faz apenas conexões de saída. Este publica um endpoint HTTPS que fica na frente de acesso irrestrito ao shell, com um único token bearer como toda a barreira. Gire o token se ele for divulgado, e considere Cloudflare Access na frente dele para um segundo fator.

Ainda não automatizado para este transporte: install.sh requer tunnel-client e rejeita um id de tunnel_... ausente, então não pode instalar o caminho HTTP, e não há LaunchAgent — nada reinicia mcp-http.mjs ou cloudflared após um reinício ou uma falha. scripts/doctor.sh cobre este transporte. uninstall.sh remove os arquivos, mas não para um front end em execução.

Conectando ao ChatGPT

O diálogo de plugin do ChatGPT oferece três escolhas de Autenticação — OAuth, No Auth, Mixed — e nenhum campo de chave de API/bearer, então o token bearer estático não tem onde ser inserido. mcp-http.mjs portanto implementa um servidor de autorização OAuth 2.1, e é assim que o ChatGPT conecta.

Preencha o diálogo da seguinte forma:

CampoValor
ConnectionServer URL
Server URLhttps://<hostname>/mcp
AuthenticationOAuth
Registration methodUser-Defined OAuth Client
OAuth Client IDregistrado no início, ou defina MAC_DEV_BRIDGE_OAUTH_CLIENT_ID
OAuth Client Secretdeixe em branco
Token endpoint auth methodnone
Default scopesmcp
OIDC enableddesmarque

O Copy ChatGPT Setup do app da barra de menus produz esta lista pré-preenchida.

Desmarque OIDC porque /.well-known/openid-configuration é servido apenas como um alias dos metadados OAuth e deliberadamente omite todos os campos de assinatura e assunto. Nenhum token de ID é emitido, então um cliente estrito de OIDC deve abortar em vez de exigir um.

O ChatGPT então abre uma página de consentimento servida pela sua própria máquina. Ela nomeia o callback exato para o qual redirecionará e pede o token da ponte, que é como sabe que a aprovação veio de você. Leia a linha "Will redirect to" antes de aprovar — qualquer caminho /connector/oauth/<token> é um conector válido do ChatGPT, incluindo um criado por outra pessoa.

Use um túnel Cloudflare nomeado. O hostname de um túnel rápido muda a cada início, e esse hostname é o emissor OAuth — então um reinício entre descoberta e callback faz o emissor parar de corresponder ao que o ChatGPT registrou, e um cliente estrito descarta o callback silenciosamente. Um túnel nomeado também significa criar o conector uma vez em vez de a cada execução.

Endpoints servidos: /.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server, /.well-known/openid-configuration mais /.well-known/oauth-protected-resource/mcp, /.well-known/oauth-authorization-server/mcp, /.well-known/openid-configuration/mcp e /mcp/.well-known/openid-configuration — sete caminhos no total, já que a forma prefixada com /mcp/ existe apenas para openid-configuration. Então /authorize, /token, /revoke, /revoke-all e /healthz. Um 401 de /mcp carrega WWW-Authenticate: Bearer resource_metadata="…", que é o que permite a um cliente descobrir o resto.

App da barra de menus (transporte HTTP)

menubar/ constrói um pequeno app de barra de status AppKit que possui os dois processos que este transporte precisa e expõe as três coisas que você realmente usa: a URL pública, o token bearer e se o endpoint está respondendo.

./menubar/build.sh          # also installs a copy to /Applications
open /Applications/MacDevBridge.app

A compilação instala em /Applications (com fallback para ~/Applications) porque Launchpad e Spotlight não exibem apps que vivem em ~/Downloads. O bundle localiza mcp-http.mjs via MAC_DEV_BRIDGE_HOME, depois um pacote ao lado de si mesmo, depois um caminho embutido em Info.plist no momento da compilação — então a cópia instalada ainda encontra o pacote.

O menu dá a você: status atual, o modo de túnel, Copy Server URL, Copy OAuth Client ID, Copy ChatGPT Setup (o diálogo inteiro preenchido, em ordem), Copy Bearer Token, Start/Stop, uma caixa de seleção ao vivo Strict approvals (desligada por padrão), Rotate Token, Open Logs e Quit. Ele prefere um túnel nomeado do Cloudflare quando ~/.cloudflared/config.yml declara um, fornecendo uma URL estável — caso contrário, um túnel rápido, cujo hostname muda a cada início e força o conector do ChatGPT a ser recriado toda vez. O menu mostra qual modo está ativo.

Por que vale a pena usar em vez dos comandos brutos:

  • Ele é o supervisor. Iniciar gera mcp-http.mjs e cloudflared; Parar e Sair interrompem exatamente o que ele iniciou, em vez de descobrir processos pelo nome.
  • Iniciar grava o arquivo de desbloqueio e Parar o remove, então a interrupção é à prova de falhas através do bloqueio por chamada de bridge.mjs, não apenas uma eliminação de processo.
  • O token vive em um arquivo com modo 0600 e é passado por MAC_DEV_BRIDGE_HTTP_TOKEN_FILE, mantendo-o fora de ps eww.
  • O status é consultado de /healthz e da vivacidade dos processos filhos, então um filho que morre é relatado em vez de ser presumido como ativo.
  • Na inicialização, ele recupera órfãos — ambos os filhos. applicationWillTerminate não é executado em uma saída forçada, falha ou reinicialização abrupta, então uma execução anterior pode deixar o arquivo de desbloqueio armado, o front end servindo e cloudflared ainda publicando um hostname público. A inicialização desarma o bloqueio e interrompe o que estiver registrado em mcp-http.pid e cloudflared.pid, cada um verificado por identidade primeiro porque pids são reciclados e esses arquivos sobrevivem a SIGKILL e reinicializações. Recuperar apenas o front end anteriormente deixava um ingresso público que nenhuma execução posterior podia fechar, e que o próximo Iniciar rearmaria junto com um segundo túnel.
  • Ele nunca passa MAC_DEV_BRIDGE_FULL_ACCESS_ACK para seus filhos. Essa variável é um desbloqueio permanente em bridge.mjs, então herdá-la tornaria Parar incapaz de revogar qualquer coisa — e a documentação de instalação diz para você exportá-la.
  • Um filho que morre interrompe o outro. Relatar uma falha enquanto deixa o irmão vivo deixava cloudflared publicando com o bloqueio ainda armado e o menu lendo "não está em execução".
  • Os filhos herdam o PATH do shell de login, então shell_exec se comporta da mesma forma que em um terminal (um aplicativo iniciado pela GUI não tem nvm ou Homebrew).

O aplicativo é assinado ad-hoc e não é notarizado. Ele localiza mcp-http.mjs via MAC_DEV_BRIDGE_HOME, depois um pacote ao lado do bundle, depois um caminho embutido em Info.plist no momento da compilação — então a cópia /Applications funciona com o pacote deixado onde está. Recompile após mover o pacote para que o caminho embutido permaneça correto. MAC_DEV_BRIDGE_HOME.

Ele não substitui scripts/disable.sh: trabalhos shell_start destacados sobrevivem ao front end por design, e apenas esse script os recupera do registro de trabalhos.

Pré-requisitos

Ambos os transportes:

  1. macOS e um usuário de desktop conectado.
  2. Node.js 18 ou mais recente.
  3. Modo de desenvolvedor do ChatGPT.
  4. Uma CLI codex funcional apenas para as três ferramentas de histórico do Codex. O acesso a shell e sistema de arquivos não depende do Codex.

O OpenAI Secure MCP Tunnel adicionalmente requer:

  1. O binário oficial tunnel-client, baixado de OpenAI Platform Tunnels ou do lançamento oficial do OpenAI no GitHub, executável e disponível em PATH ou em ~/.local/bin/tunnel-client.
  2. Um ID de túnel OpenAI com escopo para o workspace do ChatGPT que o usará.
  3. Uma chave de API em tempo de execução cujo principal tenha Tunnels Read + Use.
  4. A opção de conexão Tunnel no diálogo do plugin do ChatGPT.

Cloudflare Tunnel + Server URL adicionalmente requer:

  1. cloudflared, autenticado em uma conta Cloudflare.
  2. Um hostname que você controla, ou uma URL de túnel rápido.
  3. openssl para gerar o token de portador.
  4. A opção de conexão Server URL com OAuth — veja Conectando ao ChatGPT. Não há modo No-Auth: /mcp é codificado sem substituição, e mcp-http.mjs se recusa a iniciar sem um token.

A disponibilidade do modo de desenvolvedor é controlada pelo rollout da conta e pela política do workspace. Se a alternância do modo de desenvolvedor estiver ausente, este pacote não pode substituir essa limitação do lado do produto. A opção Tunnel especificamente não está disponível em contas pessoais — ela é renderizada, mas desabilitada — que é o motivo pelo qual o transporte HTTP existe.

Instalação

Clone o repositório (ou baixe um lançamento/arquivo) e abra o Terminal em sua pasta:

git clone https://github.com/alexanderradahl/mac-developer-bridge.git
cd mac-developer-bridge

Depois instale:

chmod +x install.sh uninstall.sh bridge.mjs scripts/*.sh

export MAC_DEV_BRIDGE_FULL_ACCESS_ACK='I_UNDERSTAND_THIS_GRANTS_FULL_ACCESS'
export CONTROL_PLANE_TUNNEL_ID='tunnel_0123456789abcdef0123456789abcdef'

# Hidden input; the key is not placed in shell history.
read -r -s -p 'Tunnel runtime API key: ' CONTROL_PLANE_API_KEY; printf '\n'
export CONTROL_PLANE_API_KEY

./install.sh

unset CONTROL_PLANE_API_KEY MAC_DEV_BRIDGE_FULL_ACCESS_ACK

O instalador também pode solicitar o ID do túnel e a chave de tempo de execução quando executado interativamente. A chave de tempo de execução é armazenada no Keychain de login do macOS e não é gravada no pacote, perfil de túnel ou plist do LaunchAgent.

O instalador:

  1. Exige o reconhecimento exato de acesso total.
  2. Valida macOS, Node, tunnel-client, descoberta do Codex, formato do ID do túnel, modo de auditoria e shell.
  3. Copia a ponte para ~/.local/share/mac-developer-bridge.
  4. Cria ~/Library/Application Support/MacDeveloperBridge/FULL_ACCESS_ENABLED com modo 0600.
  5. Executa testes de sintaxe, protocolo MCP, sistema de arquivos, patch, processo, limpeza de segredos e adaptador Codex.
  6. Armazena a chave de tempo de execução no Keychain.
  7. Cria um perfil stdio tunnel-client único e executa tunnel-client doctor.
  8. Instala e inicia um LaunchAgent persistente por usuário.

Locais personalizados são suportados com as variáveis de ambiente MAC_DEV_BRIDGE_INSTALL_DIR, MAC_DEV_BRIDGE_BIN_DIR, MAC_DEV_BRIDGE_PLIST_DIR, MAC_DEV_BRIDGE_DATA_DIR e MAC_DEV_BRIDGE_LOG_DIR.

Conecte o ChatGPT

  1. Habilite o modo de desenvolvedor no ChatGPT.
  2. Abra Plugins do ChatGPT e crie um aplicativo em modo de desenvolvedor.
  3. Defina a conexão, por transporte:
    • Transporte Tunnel: escolha Tunnel, depois selecione ou cole o mesmo ID de túnel usado durante a instalação. Indisponível em contas pessoais — a opção é renderizada, mas desabilitada.
    • Transporte HTTP: escolha Server URL e insira https://<hostname>/mcp. A autenticação oferece apenas OAuth, No Auth ou Mixed — veja Conectando ao ChatGPT; o token de portador não tem campo neste diálogo.
  4. Revise e habilite as ferramentas, e marque o reconhecimento de risco.
  5. Inicie uma nova conversa no Chat, selecione o aplicativo e chame bridge_status.

Primeiro prompt sugerido:

Use only the Mac Developer Bridge app for local-machine operations.

First call bridge_status and report the effective user, home directory, shell, Codex binary, audit mode, and whether the tunnel runtime key was scrubbed from child command environments.

Then call codex_thread_read with:
{"thread_id":"019fa926-dbbd-7d72-aa0c-8edd41bd585c","include_turns":true}

If the result is too large, call codex_thread_turns_list in ascending order with items_view="full" and continue through nextCursor until the complete persisted history is recovered.

Do not invoke codex, codex exec, codex-reply, turn/start, or any OpenAI API from shell commands. The Chat conversation is the reasoning agent. Inspect the repository and branch referenced by the thread, report the current state, and continue the unfinished work.

Ask before production deployments, destructive database operations, credential changes, force pushes, or deleting user data.

codex_thread_read e codex_thread_turns_list usam APIs de leitura locais do codex app-server. A ponte não expõe nenhum método do Codex que inicie uma rodada de modelo.

Acesso Total ao Disco

Verifique o estado atual antes de adivinhar:

scripts/tcc-doctor.sh          # add --open to jump to the settings pane

Ele testa um caminho protegido por TCC como node e como $MAC_DEV_BRIDGE_SHELL — apenas esses dois — e relata quais têm a concessão. O Acesso Total ao Disco não pode ser concedido por um script — os bancos de dados TCC são protegidos por SIP, então não são graváveis mesmo como root, e tccutil só pode redefinir entradas. Um humano deve adicionar o binário em Ajustes do Sistema, ou um MDM deve enviar um perfil PPPC.

Se as leituras falharem com EPERM ou "Operação não permitida", conceda Acesso Total ao Disco aos executáveis reais na cadeia de tempo de execução:

  • o binário exato node mostrado por bridge_status
  • /bin/zsh
  • o binário instalado tunnel-client, apenas para o transporte Tunnel

cloudflared não precisa disso: ele apenas encaminha HTTP para loopback e nunca toca no sistema de arquivos em nome de uma ferramenta.

Um LaunchAgent pode não herdar permissões de privacidade anteriormente concedidas ao Terminal ou a uma instalação diferente do Node. O Acesso Total ao Disco é separado das permissões POSIX comuns.

Operações

Os caminhos ~/.local/share/mac-developer-bridge abaixo existem apenas se install.sh foi executado, o que requer tunnel-client — então no transporte HTTP esse diretório não existe e você executa os scripts do diretório do pacote extraído em vez disso.

Ambos os transportes:

# Full diagnostic report (includes the Full Disk Access check)
./scripts/doctor.sh                 # or ~/.local/share/mac-developer-bridge/scripts/doctor.sh

# Kill switch. Read its output; a non-zero exit means NOT contained.
./scripts/disable.sh

# Audit log
tail -f "$HOME/Library/Logs/MacDeveloperBridge/audit.jsonl"

Transporte HTTP:

# Logs (only populated if you redirected them, as DEPLOY.md step 2 does)
tail -f "$HOME/Library/Logs/MacDeveloperBridge/http.stderr.log"

# Restart: there is no LaunchAgent, so stop and re-run it.
# disable.sh REMOVES the unlock file, so it must be recreated — without this the
# front end starts and /healthz answers 200 while every tool call fails 503,
# because /healthz never spawns the bridge.
./scripts/disable.sh
printf 'I_UNDERSTAND_THIS_GRANTS_FULL_ACCESS\n' \
  > "$HOME/Library/Application Support/MacDeveloperBridge/FULL_ACCESS_ENABLED"
chmod 600 "$HOME/Library/Application Support/MacDeveloperBridge/FULL_ACCESS_ENABLED"
export MAC_DEV_BRIDGE_HTTP_TOKEN='<the same token the plugin uses>'
node mcp-http.mjs >>"$HOME/Library/Logs/MacDeveloperBridge/http.stderr.log" 2>&1 &

Transporte Tunnel:

launchctl print "gui/$(id -u)/com.openai.mac-developer-bridge-tunnel"
launchctl kickstart -k "gui/$(id -u)/com.openai.mac-developer-bridge-tunnel"

# Re-enable after an explicit acknowledgement (requires the LaunchAgent plist)
export MAC_DEV_BRIDGE_FULL_ACCESS_ACK='I_UNDERSTAND_THIS_GRANTS_FULL_ACCESS'
~/.local/share/mac-developer-bridge/scripts/enable.sh
unset MAC_DEV_BRIDGE_FULL_ACCESS_ACK

# Rotate the tunnel runtime key with hidden input
~/.local/share/mac-developer-bridge/scripts/rotate-tunnel-key.sh

tail -f "$HOME/Library/Logs/MacDeveloperBridge/tunnel.stderr.log"

enable.sh atualmente requer o plist do LaunchAgent, então não funciona no transporte HTTP. Para reativar lá, recrie o arquivo de desbloqueio e reinicie o front end como na Opção B do DEPLOY.md.

tunnel-client normalmente expõe endpoints de saúde de loopback e uma UI de operador em http://127.0.0.1:8080/healthz, /readyz, /metrics e /ui enquanto está em execução.

Auditoria

O modo padrão é metadata. Ele registra:

  • timestamp e nome da ferramenta
  • uma prévia editada dos argumentos
  • hash SHA-256 dos argumentos completos
  • um resumo compacto do resultado ou erro

No transporte HTTP não há instalação, e um aplicativo de barra de menus iniciado pela GUI não tem ambiente de shell para herdar — então as únicas maneiras de mudar o modo de auditoria lá são exportá-lo em um shell e iniciar mcp-http.mjs desse shell, ou iniciar o aplicativo com open -a MacDevBridge --env MAC_DEV_BRIDGE_AUDIT_MODE=full. Caso contrário, ele permanece em metadata.

Defina MAC_DEV_BRIDGE_AUDIT_MODE antes da instalação para um dos:

MAC_DEV_BRIDGE_AUDIT_MODE=off
MAC_DEV_BRIDGE_AUDIT_MODE=metadata
MAC_DEV_BRIDGE_AUDIT_MODE=full

full pode persistir argumentos de comandos sensíveis e conteúdo de arquivos mesmo após a edição comum de padrões de token. Trate o log de auditoria como sensível. A chave de tempo de execução do túnel é removida do ambiente do processo da ponte antes que qualquer ferramenta de shell ou sistema de arquivos possa ser executada, embora o acesso irrestrito ao shell ainda possa alcançar outras credenciais disponíveis para a conta macOS.

Verifique o roteamento de uso

A ponte em si não contém nenhum cliente de inferência OpenAI e os adaptadores do Codex chamam métodos somente leitura do servidor de aplicativos. Mesmo assim, verifique o comportamento específico da conta após a conexão:

  1. Registre o saldo atual de créditos Codex/Work.
  2. No Chat, chame apenas bridge_status e fs_stat em um caminho inofensivo.
  3. Atualize a página de uso do Codex/Work.
  4. Confirme que nenhum uso de modelo do Codex foi registrado.
  5. Depois leia o thread do Codex armazenado e continue o trabalho aqui.

Não use shell_exec para executar o Codex em si se o objetivo for evitar o uso de modelo do Codex.

Desinstalação

Do diretório do pacote extraído ou instalado:

./uninstall.sh

O desinstalador remove o LaunchAgent, a instalação da ponte, o symlink de comando, o arquivo de desbloqueio e a chave de tempo de execução do Keychain.

Ele não remove o diretório de dados, então estes sobrevivem a uma desinstalação — incluindo duas credenciais ativas:

  • http-token — o token de portador (modo 0600)
  • oauth-state.json — o ID do cliente OAuth mais os digests do token de acesso/atualização (modo 0600)
  • oauth-client-id, mcp-http.pid, cloudflared.pid, jobs/ e o log de auditoria

Exclua ~/Library/Application Support/MacDeveloperBridge também se quiser que as credenciais desapareçam. Ele também não interrompe um front end em execução; execute scripts/disable.sh primeiro.