mcpField
I
internal-dev-workbench

internal-dev-workbench

github.com/vercel开发2.5k651 次安装今天更新
代码仓库

关于 internal-dev-workbench

internal-dev-workbench 是面向 Workflow SDK 贡献者的可选开发工具,可启动三窗格 tmux 会话。它为不同 worktree 配置独立的 portless URL,并提供 Claude Code 状态栏脚本显示服务链接和会话命令。

主要功能

  1. 01三窗格 tmux 开发会话
  2. 02按 worktree 区分的 portless 路由
  3. 03查看当前 workbench 的可观测性界面
  4. 04Claude Code 状态栏脚本
  5. 05端到端测试与重启指引

适用场景

  1. 01并行运行开发服务和可观测性界面
  2. 02在多个 git worktree 中协作且避免端口冲突
  3. 03查看服务 URL 并连接 tmux 会话
  4. 04针对本地会话运行端到端测试

安装

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

$npx skills add https://github.com/vercel/workflow --skill internal-dev-workbench

安装后在 Claude Code 中输入/internal-dev-workbench确认已加载。

附带文件statusline.sh

详细介绍

internal-dev-workbench

为端到端 Workflow SDK 开发启动一个遵循特定约定的 3 窗格 tmux 会话。每个窗格都通过 portless 启动,因此 URL 稳定且限定于各自的 worktree(例如 https://<branch>.turbopack.localhost),让多个 worktree 可以并行运行而不会发生端口冲突。配套的状态栏脚本会在 Claude Code 的提示符中显示当前 URL。

这是供贡献者选择使用的工具。仓库的标准开发流程(在 workbench 中运行 pnpm dev,不使用 portless)不受影响。

先决条件

  • 已安装 tmux
  • 已全局安装 portless(使用 npm i -g portless 或通过 Homebrew 安装)。运行 portless --version 进行验证。
  • 已初始化仓库:pnpm install && pnpm build。在全新的 worktree 中首次运行时,必须先完成这两步,才能启动任何开发服务器(workbench 应用依赖已构建的工作区包;若未运行 pnpm build,会出现 MODULE_NOT_FOUND,无法找到 workflow)。
  • 使用开发服务器运行端到端测试时,必须设置 WORKFLOW_PUBLIC_MANIFEST=1(否则会拦截 /.well-known/workflow/v1/manifest.json)。

布局

使用 main-vertical 时,开发服务器占据左侧栏;右侧栏则在上方显示可观测性 UI,下方显示临时终端:

+----------------------+--------------------------+
|                      |  PANE_OBS: workflow web  |
|                      |  (observability UI       |
|  PANE_DEV: turbopack |   scoped to the          |
|  (Next.js dev)       |   workbench app)         |
|                      +--------------------------+
|                      |  PANE_SH: zsh scratchpad |
|                      |  (repo root, for build, |
|                      |   tests, e2e, git, etc.) |
+----------------------+--------------------------+

设置

会话名称必须与 worktree 的 portless 前缀相匹配(即当前分支的基本名称),这样状态栏以及其他通过分支推导前缀的工具才能找到它。务必先运行 tmux ls,确认不存在同名的现有会话;切勿终止已有会话。

tmux 中的窗格索引取决于 pane-base-index(默认为 0;使用常见 dotfile 覆盖设置时为 1)。为确保两种情况下都能正常工作,请在拆分窗格时使用 -P -F '#{pane_id}' 获取每个窗格的 ID,并将这些 ID 用作目标:

REPO=/path/to/workflow--<worktree-suffix>
# Session name = basename of the branch (matches portless's subdomain prefix
# and the statusline's `tmux attach -t <prefix>` indicator). For branch
# `pgp/foo-bar` this resolves to `foo-bar`.
SESSION=$(git -C "$REPO" rev-parse --abbrev-ref HEAD)
SESSION="${SESSION##*/}"

# Create the session and capture the initial pane ID
PANE_DEV=$(tmux new-session -d -s "$SESSION" -c "$REPO" -P -F '#{pane_id}')
PANE_OBS=$(tmux split-window -h -t "$PANE_DEV" -c "$REPO" -P -F '#{pane_id}')
PANE_SH=$(tmux split-window -v -t "$PANE_OBS" -c "$REPO" -P -F '#{pane_id}')
tmux select-layout -t "$SESSION" main-vertical

# Pane DEV (left): Next.js turbopack workbench, with manifest exposed for e2e
tmux send-keys -t "$PANE_DEV" \
  'cd workbench/nextjs-turbopack && WORKFLOW_PUBLIC_MANIFEST=1 portless run --name turbopack pnpm dev' C-m

# Pane OBS (top-right): observability UI scoped to the workbench app
tmux send-keys -t "$PANE_OBS" \
  'cd workbench/nextjs-turbopack && portless run --name workflow-obs sh -c "pnpm workflow web --webPort \$PORT --noBrowser"' C-m

# Pane SH (bottom-right): scratchpad at repo root
tmux send-keys -t "$PANE_SH" 'echo "scratchpad: $(pwd)"' C-m

tmux attach -t "$SESSION"

两个服务器就绪后,运行 portless list 即可查看路由。使用 portless run 时,每个关联的 worktree 都会获得一个以分支名为前缀的唯一子域名(例如 stepflow-test.turbopack.localhost),因此多个 worktree 可以共存,无需更改配置。

各部分的作用

  • portless run --name <name>(而不是 portless <name> <cmd>):run 会自动检测 git worktree,并将经过处理的分支名称作为子域名前缀。--name 标志会覆盖推断出的基本名称,同时保留 worktree 前缀。
  • pnpm workflow web --webPort $PORT --noBrowser(而不是在 packages/web 中运行 pnpm dev):打包后的 CLI 会启动针对当前 workbench 应用配置的可观测性 UI,并使用该项目的本地 World 数据为 UI 加载数据。运行 packages/web 自带的 dev 脚本虽然会启动 UI,但 UI 不会连接到任何内容。
  • sh -c '... --webPort $PORT':只有当 portless 能在命令行中检测到已知框架时,才会自动注入 --port。命令为 CLI 包装器(pnpm workflow web)时,请用 sh -c 包装,并显式读取 $PORT(portless 始终会设置该变量)。
  • 在开发窗格中设置 WORKFLOW_PUBLIC_MANIFEST=1:端到端测试必须设置此变量,才能从开发服务器获取工作流注册表。
  • -P -F '#{pane_id}':无论用户的 pane-base-index 设置如何,都能确保代码片段正确运行(不同配置中的默认值可能不同)。

Claude 状态栏集成

此技能提供了一个状态栏辅助脚本 skills/internal-dev-workbench/statusline.sh,它根据当前分支推导 worktree 前缀,并输出一行精简信息:

 dev  ·   obs  ·   tmux attach -t <worktree-prefix>

dev / obs 标签(Nerd Font 火箭 / 图表字形)是 OSC 8 超链接,可在 iTerm2、Kitty、WezTerm、Terminal.app 和 Ghostty 中点击。粗体、带下划线的亮青色样式用于标识链接。tmux 片段(Nerd Font 复制字形)为粗体亮绿色,表示“复制此内容”,而非“点击此处”。只有在存在名称与 worktree 前缀完全一致的会话时才会显示,并且会以完整的可直接粘贴的 tmux attach -t <prefix> 命令形式输出。字体必须包含 Nerd Font 字形,图标才能正确显示;否则会看到替代方框,但布局仍然正常。每个部分互相独立。如果 portless 没有 <prefix>.turbopack.localhost 路由,就会省略 dev 片段,其他部分也同理。如果没有可显示的内容,脚本不会输出任何内容,状态栏将保持静默。

将其接入 ~/.claude/settings.json,使其在所有会话和 worktree 中生效。路径应指向主检出目录,而不是 worktree。worktree 会被删除,因此任何类似 ~/github/vercel/workflow--<branch>/... 的路径都会在删除该 worktree 后失效:

{
  "statusLine": {
    "type": "command",
    "command": "$HOME/github/vercel/workflow/skills/internal-dev-workbench/statusline.sh"
  }
}

如果主检出目录位于其他位置,请调整前缀。脚本本身能够感知 worktree:它会从标准输入读取 Claude 的 workspace.current_dir 来推导当前分支,因此从 ~/github/vercel/workflow/... 调用 同一脚本 时,也能正确显示 Claude 会话所在 worktree 的路由。

输出规则:

  • 没有可显示的内容(没有匹配的 portless 路由,也没有匹配的 tmux 会话)→ 输出为空。
  • 各部分独立显示。启动了服务器但没有 tmux 会话时,只会显示 dev / obs 片段;反之则只会显示 tmux 片段。
  • 没有 git 上下文但存在路由 → 回退到第一个匹配的 turbopack / workflow-obs 路由,不显示 tmux 指示符。

如果你已经在使用状态栏,并希望追加 internal-dev-workbench 信息,请运行此辅助脚本,并在现有包装脚本中拼接其输出,而不是直接替换 command。

编辑 workflow 文件后重启

workflow 清单会在开发服务器启动时构建。添加到 workbench/example/workflows/*.ts 的新 workflow 或步骤(以及其他 workbench 中对应的符号链接)在运行时不会显示,即使启用了 HMR 也是如此,必须重启开发服务器才会生效。

tmux send-keys -t "$PANE_DEV" C-c
# Wait for the prompt to return
tmux send-keys -t "$PANE_DEV" \
  'cd workbench/nextjs-turbopack && WORKFLOW_PUBLIC_MANIFEST=1 portless run --name turbopack pnpm dev' C-m

验证新 workflow 是否已注册(使用 portless list 分配的本地端口,或使用受信任 CA 的 .localhost URL):

/usr/bin/curl -s "$(portless get turbopack)/.well-known/workflow/v1/manifest.json" \
  | grep -o '<your-new-workflow>'

对于通过 HTTPS URL 访问的 Node 客户端,如果它们不在 portless 管理的子进程中运行,则需要设置 NODE_EXTRA_CA_CERTS=/tmp/portless/ca.pem。执行过 portless trust 后,浏览器即可正常访问。

针对当前会话运行 e2e 测试

在 scratchpad 窗格中运行。使用 portless 分配的本地端口绕过 TLS,供测试运行器使用:

PORT=$(portless list | awk '/turbopack/ {n=split($3,a,":"); print a[n]; exit}')
DEPLOYMENT_URL="http://localhost:$PORT" APP_NAME="nextjs-turbopack" \
  pnpm vitest run packages/core/e2e/e2e.test.ts -t "<test name>"

或者使用已信任 CA 的 portless URL:

NODE_EXTRA_CA_CERTS=/tmp/portless/ca.pem \
  DEPLOYMENT_URL="$(portless get turbopack)" APP_NAME="nextjs-turbopack" \
  pnpm vitest run packages/core/e2e/e2e.test.ts -t "<test name>"

清理

tmux kill-session -t "$SESSION"

每个子进程退出时,Portless 都会移除对应路由(如果想让 portless list 的输出干净,请先在各窗格中按 Ctrl+C)。代理本身会继续为其他会话运行;如有需要,可使用 portless proxy stop 显式停止。

故障排除

  • 开发面板中出现 MODULE_NOT_FOUND: 'workflow':工作区软件包尚未构建。在仓库根目录运行 pnpm build,然后重启面板。
  • 可观测性界面没有显示运行记录:确认 obs 面板是在 workbench/nextjs-turbopack(或你要检查的其他 workbench)目录中启动的。CLI 会读取当前工作目录中的本地 World。
  • react-router 使用 :5173,而不是 portless 端口:这是因为 obs 面板使用了 packages/web 中的 pnpm dev。请改用上文的 pnpm workflow web --webPort $PORT 形式。
  • 启动时出现 Source-map 警告(failed to read input source map ... packages/serde/dist/index.js.map):此警告无害,不会阻碍开发。
  • 编辑 99_e2e.ts 后工作流注册信息过时:重启开发面板;HMR 不会重建清单。
  • 状态栏没有显示内容:确认 portless list 至少有一个匹配的路由,settings.json 中的路径是绝对路径,且脚本具有可执行权限(chmod +x)。

评论

支持 Markdown