Skip to content

fix(cli-adapters): drain the outbound queue on the poll-static-idle r… #2466

fix(cli-adapters): drain the outbound queue on the poll-static-idle r…

fix(cli-adapters): drain the outbound queue on the poll-static-idle r… #2466

Workflow file for this run

name: CI
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
workflow_dispatch:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
actions: read
contents: read
concurrency:
group: ci-oss-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
if: "${{ github.event_name != 'push' || github.ref_type != 'branch' || !startsWith(github.event.head_commit.message || '', 'chore: bump version to v') }}"
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22]
env:
ADHDEV_SKIP_GHOSTTY_VT_BUILD: '1'
steps:
- uses: actions/checkout@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Compute ghostty triplet
run: echo "GHOSTTY_TRIPLET=linux-x64-node$(node -p 'process.versions.modules')" >> "$GITHUB_ENV"
- name: Download latest ghostty prebuilt artifact
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}"
run_id="$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/ghostty-vt-node.yml/runs?branch=main&event=push&per_page=20" --jq '.workflow_runs[] | select(.conclusion == "success") | .id' | head -n 1 || true)"
if [ -z "${run_id}" ]; then
echo "No successful Ghostty VT Native run found on main; continuing without prebuilt artifact."
exit 0
fi
gh run download "${run_id}" \
--repo "${GITHUB_REPOSITORY}" \
--name "ghostty-vt-node-${GHOSTTY_TRIPLET}" \
--dir "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}" || {
echo "Ghostty prebuilt artifact download failed; continuing without it."
exit 0
}
find "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}" -mindepth 2 -maxdepth 2 -type f -exec mv {} "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}/" \;
find "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}" -mindepth 1 -type d -empty -delete
- name: Verify package lock is in sync
run: |
npm install --package-lock-only --ignore-scripts
git diff --exit-code -- package-lock.json
- name: Install dependencies
run: npm ci
- name: Build all packages
run: npm run build
- name: Verify shebang
run: head -1 packages/daemon-standalone/dist/index.js | grep '#!/usr/bin/env node'
- name: Upload publish artifacts
if: startsWith(github.ref, 'refs/tags/v')
uses: actions/upload-artifact@v7
with:
name: publish-artifacts
path: |
packages/session-host-core/dist
packages/session-host-daemon/dist
packages/mcp-server/dist
packages/daemon-core/dist
packages/daemon-standalone/dist
packages/web-standalone/dist
if-no-files-found: error
test:
needs: build
runs-on: ubuntu-latest
env:
ADHDEV_SKIP_GHOSTTY_VT_BUILD: '1'
steps:
- uses: actions/checkout@v5
# Provider spec fixtures live in the sibling adhdev-providers repo (a
# submodule of the private cloud monorepo, unknown to this OSS repo). The
# spec-driven tests resolve them at repoRoot/adhdev-providers, where
# repoRoot = $GITHUB_WORKSPACE/.. — a path outside the workspace that
# actions/checkout cannot target directly. Check it out inside the
# workspace and symlink it to the expected sibling location.
- name: Checkout provider specs
# Non-fatal: fork PRs get a read-only token scoped to the head repo and
# may lack access to adhdev-providers. On push/tag (the release path)
# the token has access and this succeeds.
continue-on-error: true
uses: actions/checkout@v5
with:
repository: vilmire/adhdev-providers
path: .adhdev-providers-src
token: ${{ secrets.GITHUB_TOKEN }}
- name: Link provider specs into expected sibling path
run: |
set -euo pipefail
if [ -d "$GITHUB_WORKSPACE/.adhdev-providers-src" ]; then
ln -sfn "$GITHUB_WORKSPACE/.adhdev-providers-src" "$GITHUB_WORKSPACE/../adhdev-providers"
echo "Linked adhdev-providers → $GITHUB_WORKSPACE/../adhdev-providers"
ls "$GITHUB_WORKSPACE/../adhdev-providers/cli/claude-cli/specs/4.0.json"
else
echo "⚠ adhdev-providers checkout absent (external fork without access); spec tests will resolve via fallback or fail."
fi
# Real-git fixture tests (mesh-fast-forward-node, git-status, refine-*)
# shell out to git init/commit/rev-parse. Without an identity and default
# branch, those spawns emit hints and misbehave on the runner.
- name: Configure git identity for test fixtures
run: |
git config --global user.email "ci@adhf.dev"
git config --global user.name "ADHDev CI"
git config --global init.defaultBranch main
- name: Use Node.js 22
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'npm'
- name: Compute ghostty triplet
run: echo "GHOSTTY_TRIPLET=linux-x64-node$(node -p 'process.versions.modules')" >> "$GITHUB_ENV"
- name: Download latest ghostty prebuilt artifact
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}"
run_id="$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/ghostty-vt-node.yml/runs?branch=main&event=push&per_page=20" --jq '.workflow_runs[] | select(.conclusion == "success") | .id' | head -n 1 || true)"
if [ -z "${run_id}" ]; then
echo "No successful Ghostty VT Native run found on main; continuing without prebuilt artifact."
exit 0
fi
gh run download "${run_id}" \
--repo "${GITHUB_REPOSITORY}" \
--name "ghostty-vt-node-${GHOSTTY_TRIPLET}" \
--dir "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}" || {
echo "Ghostty prebuilt artifact download failed; continuing without it."
exit 0
}
find "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}" -mindepth 2 -maxdepth 2 -type f -exec mv {} "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}/" \;
find "packages/ghostty-vt-node/prebuilt/${GHOSTTY_TRIPLET}" -mindepth 1 -type d -empty -delete
- name: Install dependencies
run: npm ci
# Build order follows the @adhdev dependency graph, because the typecheck
# steps below resolve workspace imports through each package's published
# `types`/`exports` — i.e. through its dist/, not its src/. A package whose
# dependency was never built fails with TS2307 "Cannot find module".
#
# terminal-mux-{core,control} are built here for exactly that reason:
# without them "Typecheck @adhdev/terminal-mux-cli" and
# "Typecheck @adhdev/daemon-standalone" both fail on TS2307. This is
# invisible locally (and to Refinery, which runs from the root worktree)
# because dist/ already exists there — it only reproduces in a clean
# checkout, which is what CI is.
#
# Order: session-host-core → terminal-mux-core → terminal-mux-control
# (mux-core needs session-host-core; mux-control needs both).
- name: Build packages required for tests
run: |
npm run build -w packages/session-host-core
npm run build -w packages/session-host-daemon
npm run build -w packages/mesh-shared
npm run build -w packages/daemon-core
npm run build -w packages/terminal-mux-core
npm run build -w packages/terminal-mux-control
- name: Test @adhdev/mesh-shared
run: npm run test -w packages/mesh-shared
- name: Test @adhdev/daemon-core
run: npm run test -w packages/daemon-core
- name: Test @adhdev/mcp-server
run: npm run test -w packages/mcp-server
- name: Test @adhdev/web-core
run: npm run test -w packages/web-core
- name: Test @adhdev/session-host-daemon
run: npm run test -w packages/session-host-daemon
- name: Test @adhdev/session-host-core
run: npm run test -w packages/session-host-core
# terminal-mux-{core,cli} tests import leaf modules directly rather than
# the package barrel, so they never load the @adhdev/ghostty-vt-node
# native binding and stay green on runners without a prebuilt.
- name: Test @adhdev/terminal-mux-core
run: npm run test -w packages/terminal-mux-core
- name: Test @adhdev/terminal-mux-cli
run: npm run test -w packages/terminal-mux-cli
- name: Test @adhdev/terminal-render-web
run: npm run test -w packages/terminal-render-web
- name: Test @adhdev/web-standalone
run: npm run test -w packages/web-standalone
# Dual runner: `node --import tsx --test test/*.test.ts && vitest run`.
# Both halves must pass — the node:test half covers session-host stop and
# CLI arg handling, the vitest half covers the HTTP surfaces.
- name: Test @adhdev/daemon-standalone
run: npm run test -w packages/daemon-standalone
- name: Typecheck @adhdev/daemon-core
run: npm run typecheck -w packages/daemon-core
- name: Typecheck @adhdev/session-host-daemon
run: npm run typecheck -w packages/session-host-daemon
- name: Typecheck @adhdev/session-host-core
run: npm run typecheck -w packages/session-host-core
- name: Typecheck @adhdev/terminal-mux-core
run: npm run typecheck -w packages/terminal-mux-core
- name: Typecheck @adhdev/terminal-mux-cli
run: npm run typecheck -w packages/terminal-mux-cli
- name: Typecheck @adhdev/terminal-render-web
run: npm run typecheck -w packages/terminal-render-web
- name: Typecheck @adhdev/daemon-standalone
run: npm run typecheck -w packages/daemon-standalone
publish:
needs: [build, test]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
env:
ADHDEV_SKIP_GHOSTTY_VT_BUILD: '1'
steps:
- uses: actions/checkout@v5
- name: Use Node.js 22
uses: actions/setup-node@v6
with:
node-version: 22
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
- name: Verify package lock is in sync
run: |
npm install --package-lock-only --ignore-scripts
git diff --exit-code -- package-lock.json
- name: Install dependencies
run: npm ci
- name: Check ghostty release need
id: ghostty_release
run: |
set -euo pipefail
package_version="$(node -p "require('./packages/ghostty-vt-node/package.json').version")"
published_version="$(npm view @adhdev/ghostty-vt-node version 2>/dev/null || true)"
if [ "${package_version}" = "${published_version}" ]; then
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "Ghostty package version ${package_version} is already published; skipping ghostty release work."
else
echo "publish=true" >> "$GITHUB_OUTPUT"
echo "Ghostty package version ${package_version} is unpublished; running ghostty release work."
fi
- name: Download release ghostty prebuilt artifacts
if: steps.ghostty_release.outputs.publish == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
run_id=""
for attempt in $(seq 1 30); do
run_id="$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/ghostty-vt-node.yml/runs?per_page=30" --jq '.workflow_runs[] | select(.head_sha == "'"${GITHUB_SHA}"'" and .conclusion == "success") | .id' | head -n 1 || true)"
if [ -n "${run_id}" ]; then
break
fi
pending_run="$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/ghostty-vt-node.yml/runs?per_page=30" --jq '.workflow_runs[] | select(.head_sha == "'"${GITHUB_SHA}"'" and (.status == "queued" or .status == "in_progress" or .status == "pending" or .status == "waiting")) | .id' | head -n 1 || true)"
if [ -z "${pending_run}" ]; then
echo "No successful Ghostty VT Native run found for ${GITHUB_SHA}."
exit 1
fi
echo "Waiting for Ghostty VT Native run ${pending_run} to finish..."
sleep 10
done
if [ -z "${run_id}" ]; then
echo "Timed out waiting for Ghostty VT Native run on ${GITHUB_SHA}."
exit 1
fi
artifact_names="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${run_id}/artifacts?per_page=100" --jq '.artifacts[] | select(.expired == false and (.name | startswith("ghostty-vt-node-"))) | .name')"
if [ -z "${artifact_names}" ]; then
echo "No ghostty native artifacts found for run ${run_id}."
exit 1
fi
while IFS= read -r artifact_name; do
[ -n "${artifact_name}" ] || continue
triplet="${artifact_name#ghostty-vt-node-}"
mkdir -p "packages/ghostty-vt-node/prebuilt/${triplet}"
gh run download "${run_id}" \
--repo "${GITHUB_REPOSITORY}" \
--name "${artifact_name}" \
--dir "packages/ghostty-vt-node/prebuilt/${triplet}"
find "packages/ghostty-vt-node/prebuilt/${triplet}" -mindepth 2 -maxdepth 2 -type f -exec mv {} "packages/ghostty-vt-node/prebuilt/${triplet}/" \;
find "packages/ghostty-vt-node/prebuilt/${triplet}" -mindepth 1 -type d -empty -delete
done <<< "${artifact_names}"
find packages/ghostty-vt-node/prebuilt -maxdepth 2 -type f -print | sort
- name: Download publish artifacts
uses: actions/download-artifact@v8
with:
name: publish-artifacts
path: packages
- name: Bundle web into daemon-standalone
run: npm run bundle:web -w packages/daemon-standalone
- name: Bundle vendored runtime deps into daemon-standalone
run: npm run bundle:vendor -w packages/daemon-standalone
- name: Build @adhdev/mesh-shared
run: npm run build -w packages/mesh-shared
- name: Publish @adhdev/mesh-shared
if: env.NODE_AUTH_TOKEN != ''
run: cd packages/mesh-shared && npm publish --access public --ignore-scripts || echo '⚠️ mesh-shared publish skipped (version may already exist)'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish @adhdev/session-host-core
if: env.NODE_AUTH_TOKEN != ''
run: cd packages/session-host-core && npm publish --access public --ignore-scripts || echo '⚠️ session-host-core publish skipped (version may already exist)'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish @adhdev/ghostty-vt-node
if: env.NODE_AUTH_TOKEN != '' && steps.ghostty_release.outputs.publish == 'true'
run: cd packages/ghostty-vt-node && npm publish --access public --ignore-scripts || echo '⚠️ ghostty-vt-node publish skipped (version may already exist)'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish @adhdev/mcp-server
if: env.NODE_AUTH_TOKEN != ''
run: cd packages/mcp-server && npm publish --access public --ignore-scripts || echo '⚠️ mcp-server publish skipped (version may already exist)'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Vendor session-host-core into daemon-core
run: |
set -euo pipefail
target="packages/daemon-core/node_modules/@adhdev/session-host-core"
rm -rf "${target}"
mkdir -p "${target}"
cp packages/session-host-core/package.json "${target}/package.json"
cp -R packages/session-host-core/dist "${target}/dist"
- name: Publish @adhdev/daemon-core
if: env.NODE_AUTH_TOKEN != ''
run: cd packages/daemon-core && npm publish --access public --ignore-scripts || echo '⚠️ daemon-core publish skipped (version may already exist)'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish @adhdev/daemon-standalone
if: env.NODE_AUTH_TOKEN != ''
run: cd packages/daemon-standalone && npm publish --access public --ignore-scripts || echo '⚠️ daemon-standalone publish skipped (version may already exist)'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}