mcpField
O
outport

outport

github.com/steveclarke开发24112 次安装5 天前更新
代码仓库

关于 outport

Outport 是开发服务端口管理命令行工具,面向需要在项目和 Git worktree 间管理端口与本地域名的开发者。它将分配结果及计算出的 URL 写入 .env,并可配置 .test 域名代理。

主要功能

  1. 01确定性分配且避免端口冲突
  2. 02将端口和计算值写入 .env
  3. 03为服务分配 .test 主机名
  4. 04自动区分 Git worktree 实例
  5. 05提供状态与 JSON 输出

适用场景

  1. 01为项目中的开发服务分配端口
  2. 02在 monorepo 的多个 .env 文件中同步配置
  3. 03并行运行多个 worktree 实例
  4. 04查看服务端口、健康状态和 URL

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/steveclarke/outport --skill outport

安装后在 Claude Code 中输入/outport确认已加载。

详细介绍

Outport — 开发端口管理器

Outport 为开发服务分配确定且互不冲突的端口,分配 .test 主机名,并将所有内容写入 .env 文件。每个框架都会读取 .env——Rails、Nuxt、Django、Docker Compose 等均如此,因此端口和 URL 无需手动配置即可使用。

快速参考

# Project commands
outport init              # Create outport.yml (interactive)
outport up                # Allocate ports, assign hostnames, write .env
outport up --force        # Clear and re-allocate all ports from scratch
outport down              # Remove ports and clean .env files

# Inspect & diagnose
outport status            # Show project status (ports, health, URLs)
outport status --computed # Include computed values
outport ports             # Show ports with live process info (PID, memory, uptime)
outport ports --all       # Full machine scan (Outport + non-Outport ports)
outport ports --down      # Include ports with no running process
outport ports kill <svc>  # Kill process by service name or port number
outport ports kill --orphans  # Kill all orphaned dev processes
outport open              # Open HTTP services in browser
outport open web          # Open a specific service
outport open admin        # Open a unique alias
outport open web:admin    # Open a specific service alias
outport share             # Tunnel HTTP services to public URLs
outport share web         # Tunnel a specific service
outport qr                # Show QR codes for mobile device access
outport qr --tunnel       # Show QR codes with tunnel URLs
outport doctor            # Check system health and project config

# System commands (machine-wide)
outport system start      # Install DNS, CA, and start the daemon
outport system stop       # Stop the daemon
outport system restart    # Re-write plist and restart the daemon
outport system status     # Show all registered projects
outport system status --check  # Show with health checks (up/down)
outport system prune      # Remove stale and duplicate registry entries
outport system uninstall  # Remove DNS resolver, daemon, and CA

# Instance management
outport rename <old> <new>  # Rename the current instance
outport promote             # Promote the current instance to main

所有命令都支持 --json,用于输出机器可读的信息。

设置新项目

1. 创建 outport.yml

运行 outport init 进行交互式设置,或手动创建:

name: my-project
services:
  web:
    env_var: PORT
  postgres:
    env_var: DB_PORT
  redis:
    env_var: REDIS_PORT
2. 运行 outport up

分配确定的端口(根据项目名称和服务名称进行哈希计算),并将其写入 .env。相同的输入始终会生成相同的端口。

3. 配置项目从 .env 读取

大多数框架都能原生读取 .env,或只需少量设置即可读取:

  • Docker Compose——自动读取 .env。在 compose.yml 中使用 ${DB_PORT:-5432}
  • Rails——使用 dotenv-rails gem,或在配置中引用环境变量: port: ENV.fetch("DB_PORT", 5432)
  • Nuxt——原生读取 .env。运行时配置值可通过 NUXT_* 环境变量覆盖
  • Foreman——自动读取 .env
  • Overmind——不会自动加载 .env。在启动脚本中加载它:
    if [ -f .env ]; then set -a; source .env; set +a; fi
    
4. 提交 outport.yml,将 .env 加入 gitignore

outport.yml 是项目配置——提交它,以便工作树和团队成员都能继承配置。.env 包含分配的端口——将其加入 gitignore。每个检出副本都会有自己的文件。

.test 域名(DNS 代理)

运行一次 outport system start,即可为服务启用易记的 .test 主机名。完成设置后,https://myapp.test 会路由到你的应用,而不是 http://localhost:24920。

工作原理

outport system start 会安装三个组件(配置 DNS 和 CA 信任需要 sudo):

  1. DNS 解析器——配置操作系统,将 *.test 查询发送到本地 15353 端口上的 DNS 服务器,该服务器会将所有 *.test 名称解析为 127.0.0.1。在 macOS 上,此配置位于 /etc/resolver/test;在 Linux 上,则是 systemd-resolved 的扩展配置。
  2. 反向代理——守护进程运行在 80 和 443 端口上,根据 Host 标头将请求路由到相应的服务端口,并在运行 outport up 时自动更新。WebSocket 连接会透明代理。在 macOS 上由 launchd 管理,在 Linux 上由 systemd 管理。
  3. 本地 CA——生成用于 HTTPS 的证书颁发机构。80 端口上的 HTTP 请求会通过 307 重定向到 HTTPS。
outport system start      # Install DNS + CA + daemon (one-time, prompts for sudo)
outport system stop       # Stop the daemon
outport system restart    # Re-write plist and restart the daemon
outport system uninstall  # Remove everything — reverse of start
配置 .test 主机名

为服务添加 hostname,即可为其分配 .test URL:

name: myapp
services:
  web:
    env_var: PORT
    hostname: myapp.test       # → https://myapp.test
  postgres:
    env_var: DB_PORT           # no hostname: port allocation only

主机名规则:

  • 必须包含项目名称(例如,myapp.test、app.myapp.test)
  • 非主实例会自动追加实例代码(请参阅多个实例)
Cookie 隔离

每个实例都有自己的 .test 主机名,因此浏览器 Cookie 和会话会自动隔离——无需使用无痕窗口:

myapp [main]   web → 24920   http://myapp.test
myapp [bkrm]   web → 28104   http://myapp-bkrm.test

配置参考

项目级字段
字段必填说明
name是项目标识符。用于端口分配和主机名生成。
open否服务名称列表,outport open 默认会打开这些服务。省略时,会打开所有配置了主机名的服务。
服务字段
字段必填说明
env_var是写入 .env 的环境变量名称
hostname否此服务的 .test 主机名(例如,myapp.test)。启用 HTTP。非主实例会追加实例代码。
aliases否命名的备用主机名(标签 → 主机名的映射)。每个别名都会路由到同一端口。需要配置 hostname。
subdomains否启用通配符子域名路由(*.myapp.test)。所有子域名都会路由到同一端口。需要配置 hostname。
preferred_port否优先尝试的端口。如果端口已被占用,则回退到基于哈希的分配方式
env_file否写入位置。可以是字符串或数组。默认为项目根目录下的 .env
写入多个 .env 文件

对于 monorepo,通常需要将端口写入多个 .env 文件。为 env_file 使用 数组:

services:
  rails:
    env_var: RAILS_PORT
    env_file:
      - backend/.env
      - frontend/.env          # Frontend needs this to construct API URLs

计算值

应用不仅需要端口号,还需要 URL。计算值会根据服务映射计算环境变量,并将最终值写入 .env。

基本语法
computed:
  API_URL:
    value: "${rails.url:direct}/api/v1"   # http://localhost:24920/api/v1
    env_file: frontend/.env
  CORS_ORIGINS:
    value: "${web.url}"                   # http://myapp.test (or localhost:PORT)
    env_file: backend/.env
  • ${service_name.field} 引用服务字段
  • env_file 为必填项(没有默认值,必须明确指定)
  • 计算值名称不得与服务的 env_var 名称冲突
模板字段
模板解析为用途
${rails.port}24920原始端口号
${rails.hostname}myapp.test(如果未设置主机名,则为 localhost)仅主机名
${rails.url}http://myapp.test面向浏览器的 URL(CORS、资源主机),通过代理路由
${rails.url:direct}http://localhost:24920绕过代理的服务间调用
${rails.env_var}PORT服务的环境变量名称
${rails.alias.NAME}app.myapp.test按标签指定的别名主机名
${rails.alias_url.NAME}https://app.myapp.test按标签指定的别名 URL

何时使用 url,何时使用 url:direct:

  • ${service.url} — 用于浏览器发送的值(CORS 源、资源主机、 OAuth 重定向 URI)。配置后使用 .test 主机名。
  • ${service.url:direct} — 用于服务间调用(后端获取数据时使用的 API 基础 URL、Node 服务发起的 WebSocket 连接)。始终使用 localhost,不经过代理。
独立变量和 bash 风格的参数展开

计算值支持独立变量和 bash 风格的参数展开,可用于感知实例的配置:

变量主实例Worktree 实例(例如,bxcf)
${project_name}myappmyapp
${instance}(空字符串)bxcf
${instance:-default}defaultbxcf
${instance:+replacement}(空字符串)replacement
${instance:+-${instance}}(空字符串)-bxcf

常见模式 — Docker Compose 项目名称:

computed:
  COMPOSE_PROJECT_NAME:
    value: "${project_name}${instance:+-${instance}}"
    env_file: .env

主实例的结果为 myapp,worktree 的结果为 myapp-bxcf, 从而让每个实例拥有彼此隔离的 Docker 容器。

按文件覆盖值

当同一个环境变量在不同文件中需要不同的值时(这在多个应用共用某种框架约定的 monorepo 中很常见),请对 env_file 条目使用对象语法:

computed:
  NUXT_API_BASE_URL:
    env_file:
      - file: frontend/apps/main/.env
        value: "${rails.url:direct}/api/v1"
      - file: frontend/apps/portal/.env
        value: "${rails.url:direct}/portal/api/v1"

你可以在同一个列表中混用普通字符串条目(使用顶层 value)和对象条目。

实际 monorepo 示例

使用两个 Nuxt 前端的 Rails 后端。后端需要前端 .test URL 作为 CORS 源。前端需要 Rails API URL 以便在服务端发起请求:

name: myapp
services:
  rails:
    env_var: RAILS_PORT
    hostname: myapp.test
    env_file: backend/.env
  frontend_main:
    env_var: MAIN_PORT
    hostname: app.myapp.test
    env_file:
      - frontend/apps/main/.env
      - backend/.env               # Backend needs this for CORS
  frontend_portal:
    env_var: PORTAL_PORT
    hostname: portal.myapp.test
    env_file:
      - frontend/apps/portal/.env
      - backend/.env               # Backend needs this for CORS

computed:
  # Server-to-server API URLs (bypass proxy — use direct localhost)
  NUXT_API_BASE_URL:
    env_file:
      - file: frontend/apps/main/.env
        value: "${rails.url:direct}/api/v1"
      - file: frontend/apps/portal/.env
        value: "${rails.url:direct}/portal/api/v1"

  # Backend CORS (browser-facing — use .test hostnames)
  CORE_CORS_ORIGINS:
    value: "${frontend_main.url},${frontend_portal.url}"
    env_file: backend/.env

  # Backend asset host (browser-facing)
  SHRINE_ASSET_HOST:
    value: "${rails.url}"
    env_file: backend/.env

执行 outport up 后,每个服务都会获得正确的端口和 URL。 不会残留硬编码值。

框架环境变量约定

设置计算值时,了解框架如何将环境变量映射到配置至关重要:

框架约定示例
NuxtNUXT_ 前缀映射到 runtimeConfigNUXT_API_BASE_URL 覆盖 runtimeConfig.apiBaseUrl
Rails (AnyWayConfig)PREFIX_ATTR 映射到配置类CORE_CORS_ORIGINS 覆盖 CoreConfig.cors_origins
Rails (Shrine)与 AnyWayConfig 使用相同模式SHRINE_ASSET_HOST 覆盖 ShrineConfig.asset_host
Django通常直接读取 os.environ根据 settings.py 的要求命名变量
Docker Compose自动读取 .envcompose.yml 中的 ${DB_PORT:-5432}

当计算值功能写入的环境变量符合这些框架约定时,它的作用最为显著—— 框架可以直接读取该值,无需更改配置代码。

.env 文件格式

Outport 会在每个 .env 文件末尾的围栏代码块中写入托管变量:

# Your own variables — Outport never touches these
SECRET_KEY=abc123
RAILS_ENV=development

# --- begin outport.dev ---
DB_PORT=21536
RAILS_PORT=24920
NUXT_API_BASE_URL=http://localhost:24920/api/v1
# --- end outport.dev ---

每次运行 outport up 时,围栏代码块都会替换为当前值。从 outport.yml 中移除的变量会从代码块中消失。代码块之外的所有内容都会保留。

多个实例

Outport 会自动检测 Git 工作树。每个工作树都会获得唯一的端口和自己的 .test 主机名,无需配置:

# Main checkout
$ outport up
my-app [main]
  rails  RAILS_PORT → 24920  http://my-app.test
  web    MAIN_PORT  → 21349

# Worktree — different ports, different hostname, zero conflicts
$ cd ../my-app-feature && outport up
  Registered as my-app-bkrm. Use 'outport rename bkrm <name>' to rename.
my-app [bkrm]
  rails  RAILS_PORT → 20192  http://my-app-bkrm.test
  web    MAIN_PORT  → 21133

每个实例都会重新计算派生值,例如 CORS 源、API URL 和所有其他派生值,并自动使用该实例的端口和主机名。两个完整实例可同时运行,不会发生端口冲突或主机名冲突,也无需手动配置。

使用以下命令管理实例:

outport rename bkrm my-feature   # Rename an instance
outport promote                   # Promote current instance to main

与设置脚本集成

在项目设置流程的早期运行 outport up,即在创建 .env 文件后、启动服务前。将其设为可选项,避免阻止未使用 Outport 的开发者:

# In bin/setup or similar
if command -v outport > /dev/null 2>&1; then
  outport up
else
  echo "Outport not found — using default ports"
  echo "Install: brew install steveclarke/tap/outport"
fi

常见任务

与另一个项目发生端口冲突

在两个项目中都运行 outport up。Outport 的注册表可确保所有已注册项目之间不会发生冲突。

端口是旧分配中的过期值

运行 outport up --force 以清除并重新分配。

释放已不再使用的项目所占端口

运行 outport down,将项目从注册表中移除并释放所有端口。

向现有项目添加新服务

将其添加到 outport.yml 并运行 outport up。现有分配会保留,只有新服务会获得端口。

Agent 需要了解项目的 URL

运行 outport status --json,获取包含端口、运行状况和 URL 的结构化输出。

服务使用的端口与预期不同

运行 outport system status 查看所有分配。如果其他项目占用了你想要的端口,请先在该项目中运行 outport down,然后在你的项目中运行 outport up --force。

通过手机访问开发服务

运行 outport qr,显示包含每个 HTTP 服务的局域网 URL 的二维码。在同一 Wi-Fi 网络下用手机扫描即可打开应用。运行 outport share 时,可使用 outport qr --tunnel 获取包含公共隧道 URL 的二维码。也可在 outport.test 的仪表板中查看二维码。

与网络之外的人共享服务

运行 outport share,通过隧道将所有 HTTP 服务映射到公共 Cloudflare URL。需要 cloudflared(brew install cloudflared)。按 Ctrl+C 停止。

某些功能无法正常运行

运行 outport doctor 检查 DNS、守护进程、证书、注册表和项目配置。每项检查都会显示通过或失败,并提供修复建议。

.test 域名无法解析

运行 outport doctor 进行诊断。常见原因:守护进程未运行(outport system start),或缺少 DNS 解析器(outport system start)。

错误与功能请求

请通过 https://github.com/steveclarke/outport/issues 报告错误或请求新功能

评论

支持 Markdown