From 3908854afa775d62ac9d544d0333cae885fac5b8 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 15:13:24 +0000 Subject: [PATCH 01/28] feat(runtime): Add portable resource root resolution Add a shared resource-root resolver that prioritizes XCODEBUILDMCP_RESOURCE_ROOT, then executable-relative paths, then package-root fallback for npm/source installs. Route manifest and bundled AXe path discovery through the shared resolver so portable distributions and existing workflows use the same deterministic contract. Also export DYLD_FRAMEWORK_PATH for bundled AXe execution and add tests that cover resource-root precedence and bundled framework environment behavior. Co-Authored-By: Claude --- src/core/__tests__/resource-root.test.ts | 51 +++++++++++++++ src/core/manifest/load-manifest.ts | 71 +-------------------- src/core/resource-root.ts | 81 ++++++++++++++++++++++++ src/utils/__tests__/axe-helpers.test.ts | 63 ++++++++++++++++++ src/utils/axe-helpers.ts | 43 ++++++------- 5 files changed, 216 insertions(+), 93 deletions(-) create mode 100644 src/core/__tests__/resource-root.test.ts create mode 100644 src/core/resource-root.ts create mode 100644 src/utils/__tests__/axe-helpers.test.ts diff --git a/src/core/__tests__/resource-root.test.ts b/src/core/__tests__/resource-root.test.ts new file mode 100644 index 00000000..439f9f40 --- /dev/null +++ b/src/core/__tests__/resource-root.test.ts @@ -0,0 +1,51 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { mkdtempSync, mkdirSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { + getBundledAxePath, + getBundledFrameworksDir, + getManifestsDir, + getResourceRoot, +} from '../resource-root.ts'; + +describe('resource-root', () => { + let originalExecPath: string; + let originalResourceRoot: string | undefined; + let tempDir: string; + + beforeEach(() => { + originalExecPath = process.execPath; + originalResourceRoot = process.env.XCODEBUILDMCP_RESOURCE_ROOT; + tempDir = mkdtempSync(join(tmpdir(), 'xbmcp-resource-root-')); + }); + + afterEach(() => { + process.execPath = originalExecPath; + if (originalResourceRoot === undefined) { + delete process.env.XCODEBUILDMCP_RESOURCE_ROOT; + } else { + process.env.XCODEBUILDMCP_RESOURCE_ROOT = originalResourceRoot; + } + rmSync(tempDir, { recursive: true, force: true }); + }); + + it('uses XCODEBUILDMCP_RESOURCE_ROOT when set', () => { + const explicitRoot = join(tempDir, 'explicit-root'); + process.env.XCODEBUILDMCP_RESOURCE_ROOT = explicitRoot; + + expect(getResourceRoot()).toBe(resolve(explicitRoot)); + expect(getManifestsDir()).toBe(join(resolve(explicitRoot), 'manifests')); + expect(getBundledAxePath()).toBe(join(resolve(explicitRoot), 'bundled', 'axe')); + }); + + it('falls back to executable-relative root when resources exist next to executable', () => { + delete process.env.XCODEBUILDMCP_RESOURCE_ROOT; + const executableRoot = join(tempDir, 'portable-install', 'libexec'); + mkdirSync(join(executableRoot, 'manifests', 'tools'), { recursive: true }); + process.execPath = join(executableRoot, 'xcodebuildmcp'); + + expect(getResourceRoot()).toBe(executableRoot); + expect(getBundledFrameworksDir()).toBe(join(executableRoot, 'bundled', 'Frameworks')); + }); +}); diff --git a/src/core/manifest/load-manifest.ts b/src/core/manifest/load-manifest.ts index ad789d36..ad75ecfa 100644 --- a/src/core/manifest/load-manifest.ts +++ b/src/core/manifest/load-manifest.ts @@ -5,7 +5,6 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; -import { fileURLToPath } from 'node:url'; import { parse as parseYaml } from 'yaml'; import { toolManifestEntrySchema, @@ -14,78 +13,12 @@ import { type WorkflowManifestEntry, type ResolvedManifest, } from './schema.ts'; +import { getManifestsDir, getPackageRoot } from '../resource-root.ts'; // Re-export types for consumers export type { ResolvedManifest, ToolManifestEntry, WorkflowManifestEntry }; import { isValidPredicate } from '../../visibility/predicate-registry.ts'; - -// Capture import.meta.url at module load time (before any CJS bundling issues) -// This works because the value is captured when the module is first evaluated -const importMetaUrl: string | undefined = ((): string | undefined => { - try { - // This will be undefined in CJS bundles but valid in ESM - return import.meta.url; - } catch { - return undefined; - } -})(); - -/** - * Get the current file path, handling both ESM and CJS contexts. - * Smithery bundles to CJS where import.meta.url is undefined. - */ -function getCurrentFilePath(): string { - // ESM context - use captured import.meta.url - if (importMetaUrl) { - return fileURLToPath(importMetaUrl); - } - - // CJS context (Smithery bundle) - __filename is shimmed by esbuild - if (typeof __filename !== 'undefined' && __filename) { - return __filename as string; - } - - // Fallback: try to resolve from cwd - const cwd = process.cwd(); - const possiblePaths = [ - path.join(cwd, 'build', 'core', 'manifest', 'load-manifest.ts'), - path.join(cwd, 'src', 'core', 'manifest', 'load-manifest.ts'), - ]; - for (const p of possiblePaths) { - if (fs.existsSync(p)) { - return p; - } - } - - throw new Error('Cannot determine current file path in this runtime context'); -} - -/** - * Get the package root directory. - * Works correctly for both development and npx/npm installs. - */ -export function getPackageRoot(): string { - // Start from this file's directory and go up to find package.json - const currentFile = getCurrentFilePath(); - let dir = path.dirname(currentFile); - - // Walk up until we find package.json - while (dir !== path.dirname(dir)) { - if (fs.existsSync(path.join(dir, 'package.json'))) { - return dir; - } - dir = path.dirname(dir); - } - - throw new Error('Could not find package root (no package.json found in parent directories)'); -} - -/** - * Get the manifests directory path. - */ -export function getManifestsDir(): string { - return path.join(getPackageRoot(), 'manifests'); -} +export { getManifestsDir, getPackageRoot } from '../resource-root.ts'; /** * Load all YAML files from a directory. diff --git a/src/core/resource-root.ts b/src/core/resource-root.ts new file mode 100644 index 00000000..77715691 --- /dev/null +++ b/src/core/resource-root.ts @@ -0,0 +1,81 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const RESOURCE_ROOT_ENV_VAR = 'XCODEBUILDMCP_RESOURCE_ROOT'; + +function hasResourceLayout(root: string): boolean { + return fs.existsSync(path.join(root, 'manifests')) || fs.existsSync(path.join(root, 'bundled')); +} + +function findPackageRootFrom(startDir: string): string | null { + let dir = startDir; + while (dir !== path.dirname(dir)) { + if (fs.existsSync(path.join(dir, 'package.json'))) { + return dir; + } + dir = path.dirname(dir); + } + return null; +} + +export function getPackageRoot(): string { + const candidates: string[] = []; + candidates.push(path.dirname(fileURLToPath(import.meta.url))); + candidates.push(process.cwd()); + const entry = process.argv[1]; + if (entry) { + candidates.push(path.dirname(entry)); + } + + for (const candidate of candidates) { + const found = findPackageRootFrom(candidate); + if (found) { + return found; + } + } + + throw new Error('Could not find package root (no package.json found in parent directories)'); +} + +function getExecutableResourceRoot(): string | null { + const execPath = process.execPath; + if (!execPath) { + return null; + } + + const candidateDirs = [path.dirname(execPath), path.dirname(path.dirname(execPath))]; + for (const candidate of candidateDirs) { + if (hasResourceLayout(candidate)) { + return candidate; + } + } + + return null; +} + +export function getResourceRoot(): string { + const explicitRoot = process.env[RESOURCE_ROOT_ENV_VAR]; + if (explicitRoot) { + return path.resolve(explicitRoot); + } + + const executableRoot = getExecutableResourceRoot(); + if (executableRoot) { + return executableRoot; + } + + return getPackageRoot(); +} + +export function getManifestsDir(): string { + return path.join(getResourceRoot(), 'manifests'); +} + +export function getBundledAxePath(): string { + return path.join(getResourceRoot(), 'bundled', 'axe'); +} + +export function getBundledFrameworksDir(): string { + return path.join(getResourceRoot(), 'bundled', 'Frameworks'); +} diff --git a/src/utils/__tests__/axe-helpers.test.ts b/src/utils/__tests__/axe-helpers.test.ts new file mode 100644 index 00000000..2c7dc1ee --- /dev/null +++ b/src/utils/__tests__/axe-helpers.test.ts @@ -0,0 +1,63 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { getBundledAxeEnvironment } from '../axe-helpers.ts'; + +describe('axe-helpers', () => { + let originalResourceRoot: string | undefined; + let originalDyldFrameworkPath: string | undefined; + let tempDir: string; + + beforeEach(() => { + originalResourceRoot = process.env.XCODEBUILDMCP_RESOURCE_ROOT; + originalDyldFrameworkPath = process.env.DYLD_FRAMEWORK_PATH; + tempDir = mkdtempSync(join(tmpdir(), 'xbmcp-axe-helpers-')); + }); + + afterEach(() => { + if (originalResourceRoot === undefined) { + delete process.env.XCODEBUILDMCP_RESOURCE_ROOT; + } else { + process.env.XCODEBUILDMCP_RESOURCE_ROOT = originalResourceRoot; + } + + if (originalDyldFrameworkPath === undefined) { + delete process.env.DYLD_FRAMEWORK_PATH; + } else { + process.env.DYLD_FRAMEWORK_PATH = originalDyldFrameworkPath; + } + + rmSync(tempDir, { recursive: true, force: true }); + }); + + it('returns DYLD_FRAMEWORK_PATH when bundled axe is resolved from resource root', () => { + const resourceRoot = join(tempDir, 'portable-root'); + const axePath = join(resourceRoot, 'bundled', 'axe'); + const frameworksDir = join(resourceRoot, 'bundled', 'Frameworks'); + mkdirSync(frameworksDir, { recursive: true }); + writeFileSync(axePath, ''); + process.env.XCODEBUILDMCP_RESOURCE_ROOT = resourceRoot; + delete process.env.DYLD_FRAMEWORK_PATH; + + const env = getBundledAxeEnvironment(); + expect(env).toEqual({ + DYLD_FRAMEWORK_PATH: frameworksDir, + }); + }); + + it('preserves existing DYLD_FRAMEWORK_PATH entries when using bundled axe', () => { + const resourceRoot = join(tempDir, 'portable-root'); + const axePath = join(resourceRoot, 'bundled', 'axe'); + const frameworksDir = join(resourceRoot, 'bundled', 'Frameworks'); + mkdirSync(frameworksDir, { recursive: true }); + writeFileSync(axePath, ''); + process.env.XCODEBUILDMCP_RESOURCE_ROOT = resourceRoot; + process.env.DYLD_FRAMEWORK_PATH = '/existing/frameworks'; + + const env = getBundledAxeEnvironment(); + expect(env).toEqual({ + DYLD_FRAMEWORK_PATH: `${frameworksDir}:/existing/frameworks`, + }); + }); +}); diff --git a/src/utils/axe-helpers.ts b/src/utils/axe-helpers.ts index 1f947395..8cb0d78c 100644 --- a/src/utils/axe-helpers.ts +++ b/src/utils/axe-helpers.ts @@ -6,12 +6,13 @@ */ import { accessSync, constants, existsSync } from 'fs'; -import { dirname, join, resolve, delimiter } from 'path'; +import { delimiter, join, resolve } from 'path'; import { createTextResponse } from './validation.ts'; import type { ToolResponse } from '../types/common.ts'; import type { CommandExecutor } from './execution/index.ts'; import { getDefaultCommandExecutor } from './execution/index.ts'; import { getConfig } from './config-store.ts'; +import { getBundledAxePath, getBundledFrameworksDir } from '../core/resource-root.ts'; export type AxeBinarySource = 'env' | 'bundled' | 'path'; @@ -20,19 +21,6 @@ export type AxeBinary = { source: AxeBinarySource; }; -function getPackageRoot(): string { - const entry = process.argv[1]; - if (entry) { - const entryDir = dirname(entry); - return dirname(entryDir); - } - return process.cwd(); -} - -// In the npm package, build/index.js is at the same level as bundled/ -// So we go up one level from build/ to get to the package root -const bundledAxePath = join(getPackageRoot(), 'bundled', 'axe'); - function isExecutable(path: string): boolean { try { accessSync(path, constants.X_OK); @@ -50,14 +38,8 @@ function resolveAxePathFromConfig(): string | null { } function resolveBundledAxePath(): string | null { - const entry = process.argv[1]; const candidates = new Set(); - if (entry) { - const entryDir = dirname(entry); - candidates.add(join(dirname(entryDir), 'bundled', 'axe')); - candidates.add(join(entryDir, 'bundled', 'axe')); - } - candidates.add(bundledAxePath); + candidates.add(getBundledAxePath()); candidates.add(join(process.cwd(), 'bundled', 'axe')); for (const candidate of candidates) { @@ -110,9 +92,22 @@ export function getAxePath(): string | null { * Get environment variables needed for bundled AXe to run */ export function getBundledAxeEnvironment(): Record { - // No special environment variables needed - bundled AXe binary - // has proper @rpath configuration to find frameworks - return {}; + const resolved = resolveAxeBinary(); + if (resolved?.source !== 'bundled') { + return {}; + } + + const frameworksDir = getBundledFrameworksDir(); + if (!existsSync(frameworksDir)) { + return {}; + } + + const currentFrameworkPath = process.env.DYLD_FRAMEWORK_PATH; + const frameworkPath = currentFrameworkPath + ? `${frameworksDir}${delimiter}${currentFrameworkPath}` + : frameworksDir; + + return { DYLD_FRAMEWORK_PATH: frameworkPath }; } /** From 2c772a54cfef4755a78a064ec7685cc4358fde3d Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 15:14:28 +0000 Subject: [PATCH 02/28] build(axe): Enforce signature checks in bundling Make local AXe source path env-driven with a project-local default and add macOS verification gates for bundled artifacts. Validate codesign signatures for the AXe binary and bundled frameworks, run Gatekeeper assessment on the executable, and fail bundling when verification fails. Also use non-force directory removal where cleanup already guards for existence. Co-Authored-By: Claude --- scripts/bundle-axe.sh | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/bundle-axe.sh b/scripts/bundle-axe.sh index ea47ffc9..4c493b4e 100755 --- a/scripts/bundle-axe.sh +++ b/scripts/bundle-axe.sh @@ -8,7 +8,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" BUNDLED_DIR="$PROJECT_ROOT/bundled" -AXE_LOCAL_DIR="/Volumes/Developer/AXe" +AXE_LOCAL_DIR="${AXE_LOCAL_DIR:-/Users/cameroncooke/Developer/AXe}" AXE_TEMP_DIR="/tmp/axe-download-$$" echo "๐Ÿ”จ Preparing AXe artifacts for bundling..." @@ -31,7 +31,7 @@ echo "๐Ÿ“Œ Using AXe version: $PINNED_AXE_VERSION" # Clean up any existing bundled directory if [ -d "$BUNDLED_DIR" ]; then echo "๐Ÿงน Cleaning existing bundled directory..." - rm -rf "$BUNDLED_DIR" + rm -r "$BUNDLED_DIR" fi # Create bundled directory @@ -147,6 +147,25 @@ ls -la "$BUNDLED_DIR/Frameworks/" # Verify binary can run with bundled frameworks (macOS only) OS_NAME="$(uname -s)" if [ "$OS_NAME" = "Darwin" ]; then + echo "๐Ÿ” Verifying AXe signatures..." + if ! codesign --verify --deep --strict "$BUNDLED_DIR/axe"; then + echo "โŒ Signature verification failed for bundled AXe binary" + exit 1 + fi + + while IFS= read -r framework_path; do + if ! codesign --verify --deep --strict "$framework_path"; then + echo "โŒ Signature verification failed for framework: $framework_path" + exit 1 + fi + done < <(find "$BUNDLED_DIR/Frameworks" -name "*.framework" -type d) + + echo "๐Ÿ›ก๏ธ Assessing AXe with Gatekeeper..." + if ! spctl --assess --type execute "$BUNDLED_DIR/axe"; then + echo "โŒ Gatekeeper assessment failed for bundled AXe binary" + exit 1 + fi + echo "๐Ÿงช Testing bundled AXe binary..." if DYLD_FRAMEWORK_PATH="$BUNDLED_DIR/Frameworks" "$BUNDLED_DIR/axe" --version > /dev/null 2>&1; then echo "โœ… Bundled AXe binary test passed" @@ -166,7 +185,7 @@ echo "๐Ÿ“‹ AXe version: $AXE_VERSION" # Clean up temp directory if it was used if [ -d "$AXE_TEMP_DIR" ]; then echo "๐Ÿงน Cleaning up temporary files..." - rm -rf "$AXE_TEMP_DIR" + rm -r "$AXE_TEMP_DIR" fi # Show final bundle size From 961a40d39f8a51707e23e2c01cdc6a217727e37c Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 15:23:50 +0000 Subject: [PATCH 03/28] build(portable): Add macOS portable packaging scripts Add portable packaging and verification scripts for macOS tarball distribution, including bin/libexec layout, bundled runtime resources, production dependencies, and artifact checksum generation. Add npm entrypoints for package and verification workflows. Harden AXe bundling for release provenance by defaulting to remote artifacts, keeping local AXe as explicit opt-in, and enforcing signature checks with CLI-safe Gatekeeper handling. Co-Authored-By: Claude --- package.json | 3 + scripts/bundle-axe.sh | 41 ++++- scripts/package-macos-portable.sh | 237 +++++++++++++++++++++++++++++ scripts/verify-portable-install.sh | 95 ++++++++++++ 4 files changed, 369 insertions(+), 7 deletions(-) create mode 100755 scripts/package-macos-portable.sh create mode 100755 scripts/verify-portable-install.sh diff --git a/package.json b/package.json index bf0218ee..d85b26e9 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,9 @@ "hooks:install": "node scripts/install-git-hooks.js", "generate:version": "npx tsx scripts/generate-version.ts", "bundle:axe": "scripts/bundle-axe.sh", + "package:macos": "scripts/package-macos-portable.sh", + "package:macos:universal": "scripts/package-macos-portable.sh --universal", + "verify:portable": "scripts/verify-portable-install.sh", "lint": "eslint 'src/**/*.{js,ts}'", "lint:fix": "eslint 'src/**/*.{js,ts}' --fix", "format": "prettier --write 'src/**/*.{js,ts}'", diff --git a/scripts/bundle-axe.sh b/scripts/bundle-axe.sh index 4c493b4e..e9228c1e 100755 --- a/scripts/bundle-axe.sh +++ b/scripts/bundle-axe.sh @@ -37,8 +37,13 @@ fi # Create bundled directory mkdir -p "$BUNDLED_DIR" -# Use local AXe build if available (unless AXE_FORCE_REMOTE=1), otherwise download from GitHub releases -if [ -z "${AXE_FORCE_REMOTE}" ] && [ -d "$AXE_LOCAL_DIR" ] && [ -f "$AXE_LOCAL_DIR/Package.swift" ]; then +USE_LOCAL_AXE=false +if [ -z "${AXE_FORCE_REMOTE}" ] && [ "${AXE_USE_LOCAL:-0}" = "1" ]; then + USE_LOCAL_AXE=true +fi + +# Use local AXe build only when explicitly requested, otherwise download from GitHub releases. +if [ "$USE_LOCAL_AXE" = true ] && [ -d "$AXE_LOCAL_DIR" ] && [ -f "$AXE_LOCAL_DIR/Package.swift" ]; then echo "๐Ÿ  Using local AXe source at $AXE_LOCAL_DIR" cd "$AXE_LOCAL_DIR" @@ -80,6 +85,11 @@ if [ -z "${AXE_FORCE_REMOTE}" ] && [ -d "$AXE_LOCAL_DIR" ] && [ -f "$AXE_LOCAL_D fi done else + if [ "$USE_LOCAL_AXE" = true ]; then + echo "โŒ AXE_USE_LOCAL=1 was set, but AXE_LOCAL_DIR is missing or invalid: $AXE_LOCAL_DIR" + exit 1 + fi + echo "๐Ÿ“ฅ Downloading latest AXe release from GitHub..." # Construct release download URL from pinned version @@ -154,17 +164,34 @@ if [ "$OS_NAME" = "Darwin" ]; then fi while IFS= read -r framework_path; do - if ! codesign --verify --deep --strict "$framework_path"; then - echo "โŒ Signature verification failed for framework: $framework_path" + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [ ! -f "$framework_binary" ]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + if [ ! -f "$framework_binary" ]; then + echo "โŒ Framework binary not found: $framework_binary" + exit 1 + fi + if ! codesign --verify --deep --strict "$framework_binary"; then + echo "โŒ Signature verification failed for framework binary: $framework_binary" exit 1 fi done < <(find "$BUNDLED_DIR/Frameworks" -name "*.framework" -type d) echo "๐Ÿ›ก๏ธ Assessing AXe with Gatekeeper..." - if ! spctl --assess --type execute "$BUNDLED_DIR/axe"; then - echo "โŒ Gatekeeper assessment failed for bundled AXe binary" - exit 1 + SPCTL_LOG="$(mktemp)" + if ! spctl --assess --type execute "$BUNDLED_DIR/axe" 2>"$SPCTL_LOG"; then + if grep -q "does not seem to be an app" "$SPCTL_LOG"; then + echo "โš ๏ธ Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" + else + cat "$SPCTL_LOG" + echo "โŒ Gatekeeper assessment failed for bundled AXe binary" + rm "$SPCTL_LOG" + exit 1 + fi fi + rm "$SPCTL_LOG" echo "๐Ÿงช Testing bundled AXe binary..." if DYLD_FRAMEWORK_PATH="$BUNDLED_DIR/Frameworks" "$BUNDLED_DIR/axe" --version > /dev/null 2>&1; then diff --git a/scripts/package-macos-portable.sh b/scripts/package-macos-portable.sh new file mode 100755 index 00000000..7f29b18e --- /dev/null +++ b/scripts/package-macos-portable.sh @@ -0,0 +1,237 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +DIST_DIR_DEFAULT="$PROJECT_ROOT/dist/portable" + +ARCH="" +UNIVERSAL=false +ARM64_ROOT="" +X64_ROOT="" +DIST_DIR="$DIST_DIR_DEFAULT" +VERSION="" + +usage() { + cat <<'EOF' +Usage: + scripts/package-macos-portable.sh [--arch arm64|x64] [--dist-dir ] [--version ] + scripts/package-macos-portable.sh --universal --arm64-root --x64-root [--dist-dir ] [--version ] + +Notes: + - Arch mode packages a bundled Node runtime plus compiled JS entrypoint. + - Universal mode expects prebuilt arm64/x64 roots and combines Node runtimes with lipo. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --arch) + ARCH="${2:-}" + shift 2 + ;; + --universal) + UNIVERSAL=true + shift + ;; + --arm64-root) + ARM64_ROOT="${2:-}" + shift 2 + ;; + --x64-root) + X64_ROOT="${2:-}" + shift 2 + ;; + --dist-dir) + DIST_DIR="${2:-}" + shift 2 + ;; + --version) + VERSION="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +if [[ -z "$VERSION" ]]; then + VERSION="$(node -p "require('$PROJECT_ROOT/package.json').version")" +fi + +mkdir -p "$DIST_DIR" + +verify_axe_assets() { + local bundled_dir="$PROJECT_ROOT/bundled" + local axe_bin="$bundled_dir/axe" + local frameworks_dir="$bundled_dir/Frameworks" + + if [[ ! -x "$axe_bin" ]]; then + echo "Missing executable AXe binary at $axe_bin" + exit 1 + fi + if [[ ! -d "$frameworks_dir" ]]; then + echo "Missing AXe frameworks at $frameworks_dir" + exit 1 + fi + if [[ "$(find "$frameworks_dir" -name "*.framework" -type d | wc -l | tr -d ' ')" -eq 0 ]]; then + echo "No frameworks found under $frameworks_dir" + exit 1 + fi + + if [[ "$(uname -s)" == "Darwin" ]]; then + codesign --verify --deep --strict "$axe_bin" + while IFS= read -r framework_path; do + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [[ ! -f "$framework_binary" ]]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + if [[ ! -f "$framework_binary" ]]; then + echo "Missing framework binary at $framework_binary" + exit 1 + fi + codesign --verify --deep --strict "$framework_binary" + done < <(find "$frameworks_dir" -name "*.framework" -type d) + spctl_log="$(mktemp)" + if ! spctl --assess --type execute "$axe_bin" 2>"$spctl_log"; then + if grep -q "does not seem to be an app" "$spctl_log"; then + echo "Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" + else + cat "$spctl_log" + rm "$spctl_log" + exit 1 + fi + fi + rm "$spctl_log" + fi +} + +write_wrapper_scripts() { + local root="$1" + local bin_dir="$root/bin" + local libexec_dir="$root/libexec" + + cat > "$libexec_dir/xcodebuildmcp" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$ROOT/node-runtime" "$ROOT/build/cli.js" "$@" +EOF + + cat > "$bin_dir/xcodebuildmcp" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +RESOURCE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../libexec" && pwd)" +export XCODEBUILDMCP_RESOURCE_ROOT="$RESOURCE_ROOT" +export DYLD_FRAMEWORK_PATH="$RESOURCE_ROOT/bundled/Frameworks${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH}" +exec "$RESOURCE_ROOT/xcodebuildmcp" "$@" +EOF + + cat > "$bin_dir/xcodebuildmcp-doctor" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +RESOURCE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../libexec" && pwd)" +export XCODEBUILDMCP_RESOURCE_ROOT="$RESOURCE_ROOT" +export DYLD_FRAMEWORK_PATH="$RESOURCE_ROOT/bundled/Frameworks${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH}" +exec "$RESOURCE_ROOT/xcodebuildmcp" doctor "$@" +EOF + + chmod +x "$libexec_dir/xcodebuildmcp" "$bin_dir/xcodebuildmcp" "$bin_dir/xcodebuildmcp-doctor" +} + +create_tarball_and_checksum() { + local portable_root="$1" + local artifact_name="$2" + local tarball_path="$DIST_DIR/$artifact_name.tar.gz" + local checksum_path="$tarball_path.sha256" + + ( + cd "$(dirname "$portable_root")" + tar -czf "$tarball_path" "$(basename "$portable_root")" + ) + shasum -a 256 "$tarball_path" > "$checksum_path" + echo "Created artifact: $tarball_path" + echo "Created checksum: $checksum_path" +} + +if [[ "$UNIVERSAL" == "true" ]]; then + if [[ -z "$ARM64_ROOT" || -z "$X64_ROOT" ]]; then + echo "--universal requires --arm64-root and --x64-root" + exit 1 + fi + if [[ ! -x "$ARM64_ROOT/libexec/node-runtime" || ! -x "$X64_ROOT/libexec/node-runtime" ]]; then + echo "Missing per-arch node runtimes under provided roots" + exit 1 + fi + + UNIVERSAL_ROOT="$DIST_DIR/xcodebuildmcp-$VERSION-darwin-universal" + if [[ -d "$UNIVERSAL_ROOT" ]]; then + rm -r "$UNIVERSAL_ROOT" + fi + mkdir -p "$UNIVERSAL_ROOT/bin" "$UNIVERSAL_ROOT/libexec" + cp -R "$ARM64_ROOT/libexec/build" "$UNIVERSAL_ROOT/libexec/" + cp -R "$ARM64_ROOT/libexec/manifests" "$UNIVERSAL_ROOT/libexec/" + cp -R "$ARM64_ROOT/libexec/bundled" "$UNIVERSAL_ROOT/libexec/" + cp -R "$ARM64_ROOT/libexec/node_modules" "$UNIVERSAL_ROOT/libexec/" + cp "$ARM64_ROOT/libexec/package.json" "$UNIVERSAL_ROOT/libexec/package.json" + + lipo -create \ + "$ARM64_ROOT/libexec/node-runtime" \ + "$X64_ROOT/libexec/node-runtime" \ + -output "$UNIVERSAL_ROOT/libexec/node-runtime" + chmod +x "$UNIVERSAL_ROOT/libexec/node-runtime" + + write_wrapper_scripts "$UNIVERSAL_ROOT" + create_tarball_and_checksum "$UNIVERSAL_ROOT" "xcodebuildmcp-$VERSION-darwin-universal" + exit 0 +fi + +if [[ -z "$ARCH" ]]; then + machine_arch="$(uname -m)" + if [[ "$machine_arch" == "arm64" ]]; then + ARCH="arm64" + elif [[ "$machine_arch" == "x86_64" ]]; then + ARCH="x64" + else + echo "Unsupported machine architecture: $machine_arch" + exit 1 + fi +fi + +if [[ "$ARCH" != "arm64" && "$ARCH" != "x64" ]]; then + echo "Unsupported arch: $ARCH (expected arm64 or x64)" + exit 1 +fi + +cd "$PROJECT_ROOT" +npm run build:tsup +AXE_FORCE_REMOTE=1 npm run bundle:axe +verify_axe_assets + +PORTABLE_ROOT="$DIST_DIR/xcodebuildmcp-$VERSION-darwin-$ARCH" +if [[ -d "$PORTABLE_ROOT" ]]; then + rm -r "$PORTABLE_ROOT" +fi +mkdir -p "$PORTABLE_ROOT/bin" "$PORTABLE_ROOT/libexec" + +cp "$(command -v node)" "$PORTABLE_ROOT/libexec/node-runtime" +chmod +x "$PORTABLE_ROOT/libexec/node-runtime" + +cp -R "$PROJECT_ROOT/build" "$PORTABLE_ROOT/libexec/" +cp -R "$PROJECT_ROOT/manifests" "$PORTABLE_ROOT/libexec/" +cp -R "$PROJECT_ROOT/bundled" "$PORTABLE_ROOT/libexec/" +cp "$PROJECT_ROOT/package.json" "$PORTABLE_ROOT/libexec/package.json" +cp "$PROJECT_ROOT/package-lock.json" "$PORTABLE_ROOT/libexec/package-lock.json" +npm ci --omit=dev --ignore-scripts --prefix "$PORTABLE_ROOT/libexec" + +write_wrapper_scripts "$PORTABLE_ROOT" +create_tarball_and_checksum "$PORTABLE_ROOT" "xcodebuildmcp-$VERSION-darwin-$ARCH" diff --git a/scripts/verify-portable-install.sh b/scripts/verify-portable-install.sh new file mode 100755 index 00000000..53775f30 --- /dev/null +++ b/scripts/verify-portable-install.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ARCHIVE_PATH="" +PORTABLE_ROOT="" +TEMP_DIR="" + +usage() { + cat <<'EOF' +Usage: + scripts/verify-portable-install.sh --archive + scripts/verify-portable-install.sh --root +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --archive) + ARCHIVE_PATH="${2:-}" + shift 2 + ;; + --root) + PORTABLE_ROOT="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +if [[ -z "$ARCHIVE_PATH" && -z "$PORTABLE_ROOT" ]]; then + usage + exit 1 +fi + +if [[ -n "$ARCHIVE_PATH" ]]; then + TEMP_DIR="$(mktemp -d)" + tar -xzf "$ARCHIVE_PATH" -C "$TEMP_DIR" + extracted_count="$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" + if [[ "$extracted_count" -ne 1 ]]; then + echo "Expected archive to contain exactly one top-level directory" + exit 1 + fi + PORTABLE_ROOT="$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d | head -1)" +fi + +cleanup() { + if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then + rm -r "$TEMP_DIR" + fi +} +trap cleanup EXIT + +if [[ ! -d "$PORTABLE_ROOT/bin" || ! -d "$PORTABLE_ROOT/libexec" ]]; then + echo "Portable layout missing bin/ or libexec/: $PORTABLE_ROOT" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/bin/xcodebuildmcp" ]]; then + echo "Missing executable wrapper: $PORTABLE_ROOT/bin/xcodebuildmcp" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/bin/xcodebuildmcp-doctor" ]]; then + echo "Missing executable wrapper: $PORTABLE_ROOT/bin/xcodebuildmcp-doctor" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/libexec/xcodebuildmcp" ]]; then + echo "Missing executable binary: $PORTABLE_ROOT/libexec/xcodebuildmcp" + exit 1 +fi +if [[ ! -d "$PORTABLE_ROOT/libexec/manifests" ]]; then + echo "Missing manifests directory under libexec" + exit 1 +fi +if [[ ! -x "$PORTABLE_ROOT/libexec/bundled/axe" ]]; then + echo "Missing bundled axe binary under libexec" + exit 1 +fi +if [[ ! -d "$PORTABLE_ROOT/libexec/bundled/Frameworks" ]]; then + echo "Missing bundled Frameworks under libexec" + exit 1 +fi + +"$PORTABLE_ROOT/bin/xcodebuildmcp" --help >/dev/null +"$PORTABLE_ROOT/bin/xcodebuildmcp-doctor" --help >/dev/null + +echo "Portable install verification passed for: $PORTABLE_ROOT" + From 7d14c35c384822375bb7dc6899741abfe8afc9b7 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 15:25:11 +0000 Subject: [PATCH 04/28] ci(release): Add macOS portable packaging pipeline Extend release workflow with per-architecture macOS packaging jobs, universal artifact assembly, and portable artifact verification. Publish arm64, x64, and universal tarballs plus SHA256 files to GitHub Releases. Keep existing npm and MCP registry release flow intact while wiring version output from the release job into portable packaging stages. Co-Authored-By: Claude --- .github/workflows/release.yml | 125 ++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aabcac1f..539473e8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,8 @@ jobs: release: runs-on: macos-latest environment: production + outputs: + version: ${{ steps.get_version.outputs.VERSION }} steps: - name: Checkout code @@ -214,3 +216,126 @@ jobs: delay=$((delay*2)) done echo "โœ… MCP Registry publish succeeded." + + build_and_package_macos: + needs: release + strategy: + fail-fast: false + matrix: + include: + - arch: arm64 + runner: macos-14 + - arch: x64 + runner: macos-13 + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: Package portable artifact + run: | + npm run package:macos -- --arch "${{ matrix.arch }}" --version "${{ needs.release.outputs.version }}" + + - name: Verify portable artifact + run: | + npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz" + + - name: Upload arch artifact + uses: actions/upload-artifact@v4 + with: + name: portable-${{ matrix.arch }} + path: | + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }} + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz.sha256 + + build_universal_and_verify: + needs: [release, build_and_package_macos] + runs-on: macos-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: Download arm64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-arm64 + path: dist/portable/arm64 + + - name: Download x64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-x64 + path: dist/portable/x64 + + - name: Build universal portable artifact + run: | + npm run package:macos:universal -- \ + --version "${{ needs.release.outputs.version }}" \ + --arm64-root "dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64" \ + --x64-root "dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64" + + - name: Verify universal portable artifact + run: | + npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz" + + - name: Upload universal artifact + uses: actions/upload-artifact@v4 + with: + name: portable-universal + path: | + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz + dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256 + + publish_portable_assets: + if: github.event_name == 'push' + needs: [release, build_universal_and_verify] + runs-on: ubuntu-latest + steps: + - name: Download arm64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-arm64 + path: dist/portable/arm64 + + - name: Download x64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-x64 + path: dist/portable/x64 + + - name: Download universal artifact + uses: actions/download-artifact@v4 + with: + name: portable-universal + path: dist/portable/universal + + - name: Upload portable assets to GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload "v${{ needs.release.outputs.version }}" \ + dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz \ + dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz.sha256 \ + dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz \ + dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz.sha256 \ + dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz \ + dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256 \ + --clobber From 058b6f81aaeae05986cf4063b7de342d092021ef Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 15:26:57 +0000 Subject: [PATCH 05/28] ci(homebrew): Automate tap formula updates Add a Homebrew formula generator script and release workflow automation that computes artifact SHAs, generates Formula/xcodebuildmcp.rb, and opens a PR in cameroncooke/homebrew-xcodebuildmcp when credentials are configured. Keep the tap update flow token-gated and best-effort so releases continue when tap credentials are unavailable. Co-Authored-By: Claude --- .github/workflows/release.yml | 68 +++++++++++++++++++++++ package.json | 1 + scripts/create-homebrew-formula.sh | 88 ++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100755 scripts/create-homebrew-formula.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 539473e8..2323cf5f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -339,3 +339,71 @@ jobs: dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz \ dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256 \ --clobber + + update_homebrew_tap: + if: github.event_name == 'push' + needs: [release, publish_portable_assets] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Download arm64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-arm64 + path: dist/portable/arm64 + + - name: Download x64 artifact + uses: actions/download-artifact@v4 + with: + name: portable-x64 + path: dist/portable/x64 + + - name: Skip when tap token is unavailable + if: secrets.HOMEBREW_TAP_TOKEN == '' + run: echo "HOMEBREW_TAP_TOKEN is not set; skipping Homebrew tap update." + + - name: Generate formula + if: secrets.HOMEBREW_TAP_TOKEN != '' + run: | + VERSION="${{ needs.release.outputs.version }}" + ARM64_SHA="$(awk '{print $1}' dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256)" + X64_SHA="$(awk '{print $1}' dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256)" + npm run homebrew:formula -- \ + --version "$VERSION" \ + --arm64-sha "$ARM64_SHA" \ + --x64-sha "$X64_SHA" \ + --out dist/homebrew/Formula/xcodebuildmcp.rb + + - name: Create pull request in tap repo + if: secrets.HOMEBREW_TAP_TOKEN != '' + env: + GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + VERSION="${{ needs.release.outputs.version }}" + BRANCH="xcodebuildmcp-v${VERSION}" + git clone "https://x-access-token:${GH_TOKEN}@github.com/cameroncooke/homebrew-xcodebuildmcp.git" tap-repo + cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb + cd tap-repo + if git diff --quiet Formula/xcodebuildmcp.rb; then + echo "Formula already up to date; skipping PR." + exit 0 + fi + git checkout -b "$BRANCH" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Formula/xcodebuildmcp.rb + git commit -m "xcodebuildmcp ${VERSION}" + git push origin "$BRANCH" + gh pr create \ + --repo cameroncooke/homebrew-xcodebuildmcp \ + --title "Update xcodebuildmcp to ${VERSION}" \ + --body "Automated formula update for xcodebuildmcp ${VERSION}." \ + --base main \ + --head "$BRANCH" diff --git a/package.json b/package.json index d85b26e9..95193bf1 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "package:macos": "scripts/package-macos-portable.sh", "package:macos:universal": "scripts/package-macos-portable.sh --universal", "verify:portable": "scripts/verify-portable-install.sh", + "homebrew:formula": "scripts/create-homebrew-formula.sh", "lint": "eslint 'src/**/*.{js,ts}'", "lint:fix": "eslint 'src/**/*.{js,ts}' --fix", "format": "prettier --write 'src/**/*.{js,ts}'", diff --git a/scripts/create-homebrew-formula.sh b/scripts/create-homebrew-formula.sh new file mode 100755 index 00000000..11304dda --- /dev/null +++ b/scripts/create-homebrew-formula.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +set -euo pipefail + +VERSION="" +ARM64_SHA="" +X64_SHA="" +OUT_PATH="" + +usage() { + cat <<'EOF' +Usage: + scripts/create-homebrew-formula.sh --version --arm64-sha --x64-sha [--out ] +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --version) + VERSION="${2:-}" + shift 2 + ;; + --arm64-sha) + ARM64_SHA="${2:-}" + shift 2 + ;; + --x64-sha) + X64_SHA="${2:-}" + shift 2 + ;; + --out) + OUT_PATH="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" + usage + exit 1 + ;; + esac +done + +if [[ -z "$VERSION" || -z "$ARM64_SHA" || -z "$X64_SHA" ]]; then + usage + exit 1 +fi + +FORMULA_CONTENT="$(cat < "$OUT_PATH" +else + printf "%s\n" "$FORMULA_CONTENT" +fi + From 8365d568d68c5db3d165523221edfd64356609a5 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 15:53:42 +0000 Subject: [PATCH 06/28] fix(portable): Correct arch runtime packaging and brew install layout Download the Node runtime for the requested target architecture instead of\ncopying the host runtime so x64 packaging and universal lipo assembly\nwork reliably from arm64 builders.\n\nInstall portable archive contents at formula prefix to preserve wrapper\nresource-root path resolution during Homebrew installs.\n\nCo-Authored-By: Claude --- scripts/create-homebrew-formula.sh | 5 +--- scripts/package-macos-portable.sh | 42 ++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/scripts/create-homebrew-formula.sh b/scripts/create-homebrew-formula.sh index 11304dda..8e2a9446 100755 --- a/scripts/create-homebrew-formula.sh +++ b/scripts/create-homebrew-formula.sh @@ -67,9 +67,7 @@ class Xcodebuildmcp < Formula end def install - libexec.install Dir["*"] - bin.install_symlink libexec/"bin/xcodebuildmcp" - bin.install_symlink libexec/"bin/xcodebuildmcp-doctor" + prefix.install Dir["*"] end test do @@ -85,4 +83,3 @@ if [[ -n "$OUT_PATH" ]]; then else printf "%s\n" "$FORMULA_CONTENT" fi - diff --git a/scripts/package-macos-portable.sh b/scripts/package-macos-portable.sh index 7f29b18e..00fe6ad8 100755 --- a/scripts/package-macos-portable.sh +++ b/scripts/package-macos-portable.sh @@ -115,6 +115,45 @@ verify_axe_assets() { fi } +install_node_runtime_for_arch() { + local target_arch="$1" + local output_path="$2" + local node_version="${NODE_RUNTIME_VERSION:-$(node -p "process.versions.node")}" + local node_arch="" + + case "$target_arch" in + arm64) + node_arch="arm64" + ;; + x64) + node_arch="x64" + ;; + *) + echo "Unsupported target arch for Node runtime: $target_arch" + exit 1 + ;; + esac + + local archive_name="node-v${node_version}-darwin-${node_arch}.tar.gz" + local download_url="https://nodejs.org/dist/v${node_version}/${archive_name}" + local temp_dir + temp_dir="$(mktemp -d)" + + curl -fLsS "$download_url" -o "$temp_dir/$archive_name" + tar -xzf "$temp_dir/$archive_name" -C "$temp_dir" + + local extracted_node="$temp_dir/node-v${node_version}-darwin-${node_arch}/bin/node" + if [[ ! -x "$extracted_node" ]]; then + echo "Failed to locate extracted Node runtime at $extracted_node" + rm -r "$temp_dir" + exit 1 + fi + + cp "$extracted_node" "$output_path" + chmod +x "$output_path" + rm -r "$temp_dir" +} + write_wrapper_scripts() { local root="$1" local bin_dir="$root/bin" @@ -223,8 +262,7 @@ if [[ -d "$PORTABLE_ROOT" ]]; then fi mkdir -p "$PORTABLE_ROOT/bin" "$PORTABLE_ROOT/libexec" -cp "$(command -v node)" "$PORTABLE_ROOT/libexec/node-runtime" -chmod +x "$PORTABLE_ROOT/libexec/node-runtime" +install_node_runtime_for_arch "$ARCH" "$PORTABLE_ROOT/libexec/node-runtime" cp -R "$PROJECT_ROOT/build" "$PORTABLE_ROOT/libexec/" cp -R "$PROJECT_ROOT/manifests" "$PORTABLE_ROOT/libexec/" From 41c46677697f541e2618378314a796ae42e957c7 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 18:05:56 +0000 Subject: [PATCH 07/28] fix(build): Guard import.meta usage for CJS Smithery builds Smithery capability scanning runs a CJS bundle in CI. Accessing fileURLToPath(importMetaUrl) without a guard can throw when import.meta is present but does not include a usable url, which broke the build-and-test job. Guard the import-meta candidate so package-root discovery still works via process.cwd() and argv fallbacks in CJS environments. Co-Authored-By: Claude --- src/core/resource-root.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/resource-root.ts b/src/core/resource-root.ts index 77715691..80e3cb6f 100644 --- a/src/core/resource-root.ts +++ b/src/core/resource-root.ts @@ -21,7 +21,10 @@ function findPackageRootFrom(startDir: string): string | null { export function getPackageRoot(): string { const candidates: string[] = []; - candidates.push(path.dirname(fileURLToPath(import.meta.url))); + const importMetaUrl = typeof import.meta.url === 'string' ? import.meta.url : null; + if (importMetaUrl) { + candidates.push(path.dirname(fileURLToPath(importMetaUrl))); + } candidates.push(process.cwd()); const entry = process.argv[1]; if (entry) { From 228ecb467c85708a14c709f5aee55311d90668a3 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 18:18:29 +0000 Subject: [PATCH 08/28] docs(migration): Fix invalid CLI command reference Rename the section heading to avoid the docs command validator parsing "is now" as a CLI subcommand reference in MIGRATION_V2. Co-Authored-By: Claude --- docs/MIGRATION_V2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MIGRATION_V2.md b/docs/MIGRATION_V2.md index b3bbe7d8..cfba277b 100644 --- a/docs/MIGRATION_V2.md +++ b/docs/MIGRATION_V2.md @@ -187,7 +187,7 @@ For full details on configuration options see [CONFIGURATION.md](CONFIGURATION.m ## 3. CLI and skills -### xcodebuildmcp is now a CLI +### CLI mode for xcodebuildmcp The `xcodebuildmcp` command can now be used directly in the terminal without an MCP client: From c5e0b217e43e1e8adf2f94afb96bea95eb82e824 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 18:35:58 +0000 Subject: [PATCH 09/28] fix(docs-check): Validate CLI refs only in code examples Restrict docs command validation to fenced and inline code snippets so prose headings are not misclassified as CLI invocations. Restore the original migration heading text now that scanner behavior is context-aware. Co-Authored-By: Claude --- docs/MIGRATION_V2.md | 2 +- scripts/check-docs-cli-commands.js | 53 +++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/docs/MIGRATION_V2.md b/docs/MIGRATION_V2.md index cfba277b..b3bbe7d8 100644 --- a/docs/MIGRATION_V2.md +++ b/docs/MIGRATION_V2.md @@ -187,7 +187,7 @@ For full details on configuration options see [CONFIGURATION.md](CONFIGURATION.m ## 3. CLI and skills -### CLI mode for xcodebuildmcp +### xcodebuildmcp is now a CLI The `xcodebuildmcp` command can now be used directly in the terminal without an MCP client: diff --git a/scripts/check-docs-cli-commands.js b/scripts/check-docs-cli-commands.js index f77ac981..49e117e7 100755 --- a/scripts/check-docs-cli-commands.js +++ b/scripts/check-docs-cli-commands.js @@ -81,6 +81,49 @@ function buildValidationSets(catalog) { return { validPairs, validWorkflows }; } +function extractCommandCandidates(content) { + const lines = content.split(/\r?\n/u); + const candidates = []; + const inlineCodeRegex = /`([^`\n]+)`/g; + const fenceHeaderRegex = /^\s*```([a-z0-9_-]*)\s*$/iu; + const codeFenceLanguages = new Set(['', 'bash', 'sh', 'zsh', 'shell', 'console']); + + let inFence = false; + let shouldScanFence = false; + + for (let lineNumber = 1; lineNumber <= lines.length; lineNumber += 1) { + const line = lines[lineNumber - 1]; + const fenceMatch = line.match(fenceHeaderRegex); + + if (fenceMatch) { + if (!inFence) { + inFence = true; + shouldScanFence = codeFenceLanguages.has(fenceMatch[1].toLowerCase()); + } else { + inFence = false; + shouldScanFence = false; + } + continue; + } + + if (inFence) { + if (shouldScanFence) { + candidates.push({ lineNumber, text: line }); + } + continue; + } + + inlineCodeRegex.lastIndex = 0; + let inlineMatch = inlineCodeRegex.exec(line); + while (inlineMatch) { + candidates.push({ lineNumber, text: inlineMatch[1] }); + inlineMatch = inlineCodeRegex.exec(line); + } + } + + return candidates; +} + function findInvalidCommands(files, validPairs, validWorkflows) { const validTopLevel = new Set(['mcp', 'tools', 'daemon']); const validDaemonActions = new Set(['status', 'start', 'stop', 'restart', 'list']); @@ -92,12 +135,12 @@ function findInvalidCommands(files, validPairs, validWorkflows) { for (const absoluteFilePath of files) { const relativePath = path.relative(repoRoot, absoluteFilePath) || absoluteFilePath; const content = readFileSync(absoluteFilePath, 'utf8'); - const lines = content.split(/\r?\n/u); + const candidates = extractCommandCandidates(content); - for (let lineNumber = 1; lineNumber <= lines.length; lineNumber += 1) { - const line = lines[lineNumber - 1]; + for (const candidate of candidates) { + const { lineNumber, text } = candidate; commandRegex.lastIndex = 0; - let match = commandRegex.exec(line); + let match = commandRegex.exec(text); while (match) { const first = match[1]; @@ -118,7 +161,7 @@ function findInvalidCommands(files, validPairs, validWorkflows) { findings.push(`${relativePath}:${lineNumber}: ${command}`); } - match = commandRegex.exec(line); + match = commandRegex.exec(text); } } } From d3e343187d016334d266fc1341a9a77cb9ee7ada Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 18:50:48 +0000 Subject: [PATCH 10/28] ci(release): Allow isolated Homebrew tap updates on workflow_dispatch Enable update_homebrew_tap for manual test runs so Homebrew automation can be validated in isolation without tag releases. The workflow_dispatch path still skips npm publish and GitHub release creation because those steps remain push-only. Co-Authored-By: Claude --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2323cf5f..5a423c80 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -341,8 +341,8 @@ jobs: --clobber update_homebrew_tap: - if: github.event_name == 'push' - needs: [release, publish_portable_assets] + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + needs: [release, build_and_package_macos] runs-on: ubuntu-latest steps: - name: Checkout code From 4ba33a2a762d10f85436436942c4e6bed9b46aec Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 18:51:33 +0000 Subject: [PATCH 11/28] ci(release): Use env guard for Homebrew tap token checks Replace secrets context in step if expressions with env-based checks so workflow_dispatch validation succeeds and isolated Homebrew tests can run. Co-Authored-By: Claude --- .github/workflows/release.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a423c80..befd05a5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -344,6 +344,8 @@ jobs: if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' needs: [release, build_and_package_macos] runs-on: ubuntu-latest + env: + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} steps: - name: Checkout code uses: actions/checkout@v4 @@ -366,11 +368,11 @@ jobs: path: dist/portable/x64 - name: Skip when tap token is unavailable - if: secrets.HOMEBREW_TAP_TOKEN == '' + if: env.HOMEBREW_TAP_TOKEN == '' run: echo "HOMEBREW_TAP_TOKEN is not set; skipping Homebrew tap update." - name: Generate formula - if: secrets.HOMEBREW_TAP_TOKEN != '' + if: env.HOMEBREW_TAP_TOKEN != '' run: | VERSION="${{ needs.release.outputs.version }}" ARM64_SHA="$(awk '{print $1}' dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256)" @@ -382,9 +384,9 @@ jobs: --out dist/homebrew/Formula/xcodebuildmcp.rb - name: Create pull request in tap repo - if: secrets.HOMEBREW_TAP_TOKEN != '' + if: env.HOMEBREW_TAP_TOKEN != '' env: - GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + GH_TOKEN: ${{ env.HOMEBREW_TAP_TOKEN }} run: | VERSION="${{ needs.release.outputs.version }}" BRANCH="xcodebuildmcp-v${VERSION}" From 189af39dcdd97e81431f117785767547e0967d8a Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 20:40:11 +0000 Subject: [PATCH 12/28] ci(release): Add npm tag for workflow_dispatch dry-run publish Use an explicit npm tag during manual dry-run publish so prerelease test versions do not fail workflow_dispatch runs. Co-Authored-By: Claude --- .github/workflows/release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index befd05a5..a74e16fc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,7 +88,13 @@ jobs: if: github.event_name == 'workflow_dispatch' run: | echo "๐Ÿงช Testing package creation (dry run)" - npm publish --dry-run --access public + VERSION="${{ steps.get_version.outputs.VERSION }}" + if [[ "$VERSION" == *"-"* ]]; then + NPM_TAG="next" + else + NPM_TAG="latest" + fi + npm publish --dry-run --access public --tag "$NPM_TAG" - name: Publish to NPM (production releases only) if: github.event_name == 'push' From 96e4d1d86cea9b1fe5e74358d63a480f53c99dfd Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 20:44:22 +0000 Subject: [PATCH 13/28] ci(release): Use macos-14 for both portable arch builds The configured macos-13 hosted runner is unavailable in this environment. Build both arm64 and x64 portable artifacts on macos-14, which is valid now that packaging fetches target-arch Node runtimes. Co-Authored-By: Claude --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a74e16fc..5a25d877 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -232,7 +232,7 @@ jobs: - arch: arm64 runner: macos-14 - arch: x64 - runner: macos-13 + runner: macos-14 runs-on: ${{ matrix.runner }} steps: - name: Checkout code From 255795ffe97e6d90820feb393998cdf0eef29b52 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 20:53:10 +0000 Subject: [PATCH 14/28] ci(release): Handle artifact roots and empty tap bootstrap Resolve portable artifact roots dynamically after download to avoid path layout mismatches in the universal packaging job. Bootstrap an empty Homebrew tap by creating Formula/, committing to the default branch, and exiting cleanly. For non-empty repos, detect default branch for PR base and keep formula updates on version branch. Co-Authored-By: Claude --- .github/workflows/release.yml | 45 +++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a25d877..6aa34d47 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -290,12 +290,29 @@ jobs: name: portable-x64 path: dist/portable/x64 + - name: Resolve extracted arch roots + id: resolve_roots + run: | + VERSION="${{ needs.release.outputs.version }}" + ARM64_ROOT="$(find dist/portable/arm64 -type d -name "xcodebuildmcp-${VERSION}-darwin-arm64" -print -quit)" + X64_ROOT="$(find dist/portable/x64 -type d -name "xcodebuildmcp-${VERSION}-darwin-x64" -print -quit)" + if [ -z "$ARM64_ROOT" ] || [ -z "$X64_ROOT" ]; then + echo "Failed to resolve extracted arch roots." + echo "arm64 root: ${ARM64_ROOT:-}" + echo "x64 root: ${X64_ROOT:-}" + echo "--- dist/portable tree (depth 4) ---" + find dist/portable -maxdepth 4 -print + exit 1 + fi + echo "ARM64_ROOT=$ARM64_ROOT" >> "$GITHUB_OUTPUT" + echo "X64_ROOT=$X64_ROOT" >> "$GITHUB_OUTPUT" + - name: Build universal portable artifact run: | npm run package:macos:universal -- \ --version "${{ needs.release.outputs.version }}" \ - --arm64-root "dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64" \ - --x64-root "dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64" + --arm64-root "${{ steps.resolve_roots.outputs.ARM64_ROOT }}" \ + --x64-root "${{ steps.resolve_roots.outputs.X64_ROOT }}" - name: Verify universal portable artifact run: | @@ -396,22 +413,36 @@ jobs: run: | VERSION="${{ needs.release.outputs.version }}" BRANCH="xcodebuildmcp-v${VERSION}" + DEFAULT_BRANCH="main" git clone "https://x-access-token:${GH_TOKEN}@github.com/cameroncooke/homebrew-xcodebuildmcp.git" tap-repo + mkdir -p tap-repo/Formula cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb cd tap-repo + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + if ! git rev-parse --verify HEAD >/dev/null 2>&1; then + echo "Tap repo has no commits; bootstrapping ${DEFAULT_BRANCH}." + git checkout -b "$DEFAULT_BRANCH" + git add Formula/xcodebuildmcp.rb + git commit -m "Initialize xcodebuildmcp formula ${VERSION}" + git push origin "$DEFAULT_BRANCH" + exit 0 + fi + DETECTED_BRANCH="$(gh repo view cameroncooke/homebrew-xcodebuildmcp --json defaultBranchRef -q .defaultBranchRef.name 2>/dev/null || true)" + if [ -n "$DETECTED_BRANCH" ]; then + DEFAULT_BRANCH="$DETECTED_BRANCH" + fi if git diff --quiet Formula/xcodebuildmcp.rb; then echo "Formula already up to date; skipping PR." exit 0 fi - git checkout -b "$BRANCH" - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -B "$BRANCH" git add Formula/xcodebuildmcp.rb git commit -m "xcodebuildmcp ${VERSION}" - git push origin "$BRANCH" + git push --set-upstream origin "$BRANCH" gh pr create \ --repo cameroncooke/homebrew-xcodebuildmcp \ --title "Update xcodebuildmcp to ${VERSION}" \ --body "Automated formula update for xcodebuildmcp ${VERSION}." \ - --base main \ + --base "$DEFAULT_BRANCH" \ --head "$BRANCH" From 355606f42a7eaa344e414349aa512d3fd1acee24 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Mon, 9 Feb 2026 21:07:17 +0000 Subject: [PATCH 15/28] ci(release): Build universal package from tar archives Untar per-arch portable archives into deterministic unpack roots before running universal packaging. This avoids runtime-root resolution issues from artifact directory layout differences. Co-Authored-By: Claude --- .github/workflows/release.yml | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6aa34d47..697b5d0b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -290,20 +290,17 @@ jobs: name: portable-x64 path: dist/portable/x64 - - name: Resolve extracted arch roots - id: resolve_roots + - name: Expand per-arch archives + id: expand_archives run: | VERSION="${{ needs.release.outputs.version }}" - ARM64_ROOT="$(find dist/portable/arm64 -type d -name "xcodebuildmcp-${VERSION}-darwin-arm64" -print -quit)" - X64_ROOT="$(find dist/portable/x64 -type d -name "xcodebuildmcp-${VERSION}-darwin-x64" -print -quit)" - if [ -z "$ARM64_ROOT" ] || [ -z "$X64_ROOT" ]; then - echo "Failed to resolve extracted arch roots." - echo "arm64 root: ${ARM64_ROOT:-}" - echo "x64 root: ${X64_ROOT:-}" - echo "--- dist/portable tree (depth 4) ---" - find dist/portable -maxdepth 4 -print - exit 1 - fi + ARM64_TGZ="dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz" + X64_TGZ="dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz" + ARM64_ROOT="dist/portable/unpacked/arm64/xcodebuildmcp-${VERSION}-darwin-arm64" + X64_ROOT="dist/portable/unpacked/x64/xcodebuildmcp-${VERSION}-darwin-x64" + mkdir -p dist/portable/unpacked/arm64 dist/portable/unpacked/x64 + tar -xzf "$ARM64_TGZ" -C dist/portable/unpacked/arm64 + tar -xzf "$X64_TGZ" -C dist/portable/unpacked/x64 echo "ARM64_ROOT=$ARM64_ROOT" >> "$GITHUB_OUTPUT" echo "X64_ROOT=$X64_ROOT" >> "$GITHUB_OUTPUT" @@ -311,8 +308,8 @@ jobs: run: | npm run package:macos:universal -- \ --version "${{ needs.release.outputs.version }}" \ - --arm64-root "${{ steps.resolve_roots.outputs.ARM64_ROOT }}" \ - --x64-root "${{ steps.resolve_roots.outputs.X64_ROOT }}" + --arm64-root "${{ steps.expand_archives.outputs.ARM64_ROOT }}" \ + --x64-root "${{ steps.expand_archives.outputs.X64_ROOT }}" - name: Verify universal portable artifact run: | From dce99617b5f1598c4237850b5ae157cdb3349dbd Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 08:19:53 +0000 Subject: [PATCH 16/28] ci(release): Host test Homebrew artifacts from tap repo Allow formula generation to accept a configurable base URL. For workflow_dispatch runs, publish portable archives into the tap repo and point formula URLs at raw artifact paths so Homebrew install can be tested end-to-end without creating GitHub releases or publishing to npm. Co-Authored-By: Claude --- .github/workflows/release.yml | 22 ++++++++++++++++++++++ scripts/create-homebrew-formula.sh | 15 ++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 697b5d0b..8a9ab1f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -395,12 +395,17 @@ jobs: if: env.HOMEBREW_TAP_TOKEN != '' run: | VERSION="${{ needs.release.outputs.version }}" + FORMULA_BASE_URL="https://github.com/cameroncooke/XcodeBuildMCP/releases/download/v${VERSION}" + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + FORMULA_BASE_URL="https://raw.githubusercontent.com/cameroncooke/homebrew-xcodebuildmcp/main/artifacts/${VERSION}" + fi ARM64_SHA="$(awk '{print $1}' dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256)" X64_SHA="$(awk '{print $1}' dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256)" npm run homebrew:formula -- \ --version "$VERSION" \ --arm64-sha "$ARM64_SHA" \ --x64-sha "$X64_SHA" \ + --base-url "$FORMULA_BASE_URL" \ --out dist/homebrew/Formula/xcodebuildmcp.rb - name: Create pull request in tap repo @@ -411,8 +416,19 @@ jobs: VERSION="${{ needs.release.outputs.version }}" BRANCH="xcodebuildmcp-v${VERSION}" DEFAULT_BRANCH="main" + ADD_TAP_ARTIFACTS="false" + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + ADD_TAP_ARTIFACTS="true" + fi git clone "https://x-access-token:${GH_TOKEN}@github.com/cameroncooke/homebrew-xcodebuildmcp.git" tap-repo mkdir -p tap-repo/Formula + if [ "$ADD_TAP_ARTIFACTS" = "true" ]; then + mkdir -p "tap-repo/artifacts/${VERSION}" + cp "dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz" "tap-repo/artifacts/${VERSION}/" + cp "dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256" "tap-repo/artifacts/${VERSION}/" + cp "dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz" "tap-repo/artifacts/${VERSION}/" + cp "dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256" "tap-repo/artifacts/${VERSION}/" + fi cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb cd tap-repo git config user.name "github-actions[bot]" @@ -421,6 +437,9 @@ jobs: echo "Tap repo has no commits; bootstrapping ${DEFAULT_BRANCH}." git checkout -b "$DEFAULT_BRANCH" git add Formula/xcodebuildmcp.rb + if [ "$ADD_TAP_ARTIFACTS" = "true" ]; then + git add "artifacts/${VERSION}" + fi git commit -m "Initialize xcodebuildmcp formula ${VERSION}" git push origin "$DEFAULT_BRANCH" exit 0 @@ -435,6 +454,9 @@ jobs: fi git checkout -B "$BRANCH" git add Formula/xcodebuildmcp.rb + if [ "$ADD_TAP_ARTIFACTS" = "true" ]; then + git add "artifacts/${VERSION}" + fi git commit -m "xcodebuildmcp ${VERSION}" git push --set-upstream origin "$BRANCH" gh pr create \ diff --git a/scripts/create-homebrew-formula.sh b/scripts/create-homebrew-formula.sh index 8e2a9446..f72c3427 100755 --- a/scripts/create-homebrew-formula.sh +++ b/scripts/create-homebrew-formula.sh @@ -6,11 +6,12 @@ VERSION="" ARM64_SHA="" X64_SHA="" OUT_PATH="" +BASE_URL="" usage() { cat <<'EOF' Usage: - scripts/create-homebrew-formula.sh --version --arm64-sha --x64-sha [--out ] + scripts/create-homebrew-formula.sh --version --arm64-sha --x64-sha [--base-url ] [--out ] EOF } @@ -28,6 +29,10 @@ while [[ $# -gt 0 ]]; do X64_SHA="${2:-}" shift 2 ;; + --base-url) + BASE_URL="${2:-}" + shift 2 + ;; --out) OUT_PATH="${2:-}" shift 2 @@ -49,6 +54,10 @@ if [[ -z "$VERSION" || -z "$ARM64_SHA" || -z "$X64_SHA" ]]; then exit 1 fi +if [[ -z "$BASE_URL" ]]; then + BASE_URL="https://github.com/cameroncooke/XcodeBuildMCP/releases/download/v$VERSION" +fi + FORMULA_CONTENT="$(cat < Date: Tue, 10 Feb 2026 08:29:57 +0000 Subject: [PATCH 17/28] fix(portable): Resolve symlinked bin path for Homebrew installs Make portable launcher wrappers resolve symlink targets before deriving resource paths. This keeps libexec lookup correct when binaries are invoked via /opt/homebrew/bin symlinks. Co-Authored-By: Claude --- scripts/package-macos-portable.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/package-macos-portable.sh b/scripts/package-macos-portable.sh index 00fe6ad8..8510203a 100755 --- a/scripts/package-macos-portable.sh +++ b/scripts/package-macos-portable.sh @@ -169,7 +169,14 @@ EOF cat > "$bin_dir/xcodebuildmcp" <<'EOF' #!/usr/bin/env bash set -euo pipefail -RESOURCE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../libexec" && pwd)" +SOURCE="${BASH_SOURCE[0]}" +while [[ -L "$SOURCE" ]]; do + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" +RESOURCE_ROOT="$(cd "$SCRIPT_DIR/../libexec" && pwd)" export XCODEBUILDMCP_RESOURCE_ROOT="$RESOURCE_ROOT" export DYLD_FRAMEWORK_PATH="$RESOURCE_ROOT/bundled/Frameworks${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH}" exec "$RESOURCE_ROOT/xcodebuildmcp" "$@" @@ -178,7 +185,14 @@ EOF cat > "$bin_dir/xcodebuildmcp-doctor" <<'EOF' #!/usr/bin/env bash set -euo pipefail -RESOURCE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../libexec" && pwd)" +SOURCE="${BASH_SOURCE[0]}" +while [[ -L "$SOURCE" ]]; do + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" +RESOURCE_ROOT="$(cd "$SCRIPT_DIR/../libexec" && pwd)" export XCODEBUILDMCP_RESOURCE_ROOT="$RESOURCE_ROOT" export DYLD_FRAMEWORK_PATH="$RESOURCE_ROOT/bundled/Frameworks${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH}" exec "$RESOURCE_ROOT/xcodebuildmcp" doctor "$@" From 6ab295979aed483f2ca405cadb54ae635c12d2e1 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 09:06:39 +0000 Subject: [PATCH 18/28] build(portable): Prefer AXe Homebrew archive for bundling Use AXe's Homebrew-specific unsigned archive when available and fall back\nto the legacy signed archive for older releases. This aligns the\nbundling pipeline with AXe's Homebrew packaging and reduces signature\nnoise during downstream install flows.\n\nCo-Authored-By: Claude --- scripts/bundle-axe.sh | 61 +++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/scripts/bundle-axe.sh b/scripts/bundle-axe.sh index e9228c1e..80f988bf 100755 --- a/scripts/bundle-axe.sh +++ b/scripts/bundle-axe.sh @@ -38,6 +38,7 @@ fi mkdir -p "$BUNDLED_DIR" USE_LOCAL_AXE=false +AXE_ARCHIVE_FLAVOR="local-signed" if [ -z "${AXE_FORCE_REMOTE}" ] && [ "${AXE_USE_LOCAL:-0}" = "1" ]; then USE_LOCAL_AXE=true fi @@ -92,16 +93,26 @@ else echo "๐Ÿ“ฅ Downloading latest AXe release from GitHub..." - # Construct release download URL from pinned version - AXE_RELEASE_URL="https://github.com/cameroncooke/AXe/releases/download/v${PINNED_AXE_VERSION}/AXe-macOS-v${PINNED_AXE_VERSION}.tar.gz" + # Prefer Homebrew-specific archive (unsigned for relocation compatibility), + # and fall back to legacy signed archive for older AXe releases. + AXE_RELEASE_BASE_URL="https://github.com/cameroncooke/AXe/releases/download/v${PINNED_AXE_VERSION}" + AXE_HOMEBREW_URL="${AXE_RELEASE_BASE_URL}/AXe-macOS-homebrew-v${PINNED_AXE_VERSION}.tar.gz" + AXE_LEGACY_URL="${AXE_RELEASE_BASE_URL}/AXe-macOS-v${PINNED_AXE_VERSION}.tar.gz" # Create temp directory mkdir -p "$AXE_TEMP_DIR" cd "$AXE_TEMP_DIR" # Download and extract the release - echo "๐Ÿ“ฅ Downloading AXe release archive ($AXE_RELEASE_URL)..." - curl -L -o "axe-release.tar.gz" "$AXE_RELEASE_URL" + echo "๐Ÿ“ฅ Downloading AXe Homebrew archive ($AXE_HOMEBREW_URL)..." + if curl -fL -o "axe-release.tar.gz" "$AXE_HOMEBREW_URL"; then + AXE_ARCHIVE_FLAVOR="homebrew-unsigned" + echo "โœ… Downloaded AXe Homebrew archive" + else + echo "โš ๏ธ AXe Homebrew archive unavailable, falling back to legacy archive" + curl -fL -o "axe-release.tar.gz" "$AXE_LEGACY_URL" + AXE_ARCHIVE_FLAVOR="legacy-signed" + fi echo "๐Ÿ“ฆ Extracting AXe release archive..." tar -xzf "axe-release.tar.gz" @@ -157,27 +168,31 @@ ls -la "$BUNDLED_DIR/Frameworks/" # Verify binary can run with bundled frameworks (macOS only) OS_NAME="$(uname -s)" if [ "$OS_NAME" = "Darwin" ]; then - echo "๐Ÿ” Verifying AXe signatures..." - if ! codesign --verify --deep --strict "$BUNDLED_DIR/axe"; then - echo "โŒ Signature verification failed for bundled AXe binary" - exit 1 - fi - - while IFS= read -r framework_path; do - framework_name="$(basename "$framework_path" .framework)" - framework_binary="$framework_path/Versions/A/$framework_name" - if [ ! -f "$framework_binary" ]; then - framework_binary="$framework_path/Versions/Current/$framework_name" - fi - if [ ! -f "$framework_binary" ]; then - echo "โŒ Framework binary not found: $framework_binary" - exit 1 - fi - if ! codesign --verify --deep --strict "$framework_binary"; then - echo "โŒ Signature verification failed for framework binary: $framework_binary" + if [ "$AXE_ARCHIVE_FLAVOR" = "homebrew-unsigned" ]; then + echo "โ„น๏ธ Skipping strict codesign verification for unsigned AXe Homebrew archive" + else + echo "๐Ÿ” Verifying AXe signatures..." + if ! codesign --verify --deep --strict "$BUNDLED_DIR/axe"; then + echo "โŒ Signature verification failed for bundled AXe binary" exit 1 fi - done < <(find "$BUNDLED_DIR/Frameworks" -name "*.framework" -type d) + + while IFS= read -r framework_path; do + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [ ! -f "$framework_binary" ]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + if [ ! -f "$framework_binary" ]; then + echo "โŒ Framework binary not found: $framework_binary" + exit 1 + fi + if ! codesign --verify --deep --strict "$framework_binary"; then + echo "โŒ Signature verification failed for framework binary: $framework_binary" + exit 1 + fi + done < <(find "$BUNDLED_DIR/Frameworks" -name "*.framework" -type d) + fi echo "๐Ÿ›ก๏ธ Assessing AXe with Gatekeeper..." SPCTL_LOG="$(mktemp)" From 6d607914a04af0763524a1a0f0f54411e8fdef9c Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 09:09:19 +0000 Subject: [PATCH 19/28] build(portable): Skip Gatekeeper check for unsigned AXe archive AXe's Homebrew artifact is intentionally unsigned, so Gatekeeper assessment\nreliably fails during CI bundling. Skip only that check for the unsigned\narchive flavor while keeping runtime execution validation in place.\n\nCo-Authored-By: Claude --- scripts/bundle-axe.sh | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/scripts/bundle-axe.sh b/scripts/bundle-axe.sh index 80f988bf..2243ff4f 100755 --- a/scripts/bundle-axe.sh +++ b/scripts/bundle-axe.sh @@ -194,19 +194,23 @@ if [ "$OS_NAME" = "Darwin" ]; then done < <(find "$BUNDLED_DIR/Frameworks" -name "*.framework" -type d) fi - echo "๐Ÿ›ก๏ธ Assessing AXe with Gatekeeper..." - SPCTL_LOG="$(mktemp)" - if ! spctl --assess --type execute "$BUNDLED_DIR/axe" 2>"$SPCTL_LOG"; then - if grep -q "does not seem to be an app" "$SPCTL_LOG"; then - echo "โš ๏ธ Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" - else - cat "$SPCTL_LOG" - echo "โŒ Gatekeeper assessment failed for bundled AXe binary" - rm "$SPCTL_LOG" - exit 1 + if [ "$AXE_ARCHIVE_FLAVOR" = "homebrew-unsigned" ]; then + echo "โ„น๏ธ Skipping Gatekeeper assessment for unsigned AXe Homebrew archive" + else + echo "๐Ÿ›ก๏ธ Assessing AXe with Gatekeeper..." + SPCTL_LOG="$(mktemp)" + if ! spctl --assess --type execute "$BUNDLED_DIR/axe" 2>"$SPCTL_LOG"; then + if grep -q "does not seem to be an app" "$SPCTL_LOG"; then + echo "โš ๏ธ Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" + else + cat "$SPCTL_LOG" + echo "โŒ Gatekeeper assessment failed for bundled AXe binary" + rm "$SPCTL_LOG" + exit 1 + fi fi + rm "$SPCTL_LOG" fi - rm "$SPCTL_LOG" echo "๐Ÿงช Testing bundled AXe binary..." if DYLD_FRAMEWORK_PATH="$BUNDLED_DIR/Frameworks" "$BUNDLED_DIR/axe" --version > /dev/null 2>&1; then From 05c78bc1822f0fe89c9c579db38280128d802e74 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 09:13:39 +0000 Subject: [PATCH 20/28] build(portable): Allow unsigned AXe Homebrew assets Portable packaging currently assumes AXe binaries are signed, which breaks\nwhen consuming AXe's Homebrew-specific unsigned archive. Keep strict\nverification for signed artifacts and fall back to runtime execution\nvalidation for unsigned artifacts.\n\nCo-Authored-By: Claude --- scripts/package-macos-portable.sh | 40 ++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/scripts/package-macos-portable.sh b/scripts/package-macos-portable.sh index 8510203a..42fb5d2d 100755 --- a/scripts/package-macos-portable.sh +++ b/scripts/package-macos-portable.sh @@ -88,7 +88,6 @@ verify_axe_assets() { fi if [[ "$(uname -s)" == "Darwin" ]]; then - codesign --verify --deep --strict "$axe_bin" while IFS= read -r framework_path; do framework_name="$(basename "$framework_path" .framework)" framework_binary="$framework_path/Versions/A/$framework_name" @@ -99,19 +98,38 @@ verify_axe_assets() { echo "Missing framework binary at $framework_binary" exit 1 fi - codesign --verify --deep --strict "$framework_binary" done < <(find "$frameworks_dir" -name "*.framework" -type d) - spctl_log="$(mktemp)" - if ! spctl --assess --type execute "$axe_bin" 2>"$spctl_log"; then - if grep -q "does not seem to be an app" "$spctl_log"; then - echo "Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" - else - cat "$spctl_log" - rm "$spctl_log" - exit 1 + + if codesign -dv "$axe_bin" >/dev/null 2>&1; then + codesign --verify --deep --strict "$axe_bin" + while IFS= read -r framework_path; do + framework_name="$(basename "$framework_path" .framework)" + framework_binary="$framework_path/Versions/A/$framework_name" + if [[ ! -f "$framework_binary" ]]; then + framework_binary="$framework_path/Versions/Current/$framework_name" + fi + codesign --verify --deep --strict "$framework_binary" + done < <(find "$frameworks_dir" -name "*.framework" -type d) + + spctl_log="$(mktemp)" + if ! spctl --assess --type execute "$axe_bin" 2>"$spctl_log"; then + if grep -q "does not seem to be an app" "$spctl_log"; then + echo "Gatekeeper execute assessment is inconclusive for CLI binaries; continuing" + else + cat "$spctl_log" + rm "$spctl_log" + exit 1 + fi fi + rm "$spctl_log" + else + echo "AXe binary is unsigned; skipping codesign and Gatekeeper verification" + fi + + if ! DYLD_FRAMEWORK_PATH="$frameworks_dir" "$axe_bin" --version >/dev/null 2>&1; then + echo "Bundled AXe runtime execution check failed" + exit 1 fi - rm "$spctl_log" fi } From d49b72474b8e4cc292630d4d616919f7bef4631f Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 09:31:42 +0000 Subject: [PATCH 21/28] build(axe): Remove machine-specific local AXe path Stop defaulting AXE_LOCAL_DIR to a user-specific filesystem path.\nLocal AXe bundling now requires explicit AXE_LOCAL_DIR when\nAXE_USE_LOCAL=1, preventing accidental machine-coupled behavior.\n\nCo-Authored-By: Claude --- scripts/bundle-axe.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/bundle-axe.sh b/scripts/bundle-axe.sh index 2243ff4f..7de4f423 100755 --- a/scripts/bundle-axe.sh +++ b/scripts/bundle-axe.sh @@ -8,7 +8,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" BUNDLED_DIR="$PROJECT_ROOT/bundled" -AXE_LOCAL_DIR="${AXE_LOCAL_DIR:-/Users/cameroncooke/Developer/AXe}" +AXE_LOCAL_DIR="${AXE_LOCAL_DIR:-}" AXE_TEMP_DIR="/tmp/axe-download-$$" echo "๐Ÿ”จ Preparing AXe artifacts for bundling..." @@ -87,7 +87,8 @@ if [ "$USE_LOCAL_AXE" = true ] && [ -d "$AXE_LOCAL_DIR" ] && [ -f "$AXE_LOCAL_DI done else if [ "$USE_LOCAL_AXE" = true ]; then - echo "โŒ AXE_USE_LOCAL=1 was set, but AXE_LOCAL_DIR is missing or invalid: $AXE_LOCAL_DIR" + echo "โŒ AXE_USE_LOCAL=1 requires AXE_LOCAL_DIR to point to a valid AXe checkout" + echo " Received AXE_LOCAL_DIR: ${AXE_LOCAL_DIR:-}" exit 1 fi From 04c53f05b69f3ffdb5a88493aa2ac019f7822462 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 09:45:05 +0000 Subject: [PATCH 22/28] ci(release): Automate Homebrew deploys for tag releases Run Homebrew tap updates only for tag-push release runs and push formula\nupdates directly to the tap default branch after release checks pass.\nManual workflow_dispatch runs now avoid Homebrew deployment entirely,\nmatching npm dry-run behavior.\n\nCo-Authored-By: Claude --- .github/workflows/release.yml | 53 ++++++++--------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a9ab1f5..0c54f26c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -361,7 +361,7 @@ jobs: --clobber update_homebrew_tap: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'push' needs: [release, build_and_package_macos] runs-on: ubuntu-latest env: @@ -396,9 +396,6 @@ jobs: run: | VERSION="${{ needs.release.outputs.version }}" FORMULA_BASE_URL="https://github.com/cameroncooke/XcodeBuildMCP/releases/download/v${VERSION}" - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - FORMULA_BASE_URL="https://raw.githubusercontent.com/cameroncooke/homebrew-xcodebuildmcp/main/artifacts/${VERSION}" - fi ARM64_SHA="$(awk '{print $1}' dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256)" X64_SHA="$(awk '{print $1}' dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256)" npm run homebrew:formula -- \ @@ -408,60 +405,34 @@ jobs: --base-url "$FORMULA_BASE_URL" \ --out dist/homebrew/Formula/xcodebuildmcp.rb - - name: Create pull request in tap repo + - name: Update tap repository if: env.HOMEBREW_TAP_TOKEN != '' env: GH_TOKEN: ${{ env.HOMEBREW_TAP_TOKEN }} run: | VERSION="${{ needs.release.outputs.version }}" - BRANCH="xcodebuildmcp-v${VERSION}" DEFAULT_BRANCH="main" - ADD_TAP_ARTIFACTS="false" - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - ADD_TAP_ARTIFACTS="true" - fi git clone "https://x-access-token:${GH_TOKEN}@github.com/cameroncooke/homebrew-xcodebuildmcp.git" tap-repo mkdir -p tap-repo/Formula - if [ "$ADD_TAP_ARTIFACTS" = "true" ]; then - mkdir -p "tap-repo/artifacts/${VERSION}" - cp "dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz" "tap-repo/artifacts/${VERSION}/" - cp "dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256" "tap-repo/artifacts/${VERSION}/" - cp "dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz" "tap-repo/artifacts/${VERSION}/" - cp "dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256" "tap-repo/artifacts/${VERSION}/" - fi cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb cd tap-repo git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - if ! git rev-parse --verify HEAD >/dev/null 2>&1; then - echo "Tap repo has no commits; bootstrapping ${DEFAULT_BRANCH}." - git checkout -b "$DEFAULT_BRANCH" - git add Formula/xcodebuildmcp.rb - if [ "$ADD_TAP_ARTIFACTS" = "true" ]; then - git add "artifacts/${VERSION}" - fi - git commit -m "Initialize xcodebuildmcp formula ${VERSION}" - git push origin "$DEFAULT_BRANCH" - exit 0 - fi DETECTED_BRANCH="$(gh repo view cameroncooke/homebrew-xcodebuildmcp --json defaultBranchRef -q .defaultBranchRef.name 2>/dev/null || true)" if [ -n "$DETECTED_BRANCH" ]; then DEFAULT_BRANCH="$DETECTED_BRANCH" fi - if git diff --quiet Formula/xcodebuildmcp.rb; then - echo "Formula already up to date; skipping PR." - exit 0 + if ! git rev-parse --verify HEAD >/dev/null 2>&1; then + echo "Tap repo has no commits; bootstrapping ${DEFAULT_BRANCH}." + git checkout -b "$DEFAULT_BRANCH" + else + git checkout "$DEFAULT_BRANCH" + git pull --ff-only origin "$DEFAULT_BRANCH" fi - git checkout -B "$BRANCH" git add Formula/xcodebuildmcp.rb - if [ "$ADD_TAP_ARTIFACTS" = "true" ]; then - git add "artifacts/${VERSION}" + if git diff --cached --quiet; then + echo "Formula already up to date; nothing to push." + exit 0 fi git commit -m "xcodebuildmcp ${VERSION}" - git push --set-upstream origin "$BRANCH" - gh pr create \ - --repo cameroncooke/homebrew-xcodebuildmcp \ - --title "Update xcodebuildmcp to ${VERSION}" \ - --body "Automated formula update for xcodebuildmcp ${VERSION}." \ - --base "$DEFAULT_BRANCH" \ - --head "$BRANCH" + git push origin "$DEFAULT_BRANCH" From 22d3d9263bf4d0a953f884f1dcd7d746439211e5 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 10:03:40 +0000 Subject: [PATCH 23/28] ci(portable): Skip execution checks for cross-arch artifacts Make portable verification architecture-aware by checking the packaged\nnode-runtime architecture before running wrapper binaries. When host and\nartifact architectures do not match, keep structural validation and skip\nexecution checks to avoid cross-arch runner failures.\n\nCo-Authored-By: Claude --- scripts/verify-portable-install.sh | 38 +++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/scripts/verify-portable-install.sh b/scripts/verify-portable-install.sh index 53775f30..91c6510f 100755 --- a/scripts/verify-portable-install.sh +++ b/scripts/verify-portable-install.sh @@ -88,8 +88,40 @@ if [[ ! -d "$PORTABLE_ROOT/libexec/bundled/Frameworks" ]]; then exit 1 fi -"$PORTABLE_ROOT/bin/xcodebuildmcp" --help >/dev/null -"$PORTABLE_ROOT/bin/xcodebuildmcp-doctor" --help >/dev/null +HOST_ARCH="$(uname -m)" +NODE_RUNTIME="$PORTABLE_ROOT/libexec/node-runtime" +if [[ ! -x "$NODE_RUNTIME" ]]; then + echo "Missing executable Node runtime under libexec" + exit 1 +fi -echo "Portable install verification passed for: $PORTABLE_ROOT" +RUNTIME_ARCHS="$(lipo -archs "$NODE_RUNTIME" 2>/dev/null || true)" +if [[ -z "$RUNTIME_ARCHS" ]]; then + if file "$NODE_RUNTIME" | grep -q "x86_64"; then + RUNTIME_ARCHS="x86_64" + elif file "$NODE_RUNTIME" | grep -q "arm64"; then + RUNTIME_ARCHS="arm64" + fi +fi + +NORMALIZED_HOST_ARCH="$HOST_ARCH" +if [[ "$HOST_ARCH" == "x86_64" ]]; then + NORMALIZED_HOST_ARCH="x86_64" +fi +CAN_EXECUTE="false" +for runtime_arch in $RUNTIME_ARCHS; do + if [[ "$runtime_arch" == "$NORMALIZED_HOST_ARCH" ]]; then + CAN_EXECUTE="true" + break + fi +done + +if [[ "$CAN_EXECUTE" == "true" ]]; then + "$PORTABLE_ROOT/bin/xcodebuildmcp" --help >/dev/null + "$PORTABLE_ROOT/bin/xcodebuildmcp-doctor" --help >/dev/null +else + echo "Skipping binary execution checks: host arch ($HOST_ARCH) not in runtime archs ($RUNTIME_ARCHS)" +fi + +echo "Portable install verification passed for: $PORTABLE_ROOT" From 8a147fd62cc6012d52542cf363b07706a3ec47f5 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 10:55:39 +0000 Subject: [PATCH 24/28] docs: Unify installation sections and update for v2.0.0 stable Restructure README and GETTING_STARTED installation sections into two clear paths (Homebrew vs npm/npx) with a single-package intro. Replace all stale @beta references with @latest to match the 2.0.0 stable release. Add Homebrew substitution note before client-specific config sections. Document AXe local bundling and release workflow modes. Co-Authored-By: Claude --- README.md | 46 ++++++++++++++++++++------ docs/GETTING_STARTED.md | 66 ++++++++++++++++++++++--------------- docs/dev/CONTRIBUTING.md | 16 +++++++++ docs/dev/RELEASE_PROCESS.md | 28 +++++++++++++++- src/version.ts | 2 +- 5 files changed, 119 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index abc1f57f..9864e8ed 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,39 @@ A Model Context Protocol (MCP) server and CLI that provides tools for agent use ## Installation -### CLI Installation +XcodeBuildMCP ships as a single package with two modes: a **CLI** for direct terminal use and an **MCP server** for AI coding agents. Both installation methods give you both modes. + +### Option A โ€” Homebrew ```bash -npm install -g xcodebuildmcp@latest +brew tap cameroncooke/xcodebuildmcp +brew install xcodebuildmcp +``` + +Use the CLI: +```bash xcodebuildmcp --help ``` -### MCP Server Installation +MCP client config: +```json +"XcodeBuildMCP": { + "command": "xcodebuildmcp", + "args": ["mcp"] +} +``` + +Upgrade later with `brew update && brew upgrade xcodebuildmcp`. -Add XcodeBuildMCP to your MCP client configuration. Most clients use JSON configuration with the following server entry: +### Option B โ€” npm / npx (Node.js 18+) +**For CLI use**, install globally: +```bash +npm install -g xcodebuildmcp@latest +xcodebuildmcp --help +``` + +**For MCP server only**, no global install needed โ€” add directly to your client config: ```json "XcodeBuildMCP": { "command": "npx", @@ -25,6 +47,12 @@ Add XcodeBuildMCP to your MCP client configuration. Most clients use JSON config } ``` +To pin a specific version, replace `@latest` with an exact version (e.g. `xcodebuildmcp@2.0.0`). + +### Client-specific setup + +The examples below use npx (Option B). If you installed via Homebrew, replace the command with `"command": "xcodebuildmcp", "args": ["mcp"]` instead. +
Cursor
@@ -188,7 +216,7 @@ Add XcodeBuildMCP to your MCP client configuration. Most clients use JSON config tool_timeout_sec = 10000 ``` - > **NOTE**: + > **NOTE**: > Codex Agent when running in Xcode has a limited PATH by default. The above example should work for most users but if you find the server doesn't start or is not available, it's likely because npx is not found so you might have to adjust the above configuration accordingly.
@@ -217,7 +245,7 @@ Add XcodeBuildMCP to your MCP client configuration. Most clients use JSON config } ``` - > **NOTE**: + > **NOTE**: > Claude Code Agent when running in Xcode has a limited PATH by default. The above example should work for most users but if you find the server doesn't start or is not available, it's likely because npx is not found so you might have to adjust the above configuration accordingly.
@@ -225,15 +253,13 @@ Add XcodeBuildMCP to your MCP client configuration. Most clients use JSON config
-For other installation options see [Getting Started](docs/GETTING_STARTED.md) - -When configuring a client manually, ensure the command includes the `mcp` subcommand (for example, `npx -y xcodebuildmcp@latest mcp`). +For other installation options see [Getting Started](docs/GETTING_STARTED.md). ## Requirements - macOS 14.5 or later - Xcode 16.x or later -- Node.js 18.x or later +- Node.js 18.x or later (not required for Homebrew installation) ## Skills diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index 62fd3c91..8695b7bd 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -3,7 +3,7 @@ ## Prerequisites - macOS 14.5 or later - Xcode 16.x or later -- Node.js 18.x or later +- Node.js 18.x or later (not required for Homebrew installation) ## Choose Your Interface @@ -16,40 +16,50 @@ XcodeBuildMCP provides a unified CLI with two modes: Both share the same tools and configuration. -## MCP Server Installation +## Installation -Most MCP clients use JSON configuration. Add the following server entry to your client's MCP config: +Both methods give you the CLI and the MCP server. +### Option A โ€” Homebrew (no Node.js required) + +```bash +brew tap cameroncooke/xcodebuildmcp +brew install xcodebuildmcp +``` + +Use the CLI: +```bash +xcodebuildmcp --help +``` + +MCP client config: ```json "XcodeBuildMCP": { - "command": "npx", - "args": [ - "-y", - "xcodebuildmcp@beta", - "mcp" - ] + "command": "xcodebuildmcp", + "args": ["mcp"] } ``` -## CLI Installation - -```bash -# Install globally -npm install -g xcodebuildmcp@beta - -# Verify installation -xcodebuildmcp --version +Upgrade later with `brew update && brew upgrade xcodebuildmcp`. -# List available tools -xcodebuildmcp tools +### Option B โ€” npm / npx (Node.js 18+) -# View CLI help +**For CLI use**, install globally: +```bash +npm install -g xcodebuildmcp@latest xcodebuildmcp --help +``` -# View tool help -xcodebuildmcp --help +**For MCP server only**, no global install needed โ€” add directly to your client config: +```json +"XcodeBuildMCP": { + "command": "npx", + "args": ["-y", "xcodebuildmcp@latest", "mcp"] +} ``` +Using `@latest` ensures clients resolve the newest version on each run. + See [CLI.md](CLI.md) for full CLI documentation. ## Project config (optional) @@ -63,6 +73,8 @@ See [CONFIGURATION.md](CONFIGURATION.md) for the full schema and examples. ## Client-specific configuration +The examples below use npx (Option B). If you installed via Homebrew, replace the command with `"command": "xcodebuildmcp", "args": ["mcp"]` instead. + ### Cursor Recommended (project-scoped): create `.cursor/mcp.json` in your project root: @@ -71,7 +83,7 @@ Recommended (project-scoped): create `.cursor/mcp.json` in your project root: "mcpServers": { "XcodeBuildMCP": { "command": "npx", - "args": ["-y", "xcodebuildmcp@beta", "mcp"] + "args": ["-y", "xcodebuildmcp@latest", "mcp"] } } } @@ -86,7 +98,7 @@ If you use a global Cursor config at `~/.cursor/mcp.json`, use this variant to a "command": "/bin/zsh", "args": [ "-lc", - "cd \"${workspaceFolder}\" && exec npx -y xcodebuildmcp@beta mcp" + "cd \"${workspaceFolder}\" && exec npx -y xcodebuildmcp@latest mcp" ] } } @@ -99,7 +111,7 @@ Codex uses TOML for MCP configuration. Add this to `~/.codex/config.toml`: ```toml [mcp_servers.XcodeBuildMCP] command = "npx" -args = ["-y", "xcodebuildmcp@beta", "mcp"] +args = ["-y", "xcodebuildmcp@latest", "mcp"] env = { "XCODEBUILDMCP_SENTRY_DISABLED" = "false" } ``` @@ -115,10 +127,10 @@ https://github.com/openai/codex/blob/main/docs/config.md#connecting-to-mcp-serve ### Claude Code CLI ```bash # Add XcodeBuildMCP server to Claude Code -claude mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@beta mcp +claude mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp # Or with environment variables -claude mcp add XcodeBuildMCP -e XCODEBUILDMCP_SENTRY_DISABLED=false -- npx -y xcodebuildmcp@beta mcp +claude mcp add XcodeBuildMCP -e XCODEBUILDMCP_SENTRY_DISABLED=false -- npx -y xcodebuildmcp@latest mcp ``` Note: XcodeBuildMCP requests xcodebuild to skip macro validation to avoid Swift Macro build errors. diff --git a/docs/dev/CONTRIBUTING.md b/docs/dev/CONTRIBUTING.md index 26040431..65bc3dce 100644 --- a/docs/dev/CONTRIBUTING.md +++ b/docs/dev/CONTRIBUTING.md @@ -51,6 +51,22 @@ brew tap cameroncooke/axe brew install axe ``` +#### Optional: Using a Local AXe Checkout for Bundling + +`npm run bundle:axe` defaults to downloading pinned AXe release artifacts from GitHub. + +To bundle from a local AXe source checkout instead: + +```bash +AXE_USE_LOCAL=1 AXE_LOCAL_DIR=/absolute/path/to/AXe npm run bundle:axe +``` + +Rules: +- Local mode is enabled only when `AXE_USE_LOCAL=1`. +- `AXE_LOCAL_DIR` must point to a valid AXe repository (must contain `Package.swift`). +- If `AXE_USE_LOCAL=1` and `AXE_LOCAL_DIR` is missing/invalid, bundling fails fast. +- `AXE_FORCE_REMOTE=1` overrides local mode and forces remote artifact download. + ### Installation 1. Clone the repository diff --git a/docs/dev/RELEASE_PROCESS.md b/docs/dev/RELEASE_PROCESS.md index 8abaf844..28da5866 100644 --- a/docs/dev/RELEASE_PROCESS.md +++ b/docs/dev/RELEASE_PROCESS.md @@ -14,9 +14,35 @@ If the latest changelog section is `## [Unreleased]` and no matching version hea Preview release notes locally: ```bash -node scripts/generate-github-release-notes.mjs --version 2.0.0-beta.1 +node scripts/generate-github-release-notes.mjs --version 2.0.0 ``` +## Release Workflow Modes + +The release workflow (`.github/workflows/release.yml`) has two execution modes: + +### Tag push (`push` on `v*`) + +Production release behavior: +- Publishes package to npm. +- Creates GitHub release and uploads npm tarball. +- Builds and verifies portable macOS artifacts (`arm64`, `x64`, `universal`). +- Uploads portable artifacts to GitHub release assets. +- Updates the Homebrew tap repository (`cameroncooke/homebrew-xcodebuildmcp`) directly when `HOMEBREW_TAP_TOKEN` is configured. +- Attempts Smithery and MCP Registry publishes (best effort based on configured secrets). + +### Manual dispatch (`workflow_dispatch`) + +Validation behavior only (no production deployment): +- Runs formatting/build/tests and packaging checks. +- Runs npm publish in `--dry-run` mode. +- Builds and verifies portable artifacts for release-pipeline validation. +- Does **not** publish to npm. +- Does **not** create GitHub release. +- Does **not** upload portable assets to a release. +- Does **not** update Homebrew tap. +- Does **not** run Smithery or MCP Registry publish jobs. + ## Step-by-Step Development Workflow ### 1. Starting New Work diff --git a/src/version.ts b/src/version.ts index 35404a97..255d996f 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,3 +1,3 @@ -export const version = '2.0.0-beta.1'; +export const version = '2.0.0'; export const iOSTemplateVersion = 'v1.0.8'; export const macOSTemplateVersion = 'v1.0.5'; From 2f7208d146a1431752762ee5635becd3960abee4 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 11:05:09 +0000 Subject: [PATCH 25/28] chore(distribution): Remove Smithery integration and references Remove Smithery from build scripts, release workflow, package metadata,\nand source entrypoints.\n\nDelete Smithery-specific docs and update remaining docs and tests to\nreflect the supported distribution paths without Smithery artifacts. Co-Authored-By: Claude --- .github/workflows/release.yml | 14 +- docs/TROUBLESHOOTING.md | 2 +- docs/dev/ARCHITECTURE.md | 4 - docs/dev/CLI_CONVERSION_PLAN.md | 2 +- docs/dev/PROJECT_CONFIG_PLAN.md | 8 +- docs/dev/README.md | 1 - docs/dev/RELEASE_PROCESS.md | 4 +- docs/dev/SMITHERY.md | 273 -- docs/dev/SMITHERY_PACKAGING_CONTEXT.md | 83 - docs/dev/oracle-prompt-workspace-daemon.md | 2608 ------------ docs/investigations/issue-163.md | 83 - package-lock.json | 3517 ++--------------- package.json | 7 +- scripts/verify-smithery-bundle.sh | 46 - smithery.yaml | 5 - .../ui-automation/__tests__/button.test.ts | 4 +- .../ui-automation/__tests__/gesture.test.ts | 4 +- .../ui-automation/__tests__/key_press.test.ts | 24 +- .../__tests__/key_sequence.test.ts | 24 +- .../__tests__/long_press.test.ts | 4 +- .../__tests__/snapshot_ui.test.ts | 4 +- .../ui-automation/__tests__/swipe.test.ts | 6 +- .../tools/ui-automation/__tests__/tap.test.ts | 16 +- .../ui-automation/__tests__/touch.test.ts | 34 +- .../ui-automation/__tests__/type_text.test.ts | 6 +- src/smithery.ts | 41 - src/utils/axe-helpers.ts | 2 +- src/utils/sentry.ts | 2 +- 28 files changed, 301 insertions(+), 6527 deletions(-) delete mode 100644 docs/dev/SMITHERY.md delete mode 100644 docs/dev/SMITHERY_PACKAGING_CONTEXT.md delete mode 100644 docs/dev/oracle-prompt-workspace-daemon.md delete mode 100644 docs/investigations/issue-163.md delete mode 100755 scripts/verify-smithery-bundle.sh delete mode 100644 smithery.yaml delete mode 100644 src/smithery.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0c54f26c..96a78a2b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: - name: Bundle AXe artifacts run: npm run bundle:axe - - name: Build Smithery bundle + - name: Build package run: npm run build - name: Run tests @@ -119,18 +119,6 @@ jobs: echo "๐Ÿ“ฆ Publishing to NPM with tag: $NPM_TAG" npm publish --access public --tag "$NPM_TAG" - - name: Deploy to Smithery (production releases only) - if: github.event_name == 'push' - continue-on-error: true - env: - SMITHERY_TOKEN: ${{ secrets.SMITHERY_TOKEN }} - run: | - if [ -z "$SMITHERY_TOKEN" ]; then - echo "Missing SMITHERY_TOKEN secret for Smithery deploy." - exit 1 - fi - npx smithery deploy --transport stdio - - name: Create GitHub Release (production releases only) if: github.event_name == 'push' uses: softprops/action-gh-release@v1 diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 0bb03626..f5d824e3 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -27,7 +27,7 @@ It reports on: ### UI automation reports missing AXe UI automation (describe/tap/swipe/type) and simulator video capture require the AXe binary. If you see a missing AXe error: -- Ensure `bundled/` artifacts exist when installing from Smithery or npm. +- Ensure `bundled/` artifacts exist in your installation. - Or set `XCODEBUILDMCP_AXE_PATH` to a known AXe binary path (preferred), or `AXE_PATH`. - Re-run the doctor tool to confirm AXe is detected. diff --git a/docs/dev/ARCHITECTURE.md b/docs/dev/ARCHITECTURE.md index 6a0d3f01..0219a5f3 100644 --- a/docs/dev/ARCHITECTURE.md +++ b/docs/dev/ARCHITECTURE.md @@ -486,10 +486,6 @@ describe('Tool Name', () => { โ””โ”€โ”€ *.js.map # Source maps ``` -### Smithery entrypoint - -Smithery deployments build from source using the Smithery CLI. The CLI discovers the TypeScript entrypoint from `package.json#module`, so this repository intentionally points `module` to `src/smithery.ts`. The npm runtime entrypoint is defined by `exports` and targets the compiled `build/` output. - ### npm Package - **Name**: `xcodebuildmcp` diff --git a/docs/dev/CLI_CONVERSION_PLAN.md b/docs/dev/CLI_CONVERSION_PLAN.md index a32a51ea..b1b029ff 100644 --- a/docs/dev/CLI_CONVERSION_PLAN.md +++ b/docs/dev/CLI_CONVERSION_PLAN.md @@ -887,7 +887,7 @@ xcodebuildcli build-sim --help ## Invariants 1. **MCP unchanged**: `xcodebuildmcp` continues to work exactly as before -2. **Smithery unchanged**: `src/smithery.ts` continues to work +2. **Build/runtime separation unchanged**: MCP and CLI continue to use shared tool handlers 3. **No code duplication**: CLI invokes same `PluginMeta.handler` functions 4. **Session defaults identical**: All runtimes use `bootstrapRuntime()` โ†’ `sessionStore` 5. **Tool logic shared**: `src/mcp/tools/*` remains single source of truth diff --git a/docs/dev/PROJECT_CONFIG_PLAN.md b/docs/dev/PROJECT_CONFIG_PLAN.md index 405ed806..7db8577e 100644 --- a/docs/dev/PROJECT_CONFIG_PLAN.md +++ b/docs/dev/PROJECT_CONFIG_PLAN.md @@ -62,7 +62,7 @@ Notes: ## Precedence (Operational) Runtime config precedence for all non-session defaults: -1. Programmatic overrides (e.g., Smithery config) +1. Programmatic overrides 2. Config file (`.xcodebuildmcp/config.yaml`) 3. Environment variables 4. Hardcoded defaults @@ -117,9 +117,9 @@ Responsibilities: - Persist session defaults through config store API. - Keep `deleteKeys` for mutual exclusivity. -### 7) Smithery overrides -**File:** `src/smithery.ts` -- Pass overrides into bootstrap/config store, so Smithery has highest precedence. +### 7) Runtime overrides +**File:** runtime entrypoints +- Pass overrides into bootstrap/config store, so explicit runtime overrides have highest precedence. ### 8) Documentation updates - Update `docs/CONFIGURATION.md`, `docs/GETTING_STARTED.md`, `docs/SESSION_DEFAULTS.md`. diff --git a/docs/dev/README.md b/docs/dev/README.md index 1f15d544..c704c142 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -9,7 +9,6 @@ ## Build and tooling - [ESLint and type safety](ESLINT_TYPE_SAFETY.md) - [Node.js and runtime notes](NODEJS_2025.md) -- [Smithery integration notes](SMITHERY.md) ## Release and maintenance - [Release process](RELEASE_PROCESS.md) diff --git a/docs/dev/RELEASE_PROCESS.md b/docs/dev/RELEASE_PROCESS.md index 28da5866..3ac65adc 100644 --- a/docs/dev/RELEASE_PROCESS.md +++ b/docs/dev/RELEASE_PROCESS.md @@ -29,7 +29,7 @@ Production release behavior: - Builds and verifies portable macOS artifacts (`arm64`, `x64`, `universal`). - Uploads portable artifacts to GitHub release assets. - Updates the Homebrew tap repository (`cameroncooke/homebrew-xcodebuildmcp`) directly when `HOMEBREW_TAP_TOKEN` is configured. -- Attempts Smithery and MCP Registry publishes (best effort based on configured secrets). +- Attempts MCP Registry publish (best effort based on configured secrets). ### Manual dispatch (`workflow_dispatch`) @@ -41,7 +41,7 @@ Validation behavior only (no production deployment): - Does **not** create GitHub release. - Does **not** upload portable assets to a release. - Does **not** update Homebrew tap. -- Does **not** run Smithery or MCP Registry publish jobs. +- Does **not** run MCP Registry publish job. ## Step-by-Step Development Workflow diff --git a/docs/dev/SMITHERY.md b/docs/dev/SMITHERY.md deleted file mode 100644 index abb64578..00000000 --- a/docs/dev/SMITHERY.md +++ /dev/null @@ -1,273 +0,0 @@ -# TypeScript Servers - -> Deploy and publish TypeScript MCP servers on Smithery using Smithery CLI - -## Overview - -Deploy TypeScript MCP servers using the official MCP SDK with two deployment options: - -* **Remote deployment**: Automatic containerization and infrastructure managed by Smithery -* **Local servers** (Beta): Distribute your server as [MCP bundle](https://github.com/anthropics/mcpb) allowing users to run it locally and one-click install it - -## Prerequisites - -* TypeScript MCP server using the official MCP SDK that exports the MCP server object at entry point -* Node.js 18+ and npm installed locally -* Smithery CLI installed as a dev dependency (`npm i -D @smithery/cli`) - - - **New to MCP servers?** See the [Getting Started guide](/getting_started) to learn how to build TypeScript MCP servers from scratch using the official SDK. - - -## Project Structure - -Your TypeScript project should look like this: - -``` -my-mcp-server/ - smithery.yaml # Smithery configuration - package.json # Node.js dependencies and scripts - tsconfig.json # TypeScript configuration - src/ - index.ts # Your MCP server code with exported createServer function -``` - -## Setup - -### 1. Configure smithery.yaml - -Create a `smithery.yaml` file in your repository root (usually where the `package.json` is): - -#### Remote Deployment (Default) - -```yaml theme={null} -runtime: "typescript" -``` - -#### Local Server (Beta) - -```yaml theme={null} -runtime: "typescript" -target: "local" -``` - - - **Local servers are in beta** - When you set `target: "local"`, your server runs locally on user's machine but is accessible through Smithery's registry for easy discovery and connection by MCP clients. - - -### 2. Configure package.json - -Your `package.json` must include the `module` field pointing to your server entry point: - -```json theme={null} -{ - "name": "my-mcp-server", - "version": "1.0.0", - "type": "module", - "module": "src/index.ts", // Points to your server entry point - "scripts": { - "build": "npx smithery build", - "dev": "npx smithery dev" - }, - "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.3", - "zod": "^3.25.46" - }, - "devDependencies": { - "@smithery/cli": "^1.4.6" - } -} -``` - - - Install the CLI locally with: - - ```bash theme={null} - npm i -D @smithery/cli - ``` - - The Smithery CLI externalizes your SDKs during bundling so your runtime uses the versions you install. If you see a warning about missing SDKs, add them to your dependencies (most servers need `@modelcontextprotocol/sdk` and `@smithery/sdk`). - - -### 3. Ensure Proper Server Structure - -Your TypeScript MCP server must export a default `createServer` function that returns the MCP server object. If you built your server following the [Getting Started guide](/getting_started), it should already have this structure. - -```typescript theme={null} -// src/index.ts -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; - -// Required: Export default createServer function -export default function createServer({ config }) { - // config contains user-provided settings (see configSchema below) - const server = new McpServer({ - name: "Your Server Name", - version: "1.0.0", - }); - - // Register your tools here... - - return server.server; // Must return the MCP server object -} -``` - -**Optional Configuration Schema**: If your server needs user configuration (API keys, settings, etc.), export a `configSchema`: - -```typescript theme={null} -// Optional: If your server doesn't need configuration, omit this -export const configSchema = z.object({ - apiKey: z.string().describe("Your API key"), - timeout: z.number().default(5000).describe("Request timeout in milliseconds"), -}); -``` - -**Where it goes**: Export `configSchema` from the same file as your `createServer` function (typically `src/index.ts`). - -**What it does**: Automatically generates [session configuration](/build/session-config) forms for users connecting to your server. - -## OAuth - - - OAuth is designed only for **remote servers**. OAuth is not available for local servers (`target: "local"`). - - -If your entry module exports `oauth`, Smithery CLI auto-mounts the required OAuth endpoints for you during remote deployment. - -### Export an OAuth provider - -```typescript theme={null} -// src/index.ts -import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js" -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" -import type { OAuthProvider } from "@smithery/sdk" -import { MyProvider } from "./provider.js" - -export default function createServer({ auth }: { auth: AuthInfo }) { - const server = new McpServer({ name: "My MCP", version: "1.0.0" }) - // register tools... - return server.server -} - -export const oauth: OAuthProvider = new MyProvider() // [!code highlight] -``` - -The CLI detects `oauth` and injects the auth routes automatically. For implementing `OAuthServerProvider`, see the [official MCP SDK authorization guide](https://modelcontextprotocol.io/docs/tutorials/security/authorization). - - - **You don't need to implement client registration.** Modern MCP clients use [Client ID Metadata Documents](https://modelcontextprotocol.io/specification/draft/basic/authorization#client-id-metadata-documents) (CIMD). Your server should advertise `client_id_metadata_document_supported: true` in its OAuth metadata โ€” see the [spec requirements](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#implementation-requirements). - - -## Local Development - -Test your server locally using the Smithery CLI: - -```bash theme={null} -# Start development server with interactive playground -npm run dev -``` - -This opens the **Smithery interactive playground** where you can: - -* Test your MCP server tools in real-time -* See tool responses and debug issues -* Validate your configuration schema -* Experiment with different inputs - -## Advanced Build Configuration - -For advanced use cases, you can customize the build process using a `smithery.config.js` file. This is useful for: - -* Marking packages as external (to avoid bundling issues) -* Configuring minification, targets, and other build options -* Adding custom esbuild plugins - -### Configuration File - -Create `smithery.config.js` in your project root: - -```javascript theme={null} -export default { - esbuild: { - // Mark problematic packages as external - external: ["playwright-core", "puppeteer-core"], - - // Enable minification for production - minify: true, - - // Set Node.js target version - target: "node18", - }, -}; -``` - -### Common Use Cases - -**External Dependencies**: If you encounter bundling issues with packages like Playwright or native modules: - -```javascript theme={null} -export default { - esbuild: { - external: ["playwright-core", "sharp", "@grpc/grpc-js"], - }, -}; -``` - -Configuration applies to both `build` and `dev` commands. - -## Deploy - -1. Push your code (including `smithery.yaml`) to GitHub -2. [Connect your GitHub](https://smithery.ai/new) to Smithery (or claim your server if already listed) -3. Navigate to the Deployments tab on your server page -4. Click Deploy to build and host your server - -## Good to Know - - - **Remote Deployment**: When you deploy to Smithery's infrastructure: - - 1. Clone your repository - 2. Parse your `smithery.yaml` to detect TypeScript runtime - 3. Install dependencies with `npm ci` - 4. Build your TypeScript code using the `module` entry point from your `package.json` - 5. Package your server into a containerized HTTP service - 6. Deploy the container to our hosting infrastructure - 7. Send MCP `initialize` and `list_tools` messages with a dummy configuration to discover your server's capabilities - 8. Make it available at `https://server.smithery.ai/your-server` - 9. Handle load balancing, scaling, and monitoring - - **Local Server (Beta)**: When you use `target: "local"`: - - 1. Your server runs locally on user's machine using `npm run dev` - 2. Smithery registers your server in the registry for discovery - 3. MCP clients can find and connect to your local server through Smithery - 4. Your server remains under your control while being accessible to others - - -## Troubleshooting - - - Common issues and solutions: - - **Remote Deployment Issues**: - - * **Missing module field**: Ensure your `package.json` has the `module` field pointing to your entry point - * **Dependencies not found**: All dependencies must be listed in `dependencies` or `devDependencies` - * **Server doesn't build locally**: Before deploying, verify your server builds and runs locally: - ```bash theme={null} - npm install - npm run build - ``` - If this fails, fix any TypeScript compilation errors or missing dependencies first - - **Local Server Issues** (Beta): - - * **Server not discoverable**: Ensure you have `target: "local"` in your `smithery.yaml` - * **Local server won't start**: Verify your server runs with `npm run dev` before expecting registry integration - * **Connection issues**: Make sure your local development environment allows the necessary network connections - - - ---- - -> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://smithery.ai/docs/llms.txt \ No newline at end of file diff --git a/docs/dev/SMITHERY_PACKAGING_CONTEXT.md b/docs/dev/SMITHERY_PACKAGING_CONTEXT.md deleted file mode 100644 index 1a020484..00000000 --- a/docs/dev/SMITHERY_PACKAGING_CONTEXT.md +++ /dev/null @@ -1,83 +0,0 @@ -# Smithery stdio packaging: problem domain and handoff - -## Audience -This document is for a new AI agent with no context about the prior investigation. - -## Original problem -XcodeBuildMCP is a **local stdio** MCP server deployed via the Smithery CLI. The server depends on bundled non-code assets (e.g., `bundled/` with an `axe` binary and frameworks). When deploying with the Smithery CLI, the resulting `.mcpb` bundle **does not include** these bundled assets, so the installed server lacks required resources at runtime. - -## Key facts about the current Smithery CLI (v3.x) -The Smithery CLI now builds and packs local stdio servers by: -1) Building the stdio bundle into an output directory (default `.smithery/stdio`). -2) Packing only that output directory into `server.mcpb` using `@anthropic-ai/mcpb`. -3) Uploading the `server.mcpb` in `smithery deploy --transport stdio`. - -Important constraints: -- The CLI **does not read `smithery.config.js`** or run any custom build hooks for asset staging. -- The CLI only reads `smithery.yaml` for metadata (e.g., name/target), not for a prepack step. -- The pack step includes only what is inside `.smithery/stdio`. - -## What we did -1) Confirmed the repository uses `@smithery/cli` v3.x and targets a local stdio server. -2) Verified that `smithery build --transport stdio` produces `.smithery/stdio/server.mcpb` **without** `bundled/`. -3) Confirmed that the previous `smithery.config.js` copy approach is obsolete because the CLI no longer loads that config. -4) Opened a Smithery CLI issue to request an official asset-staging hook: - - https://github.com/smithery-ai/cli/issues/524 - -## How to reproduce the missing-assets behavior -From the XcodeBuildMCP repo: -1) `npx smithery build --transport stdio -o .smithery/stdio` -2) `unzip -l .smithery/stdio/server.mcpb` - - The bundle contains the compiled entrypoint and manifests only. - - `bundled/` is missing. - -## Workaround (local validation only) -This is **not** compatible with `smithery deploy`, but it proves the bundle can contain assets: -1) Build the stdio bundle: - - `npx smithery build --transport stdio -o .smithery/stdio` -2) Copy assets into the stdio output directory: - - `cp -R bundled .smithery/stdio/bundled` -3) Temporarily replace `manifest.json` with `mcpb-manifest.json` for packing: - - `cp .smithery/stdio/manifest.json .smithery/stdio/manifest.payload.json` - - `cp .smithery/stdio/mcpb-manifest.json .smithery/stdio/manifest.json` -4) Re-pack using the official `mcpb` CLI: - - `npx @anthropic-ai/mcpb pack .smithery/stdio .smithery/stdio/server.mcpb` -5) Restore the original manifest: - - `mv .smithery/stdio/manifest.payload.json .smithery/stdio/manifest.json` - -Result: `server.mcpb` now contains `bundled/` and its frameworks. - -## Why the workaround cannot be used for deployment -`smithery deploy --transport stdio` rebuilds and repacks in its own flow, and provides no prepack hook to stage assets. As a result, there is no official way to inject `bundled/` into the `.mcpb` during deploy. - -## How we likely want to proceed -### Option A: Upstream fix (recommended) -Add an official asset staging hook or prepack command in the Smithery CLI: -- Example: `smithery.yaml` fields like `build.assets` or `build.prepackCommand`. -- The hook should run before `packExtension()` in `src/lib/bundle/stdio.ts`. -- This would allow asset copying into `.smithery/stdio` during `smithery deploy`. - -Issue to track: -- https://github.com/smithery-ai/cli/issues/524 - -### Option B: Fork the CLI -If the upstream fix is slow, fork `smithery-ai/cli` and add: -- A prepack hook (env var or `smithery.yaml` field). -- A deterministic asset staging step inside `buildStdioBundle` before the pack step. -Then consume the fork in CI: -- Publish the fork under a scoped npm package, or -- Install the fork from GitHub release in the release workflow. - -Current implementation (forked CLI): -- Added `build.prepackCommand` support in `smithery.yaml` (plus env var overrides). -- CI/release workflows install the forked CLI before `smithery build`. -- This repo wires `smithery.yaml` to `scripts/smithery-prepack.sh` to bundle/copy assets. - -## Current repo state that matters -- `smithery.config.js` exists but is **ignored** by v3.x CLI. - - Any asset copy logic in this file does not run in deploy. -- Release workflow currently publishes npm and MCP registry, but **does not** run Smithery deploy. -- `npm run build` uses Smitheryโ€™s default transport (shttp) unless explicit `--transport stdio` is used. - -## Summary -The problem is not an outdated CLI version. The CLI is current, but the integration strategy is obsolete because v3.x no longer honors `smithery.config.js`. The deploy flow packs only `.smithery/stdio`. Until Smithery adds an official prepack/assets hook (or a fork is used), correct bundling for local stdio deployments is not achievable via `smithery deploy`. diff --git a/docs/dev/oracle-prompt-workspace-daemon.md b/docs/dev/oracle-prompt-workspace-daemon.md deleted file mode 100644 index 9341d0d1..00000000 --- a/docs/dev/oracle-prompt-workspace-daemon.md +++ /dev/null @@ -1,2608 +0,0 @@ - -/Volumes/Developer/XcodeBuildMCP -โ”œโ”€โ”€ docs -โ”‚ โ”œโ”€โ”€ dev -โ”‚ โ”‚ โ”œโ”€โ”€ CLI_CONVERSION_PLAN.md * -โ”‚ โ”‚ โ”œโ”€โ”€ ARCHITECTURE.md -โ”‚ โ”‚ โ”œโ”€โ”€ CODE_QUALITY.md -โ”‚ โ”‚ โ”œโ”€โ”€ CONTRIBUTING.md -โ”‚ โ”‚ โ”œโ”€โ”€ ESLINT_TYPE_SAFETY.md -โ”‚ โ”‚ โ”œโ”€โ”€ MANUAL_TESTING.md -โ”‚ โ”‚ โ”œโ”€โ”€ NODEJS_2025.md -โ”‚ โ”‚ โ”œโ”€โ”€ PLUGIN_DEVELOPMENT.md -โ”‚ โ”‚ โ”œโ”€โ”€ PROJECT_CONFIG_PLAN.md -โ”‚ โ”‚ โ”œโ”€โ”€ README.md -โ”‚ โ”‚ โ”œโ”€โ”€ RELEASE_PROCESS.md -โ”‚ โ”‚ โ”œโ”€โ”€ RELOADEROO.md -โ”‚ โ”‚ โ”œโ”€โ”€ RELOADEROO_FOR_XCODEBUILDMCP.md -โ”‚ โ”‚ โ”œโ”€โ”€ RELOADEROO_XCODEBUILDMCP_PRIMER.md -โ”‚ โ”‚ โ”œโ”€โ”€ SMITHERY.md -โ”‚ โ”‚ โ”œโ”€โ”€ SMITHERY_PACKAGING_CONTEXT.md -โ”‚ โ”‚ โ”œโ”€โ”€ TESTING.md -โ”‚ โ”‚ โ”œโ”€โ”€ TEST_RUNNER_ENV_IMPLEMENTATION_PLAN.md -โ”‚ โ”‚ โ”œโ”€โ”€ ZOD_MIGRATION_GUIDE.md -โ”‚ โ”‚ โ”œโ”€โ”€ session-aware-migration-todo.md -โ”‚ โ”‚ โ”œโ”€โ”€ session_management_plan.md -โ”‚ โ”‚ โ”œโ”€โ”€ tools_cli_schema_audit_plan.md -โ”‚ โ”‚ โ””โ”€โ”€ tools_schema_redundancy.md -โ”‚ โ”œโ”€โ”€ investigations -โ”‚ โ”‚ โ”œโ”€โ”€ issue-154-screenshot-downscaling.md -โ”‚ โ”‚ โ”œโ”€โ”€ issue-163.md -โ”‚ โ”‚ โ”œโ”€โ”€ issue-debugger-attach-stopped.md -โ”‚ โ”‚ โ””โ”€โ”€ issue-describe-ui-empty-after-debugger-resume.md -โ”‚ โ”œโ”€โ”€ CLI.md -โ”‚ โ”œโ”€โ”€ CONFIGURATION.md -โ”‚ โ”œโ”€โ”€ DAP_BACKEND_IMPLEMENTATION_PLAN.md -โ”‚ โ”œโ”€โ”€ DEBUGGING_ARCHITECTURE.md -โ”‚ โ”œโ”€โ”€ DEMOS.md -โ”‚ โ”œโ”€โ”€ DEVICE_CODE_SIGNING.md -โ”‚ โ”œโ”€โ”€ GETTING_STARTED.md -โ”‚ โ”œโ”€โ”€ OVERVIEW.md -โ”‚ โ”œโ”€โ”€ PRIVACY.md -โ”‚ โ”œโ”€โ”€ README.md -โ”‚ โ”œโ”€โ”€ SESSION_DEFAULTS.md -โ”‚ โ”œโ”€โ”€ SKILLS.md -โ”‚ โ”œโ”€โ”€ TOOLS.md -โ”‚ โ””โ”€โ”€ TROUBLESHOOTING.md -โ”œโ”€โ”€ src -โ”‚ โ”œโ”€โ”€ cli -โ”‚ โ”‚ โ”œโ”€โ”€ commands -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ daemon.ts * + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ tools.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ daemon-client.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ register-tool-commands.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ yargs-app.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ output.ts + -โ”‚ โ”‚ โ””โ”€โ”€ schema-to-yargs.ts + -โ”‚ โ”œโ”€โ”€ daemon -โ”‚ โ”‚ โ”œโ”€โ”€ daemon-server.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ socket-path.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ framing.ts + -โ”‚ โ”‚ โ””โ”€โ”€ protocol.ts + -โ”‚ โ”œโ”€โ”€ runtime -โ”‚ โ”‚ โ”œโ”€โ”€ naming.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ tool-catalog.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ tool-invoker.ts * + -โ”‚ โ”‚ โ”œโ”€โ”€ types.ts * + -โ”‚ โ”‚ โ””โ”€โ”€ bootstrap-runtime.ts + -โ”‚ โ”œโ”€โ”€ core -โ”‚ โ”‚ โ”œโ”€โ”€ __tests__ -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ resources.test.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ generated-plugins.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ generated-resources.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ plugin-registry.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ plugin-types.ts + -โ”‚ โ”‚ โ””โ”€โ”€ resources.ts + -โ”‚ โ”œโ”€โ”€ mcp -โ”‚ โ”‚ โ”œโ”€โ”€ resources -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ __tests__ -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ devices.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ doctor.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ session-status.ts + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ simulators.ts + -โ”‚ โ”‚ โ””โ”€โ”€ tools -โ”‚ โ”‚ โ”œโ”€โ”€ debugging -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ device -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ doctor -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ logging -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ macos -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ project-discovery -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ project-scaffolding -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ session-management -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ simulator -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ simulator-management -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ swift-package -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ ui-automation -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ utilities -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ””โ”€โ”€ workflow-discovery -โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”œโ”€โ”€ server -โ”‚ โ”‚ โ”œโ”€โ”€ bootstrap.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ server-state.ts + -โ”‚ โ”‚ โ””โ”€โ”€ server.ts + -โ”‚ โ”œโ”€โ”€ test-utils -โ”‚ โ”‚ โ””โ”€โ”€ mock-executors.ts + -โ”‚ โ”œโ”€โ”€ types -โ”‚ โ”‚ โ””โ”€โ”€ common.ts + -โ”‚ โ”œโ”€โ”€ utils -โ”‚ โ”‚ โ”œโ”€โ”€ __tests__ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ build-utils-suppress-warnings.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ build-utils.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ config-store.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ debugger-simctl.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ environment.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ log_capture.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ project-config.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ session-aware-tool-factory.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ session-store.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ simulator-utils.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ test-runner-env-integration.test.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ typed-tool-factory.test.ts + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ workflow-selection.test.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ axe -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ build -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ debugger -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ __tests__ -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ backends -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ dap -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ debugger-manager.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ index.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ simctl.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ tool-context.ts + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ types.ts + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ui-automation-guard.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ execution -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ index.ts + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ interactive-process.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ log-capture -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ device-log-sessions.ts + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ logging -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ plugin-registry -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ responses -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ template -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ test -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ validation -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ version -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ video-capture -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ xcodemake -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ index.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ CommandExecutor.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ FileSystemExecutor.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ axe-helpers.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ build-utils.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ capabilities.ts -โ”‚ โ”‚ โ”œโ”€โ”€ command.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ config-store.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ environment.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ errors.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ log_capture.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ logger.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ project-config.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ remove-undefined.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ runtime-config-schema.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ runtime-config-types.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ schema-helpers.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ sentry.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ session-defaults-schema.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ session-status.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ session-store.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ simulator-utils.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ template-manager.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ test-common.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ tool-registry.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ typed-tool-factory.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ validation.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ video_capture.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ workflow-selection.ts + -โ”‚ โ”‚ โ”œโ”€โ”€ xcode.ts + -โ”‚ โ”‚ โ””โ”€โ”€ xcodemake.ts + -โ”‚ โ”œโ”€โ”€ cli.ts * + -โ”‚ โ”œโ”€โ”€ daemon.ts * + -โ”‚ โ”œโ”€โ”€ doctor-cli.ts + -โ”‚ โ”œโ”€โ”€ index.ts + -โ”‚ โ”œโ”€โ”€ smithery.ts + -โ”‚ โ””โ”€โ”€ version.ts + -โ”œโ”€โ”€ .claude -โ”‚ โ”œโ”€โ”€ agents -โ”‚ โ”‚ โ””โ”€โ”€ xcodebuild-mcp-qa-tester.md -โ”‚ โ””โ”€โ”€ commands -โ”‚ โ”œโ”€โ”€ rp-build-cli.md -โ”‚ โ”œโ”€โ”€ rp-investigate-cli.md -โ”‚ โ”œโ”€โ”€ rp-oracle-export-cli.md -โ”‚ โ”œโ”€โ”€ rp-refactor-cli.md -โ”‚ โ”œโ”€โ”€ rp-reminder-cli.md -โ”‚ โ””โ”€โ”€ rp-review-cli.md -โ”œโ”€โ”€ .cursor -โ”‚ โ”œโ”€โ”€ BUGBOT.md -โ”‚ โ””โ”€โ”€ environment.json -โ”œโ”€โ”€ .github -โ”‚ โ”œโ”€โ”€ ISSUE_TEMPLATE -โ”‚ โ”‚ โ”œโ”€โ”€ bug_report.yml -โ”‚ โ”‚ โ”œโ”€โ”€ config.yml -โ”‚ โ”‚ โ””โ”€โ”€ feature_request.yml -โ”‚ โ”œโ”€โ”€ workflows -โ”‚ โ”‚ โ”œโ”€โ”€ README.md -โ”‚ โ”‚ โ”œโ”€โ”€ ci.yml -โ”‚ โ”‚ โ”œโ”€โ”€ release.yml -โ”‚ โ”‚ โ”œโ”€โ”€ sentry.yml -โ”‚ โ”‚ โ””โ”€โ”€ stale.yml -โ”‚ โ””โ”€โ”€ FUNDING.yml -โ”œโ”€โ”€ build-plugins -โ”‚ โ”œโ”€โ”€ plugin-discovery.js + -โ”‚ โ”œโ”€โ”€ plugin-discovery.ts + -โ”‚ โ””โ”€โ”€ tsconfig.json -โ”œโ”€โ”€ example_projects -โ”‚ โ”œโ”€โ”€ iOS -โ”‚ โ”‚ โ”œโ”€โ”€ .cursor -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ rules -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ .xcodebuildmcp -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ config.yaml -โ”‚ โ”‚ โ”œโ”€โ”€ MCPTest -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Assets.xcassets -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Preview Content -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ ContentView.swift + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ MCPTestApp.swift + -โ”‚ โ”‚ โ”œโ”€โ”€ MCPTest.xcodeproj -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ xcshareddata -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ project.pbxproj -โ”‚ โ”‚ โ””โ”€โ”€ MCPTestUITests -โ”‚ โ”‚ โ””โ”€โ”€ MCPTestUITests.swift + -โ”‚ โ”œโ”€โ”€ iOS_Calculator -โ”‚ โ”‚ โ”œโ”€โ”€ .xcodebuildmcp -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ config.yaml -โ”‚ โ”‚ โ”œโ”€โ”€ CalculatorApp -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Assets.xcassets -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ CalculatorApp.swift + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ CalculatorApp.xctestplan -โ”‚ โ”‚ โ”œโ”€โ”€ CalculatorApp.xcodeproj -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ xcshareddata -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ project.pbxproj -โ”‚ โ”‚ โ”œโ”€โ”€ CalculatorApp.xcworkspace -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ contents.xcworkspacedata -โ”‚ โ”‚ โ”œโ”€โ”€ CalculatorAppPackage -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Sources -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Tests -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ .gitignore -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ Package.swift + -โ”‚ โ”‚ โ”œโ”€โ”€ CalculatorAppTests -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ CalculatorAppTests.swift + -โ”‚ โ”‚ โ”œโ”€โ”€ Config -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Debug.xcconfig -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Release.xcconfig -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Shared.xcconfig -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ Tests.xcconfig -โ”‚ โ”‚ โ””โ”€โ”€ .gitignore -โ”‚ โ”œโ”€โ”€ macOS -โ”‚ โ”‚ โ”œโ”€โ”€ MCPTest -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Assets.xcassets -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Preview Content -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ ContentView.swift + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ MCPTest.entitlements -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ MCPTestApp.swift + -โ”‚ โ”‚ โ”œโ”€โ”€ MCPTest.xcodeproj -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ xcshareddata -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ project.pbxproj -โ”‚ โ”‚ โ””โ”€โ”€ MCPTestTests -โ”‚ โ”‚ โ””โ”€โ”€ MCPTestTests.swift + -โ”‚ โ””โ”€โ”€ spm -โ”‚ โ”œโ”€โ”€ Sources -โ”‚ โ”‚ โ”œโ”€โ”€ TestLib -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ long-server -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ”œโ”€โ”€ quick-task -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”‚ โ””โ”€โ”€ spm -โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”œโ”€โ”€ Tests -โ”‚ โ”‚ โ””โ”€โ”€ TestLibTests -โ”‚ โ”‚ โ””โ”€โ”€ ... -โ”‚ โ”œโ”€โ”€ .gitignore -โ”‚ โ”œโ”€โ”€ Package.resolved -โ”‚ โ””โ”€โ”€ Package.swift + -โ”œโ”€โ”€ scripts -โ”‚ โ”œโ”€โ”€ analysis -โ”‚ โ”‚ โ”œโ”€โ”€ tools-analysis.ts + -โ”‚ โ”‚ โ””โ”€โ”€ tools-schema-audit.ts + -โ”‚ โ”œโ”€โ”€ bundle-axe.sh -โ”‚ โ”œโ”€โ”€ check-code-patterns.js + -โ”‚ โ”œโ”€โ”€ generate-loaders.ts + -โ”‚ โ”œโ”€โ”€ generate-version.ts + -โ”‚ โ”œโ”€โ”€ install-skill.sh -โ”‚ โ”œโ”€โ”€ release.sh -โ”‚ โ”œโ”€โ”€ tools-cli.ts + -โ”‚ โ”œโ”€โ”€ update-tools-docs.ts + -โ”‚ โ””โ”€โ”€ verify-smithery-bundle.sh -โ”œโ”€โ”€ skills -โ”‚ โ””โ”€โ”€ xcodebuildmcp -โ”‚ โ””โ”€โ”€ SKILL.md -โ”œโ”€โ”€ .axe-version -โ”œโ”€โ”€ .gitignore -โ”œโ”€โ”€ .prettierignore -โ”œโ”€โ”€ .prettierrc.js -โ”œโ”€โ”€ .repomix-output.txt -โ”œโ”€โ”€ AGENTS.md -โ”œโ”€โ”€ CHANGELOG.md -โ”œโ”€โ”€ CLAUDE.md -โ”œโ”€โ”€ CODE_OF_CONDUCT.md -โ”œโ”€โ”€ LICENSE -โ”œโ”€โ”€ README.md -โ”œโ”€โ”€ XcodeBuildMCP.code-workspace -โ”œโ”€โ”€ banner.png -โ”œโ”€โ”€ config.example.yaml -โ”œโ”€โ”€ eslint.config.js + -โ”œโ”€โ”€ mcp-install-dark.png -โ”œโ”€โ”€ package-lock.json -โ”œโ”€โ”€ package.json -โ”œโ”€โ”€ server.json -โ”œโ”€โ”€ smithery.yaml -โ”œโ”€โ”€ tsconfig.json -โ”œโ”€โ”€ tsconfig.test.json -โ”œโ”€โ”€ tsconfig.tests.json -โ”œโ”€โ”€ tsup.config.ts + -โ””โ”€โ”€ vitest.config.ts + - -/Users/cameroncooke/.codex/skills -โ”œโ”€โ”€ .claude -โ”‚ โ””โ”€โ”€ commands -โ”‚ โ”œโ”€โ”€ rp-build-cli.md -โ”‚ โ”œโ”€โ”€ rp-investigate-cli.md -โ”‚ โ”œโ”€โ”€ rp-oracle-export-cli.md -โ”‚ โ”œโ”€โ”€ rp-refactor-cli.md -โ”‚ โ”œโ”€โ”€ rp-reminder-cli.md -โ”‚ โ””โ”€โ”€ rp-review-cli.md -โ”œโ”€โ”€ .system -โ”‚ โ”œโ”€โ”€ skill-creator -โ”‚ โ”‚ โ”œโ”€โ”€ scripts -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ init_skill.py + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ package_skill.py + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ quick_validate.py + -โ”‚ โ”‚ โ”œโ”€โ”€ SKILL.md -โ”‚ โ”‚ โ””โ”€โ”€ license.txt -โ”‚ โ”œโ”€โ”€ skill-installer -โ”‚ โ”‚ โ”œโ”€โ”€ scripts -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ github_utils.py + -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ install-skill-from-github.py + -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ list-curated-skills.py + -โ”‚ โ”‚ โ”œโ”€โ”€ LICENSE.txt -โ”‚ โ”‚ โ””โ”€โ”€ SKILL.md -โ”‚ โ””โ”€โ”€ .codex-system-skills.marker -โ””โ”€โ”€ public - โ”œโ”€โ”€ agent-browser - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ agents-md - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ app-store-changelog - โ”‚ โ”œโ”€โ”€ references - โ”‚ โ”‚ โ””โ”€โ”€ release-notes-guidelines.md - โ”‚ โ”œโ”€โ”€ scripts - โ”‚ โ”‚ โ””โ”€โ”€ collect_release_changes.sh - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ brand-guidelines - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ claude-settings-audit - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ code-review - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ code-simplifier - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ commit - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ create-pr - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ doc-coauthoring - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ find-bugs - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ gh-issue-fix-flow - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ ios-debugger-agent - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ iterate-pr - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ macos-spm-app-packaging - โ”‚ โ”œโ”€โ”€ assets - โ”‚ โ”‚ โ””โ”€โ”€ templates - โ”‚ โ”‚ โ””โ”€โ”€ ... - โ”‚ โ”œโ”€โ”€ references - โ”‚ โ”‚ โ”œโ”€โ”€ packaging.md - โ”‚ โ”‚ โ”œโ”€โ”€ release.md - โ”‚ โ”‚ โ””โ”€โ”€ scaffold.md - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ swift-concurrency-expert - โ”‚ โ”œโ”€โ”€ references - โ”‚ โ”‚ โ”œโ”€โ”€ approachable-concurrency.md - โ”‚ โ”‚ โ”œโ”€โ”€ swift-6-2-concurrency.md - โ”‚ โ”‚ โ””โ”€โ”€ swiftui-concurrency-tour-wwdc.md - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ swiftui-liquid-glass - โ”‚ โ”œโ”€โ”€ references - โ”‚ โ”‚ โ””โ”€โ”€ liquid-glass.md - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ swiftui-performance-audit - โ”‚ โ”œโ”€โ”€ references - โ”‚ โ”‚ โ”œโ”€โ”€ demystify-swiftui-performance-wwdc23.md - โ”‚ โ”‚ โ”œโ”€โ”€ optimizing-swiftui-performance-instruments.md - โ”‚ โ”‚ โ”œโ”€โ”€ understanding-hangs-in-your-app.md - โ”‚ โ”‚ โ””โ”€โ”€ understanding-improving-swiftui-performance.md - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ swiftui-ui-patterns - โ”‚ โ”œโ”€โ”€ references - โ”‚ โ”‚ โ”œโ”€โ”€ app-wiring.md - โ”‚ โ”‚ โ”œโ”€โ”€ components-index.md - โ”‚ โ”‚ โ”œโ”€โ”€ controls.md - โ”‚ โ”‚ โ”œโ”€โ”€ deeplinks.md - โ”‚ โ”‚ โ”œโ”€โ”€ focus.md - โ”‚ โ”‚ โ”œโ”€โ”€ form.md - โ”‚ โ”‚ โ”œโ”€โ”€ grids.md - โ”‚ โ”‚ โ”œโ”€โ”€ haptics.md - โ”‚ โ”‚ โ”œโ”€โ”€ input-toolbar.md - โ”‚ โ”‚ โ”œโ”€โ”€ lightweight-clients.md - โ”‚ โ”‚ โ”œโ”€โ”€ list.md - โ”‚ โ”‚ โ”œโ”€โ”€ loading-placeholders.md - โ”‚ โ”‚ โ”œโ”€โ”€ macos-settings.md - โ”‚ โ”‚ โ”œโ”€โ”€ matched-transitions.md - โ”‚ โ”‚ โ”œโ”€โ”€ media.md - โ”‚ โ”‚ โ”œโ”€โ”€ menu-bar.md - โ”‚ โ”‚ โ”œโ”€โ”€ navigationstack.md - โ”‚ โ”‚ โ”œโ”€โ”€ overlay.md - โ”‚ โ”‚ โ”œโ”€โ”€ scrollview.md - โ”‚ โ”‚ โ”œโ”€โ”€ searchable.md - โ”‚ โ”‚ โ”œโ”€โ”€ sheets.md - โ”‚ โ”‚ โ”œโ”€โ”€ split-views.md - โ”‚ โ”‚ โ”œโ”€โ”€ tabview.md - โ”‚ โ”‚ โ”œโ”€โ”€ theming.md - โ”‚ โ”‚ โ”œโ”€โ”€ title-menus.md - โ”‚ โ”‚ โ””โ”€โ”€ top-bar.md - โ”‚ โ””โ”€โ”€ SKILL.md - โ”œโ”€โ”€ swiftui-view-refactor - โ”‚ โ”œโ”€โ”€ references - โ”‚ โ”‚ โ””โ”€โ”€ mv-patterns.md - โ”‚ โ””โ”€โ”€ SKILL.md - โ””โ”€โ”€ xcodebuildmcp - โ””โ”€โ”€ SKILL.md - -/Users/cameroncooke/Developer/AGENT -โ”œโ”€โ”€ ideas -โ”‚ โ”œโ”€โ”€ XcodeBuildMCP-installer.md -โ”‚ โ””โ”€โ”€ XcodeBuildMCP.md -โ””โ”€โ”€ improvements - โ”œโ”€โ”€ MCPLI.md - โ”œโ”€โ”€ Peekaboo.md - โ”œโ”€โ”€ Poltergeist.md - โ”œโ”€โ”€ XcodeBuildMCP.md - โ”œโ”€โ”€ macos-spm-app-packaging.md - โ”œโ”€โ”€ update_skills.md - โ”œโ”€โ”€ update_skills.sh.md - โ””โ”€โ”€ xcodebuildmcp-debugger-attach-stop.md - - -(* denotes selected files) -(+ denotes code-map available) -Config: depth cap 3. - - -File: /Volumes/Developer/XcodeBuildMCP/docs/dev/CLI_CONVERSION_PLAN.md -```md -# XcodeBuildMCP CLI Conversion Plan - -This document outlines the architectural plan to convert XcodeBuildMCP into a first-class CLI tool (`xcodebuildcli`) while maintaining full MCP server compatibility. - -## Overview - -### Goals - -1. **First-class CLI**: Separate CLI binary (`xcodebuildcli`) that invokes tools and exits -2. **MCP server unchanged**: `xcodebuildmcp` remains the long-lived stdio MCP server -3. **Shared tool logic**: All three runtimes (MCP, CLI, daemon) invoke the same underlying tool handlers -4. **Session defaults parity**: Identical behavior in all modes -5. **Stateful operation support**: Full daemon architecture for log capture, video recording, debugging, SwiftPM background - -### Non-Goals - -- Breaking existing MCP client integrations -- Changing the MCP protocol or tool schemas -- Wrapping MCP inside CLI (architecturally wrong) - ---- - -## Design Decisions - -| Decision | Choice | Rationale | -|----------|--------|-----------| -| CLI Framework | yargs | Better dynamic command generation, strict validation, array support | -| Stateful Support | Full daemon | Unix domain socket for complete multi-step stateful operations | -| Daemon Communication | Unix domain socket | macOS only, simple protocol, reliable | -| Stateful Tools Priority | All equally | Logging, video, debugging, SwiftPM all route to daemon | -| Tool Name Format | kebab-case | CLI-friendly, disambiguated when collisions exist | -| CLI Binary Name | `xcodebuildcli` | Distinct from MCP server binary | - ---- - -## Target Runtime Model - -### Entry Points - -| Binary | Entry Point | Description | -|--------|-------------|-------------| -| `xcodebuildmcp` | `src/index.ts` | MCP server (stdio, long-lived) - unchanged | -| `xcodebuildcli` | `src/cli.ts` | CLI (short-lived, exits after action) | -| Internal | `src/daemon.ts` | Daemon (Unix socket server, long-lived) | - -### Execution Modes - -- **Stateless tools**: CLI runs tools **in-process** by default (fast path) -- **Stateful tools** (log capture, video, debugging, SwiftPM background): CLI routes to **daemon** over Unix domain socket - -### Naming Rules - -- CLI tool names are **kebab-case** -- Internal MCP tool names remain **unchanged** (e.g., `build_sim`, `start_sim_log_cap`) -- CLI tool names are **derived** from MCP tool names, **disambiguated** when duplicates exist - -**Disambiguation rule:** -- If a tool's kebab-name is unique across enabled workflows: use it (e.g., `build-sim`) -- If duplicated across workflows (e.g., `clean` exists in multiple): CLI name becomes `-` (e.g., `simulator-clean`, `device-clean`) - ---- - -## Directory Structure - -### New Files - -``` -src/ - cli.ts # xcodebuildcli entry point (yargs) - daemon.ts # daemon entry point (unix socket server) - runtime/ - bootstrap-runtime.ts # shared runtime bootstrap (config + session defaults) - naming.ts # kebab-case + disambiguation + arg key transforms - tool-catalog.ts # loads workflows/tools, builds ToolCatalog with cliName mapping - tool-invoker.ts # shared "invoke tool by cliName" implementation - types.ts # shared core interfaces (ToolDefinition, ToolCatalog, Invoker) - daemon/ - protocol.ts # daemon protocol types (request/response, errors) - framing.ts # length-prefixed framing helpers for net.Socket - socket-path.ts # resolves default socket path + ensures dirs + cleanup - daemon-server.ts # Unix socket server + request router - cli/ - yargs-app.ts # builds yargs instance, registers commands - daemon-client.ts # CLI -> daemon client (unix socket, protocol) - commands/ - daemon.ts # yargs commands: daemon start/stop/status/restart - tools.ts # yargs command: tools (list available tool commands) - register-tool-commands.ts # auto-register tool commands from schemas - schema-to-yargs.ts # converts Zod schema shape -> yargs options - output.ts # prints ToolResponse to terminal -``` - -### Modified Files - -- `src/server/bootstrap.ts` - Refactor to use shared runtime bootstrap -- `src/core/plugin-types.ts` - Extend `PluginMeta` with optional CLI metadata -- `tsup.config.ts` - Add `cli` and `daemon` entries -- `package.json` - Add `xcodebuildcli` bin, add yargs dependency - ---- - -## Core Interfaces - -### Tool Definition and Catalog - -**File:** `src/runtime/types.ts` - -```typescript -import type * as z from 'zod'; -import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; -import type { ToolResponse } from '../types/common.ts'; -import type { ToolSchemaShape, PluginMeta } from '../core/plugin-types.ts'; - -export type RuntimeKind = 'cli' | 'daemon' | 'mcp'; - -export interface ToolDefinition { - /** Stable CLI command name (kebab-case, disambiguated) */ - cliName: string; - - /** Original MCP tool name as declared today (unchanged) */ - mcpName: string; - - /** Workflow directory name (e.g., "simulator", "device", "logging") */ - workflow: string; - - description?: string; - annotations?: ToolAnnotations; - - /** - * Schema shape used to generate yargs flags for CLI. - * Must include ALL parameters (not the session-default-hidden version). - */ - cliSchema: ToolSchemaShape; - - /** - * Schema shape used for MCP registration (what you already have). - */ - mcpSchema: ToolSchemaShape; - - /** - * Whether CLI MUST route this tool to the daemon (stateful operations). - */ - stateful: boolean; - - /** - * Shared handler (same used by MCP today). No duplication. - */ - handler: PluginMeta['handler']; -} - -export interface ToolCatalog { - tools: ToolDefinition[]; - getByCliName(name: string): ToolDefinition | null; - resolve(input: string): { tool?: ToolDefinition; ambiguous?: string[]; notFound?: boolean }; -} - -export interface InvokeOptions { - runtime: RuntimeKind; - enabledWorkflows?: string[]; - forceDaemon?: boolean; - socketPath?: string; -} - -export interface ToolInvoker { - invoke(toolName: string, args: Record, opts: InvokeOptions): Promise; -} -``` - -### Plugin CLI Metadata Extension - -**File:** `src/core/plugin-types.ts` (modify) - -```typescript -export interface PluginCliMeta { - /** Optional override of derived CLI name */ - name?: string; - /** Full schema shape for CLI flag generation (legacy, includes session-managed fields) */ - schema?: ToolSchemaShape; - /** Mark tool as requiring daemon routing */ - stateful?: boolean; -} - -export interface PluginMeta { - readonly name: string; - readonly schema: ToolSchemaShape; - readonly description?: string; - readonly annotations?: ToolAnnotations; - readonly cli?: PluginCliMeta; // NEW (optional) - handler(params: Record): Promise; -} -``` - -### Daemon Protocol - -**File:** `src/daemon/protocol.ts` - -```typescript -export const DAEMON_PROTOCOL_VERSION = 1 as const; - -export type DaemonMethod = - | 'daemon.status' - | 'daemon.stop' - | 'tool.list' - | 'tool.invoke'; - -export interface DaemonRequest { - v: typeof DAEMON_PROTOCOL_VERSION; - id: string; - method: DaemonMethod; - params?: TParams; -} - -export type DaemonErrorCode = - | 'BAD_REQUEST' - | 'NOT_FOUND' - | 'AMBIGUOUS_TOOL' - | 'TOOL_FAILED' - | 'INTERNAL'; - -export interface DaemonError { - code: DaemonErrorCode; - message: string; - data?: unknown; -} - -export interface DaemonResponse { - v: typeof DAEMON_PROTOCOL_VERSION; - id: string; - result?: TResult; - error?: DaemonError; -} - -export interface ToolInvokeParams { - tool: string; - args: Record; -} - -export interface ToolInvokeResult { - response: unknown; -} - -export interface DaemonStatusResult { - pid: number; - socketPath: string; - startedAt: string; - enabledWorkflows: string[]; - toolCount: number; -} -``` - ---- - -## Shared Runtime Bootstrap - -**File:** `src/runtime/bootstrap-runtime.ts` - -```typescript -import process from 'node:process'; -import { initConfigStore, getConfig, type RuntimeConfigOverrides } from '../utils/config-store.ts'; -import { sessionStore } from '../utils/session-store.ts'; -import { getDefaultFileSystemExecutor } from '../utils/command.ts'; -import type { FileSystemExecutor } from '../utils/FileSystemExecutor.ts'; -import type { RuntimeKind } from './types.ts'; - -export interface BootstrapRuntimeOptions { - runtime: RuntimeKind; - cwd?: string; - fs?: FileSystemExecutor; - configOverrides?: RuntimeConfigOverrides; -} - -export interface BootstrappedRuntime { - runtime: RuntimeKind; - cwd: string; - config: ReturnType; -} - -export async function bootstrapRuntime(opts: BootstrapRuntimeOptions): Promise { - const cwd = opts.cwd ?? process.cwd(); - const fs = opts.fs ?? getDefaultFileSystemExecutor(); - - await initConfigStore({ cwd, fs, overrides: opts.configOverrides }); - - const config = getConfig(); - - const defaults = config.sessionDefaults ?? {}; - if (Object.keys(defaults).length > 0) { - sessionStore.setDefaults(defaults); - } - - return { runtime: opts.runtime, cwd, config }; -} -``` - ---- - -## Tool Catalog - -**File:** `src/runtime/tool-catalog.ts` - -```typescript -import { loadWorkflowGroups } from '../core/plugin-registry.ts'; -import { resolveSelectedWorkflows } from '../utils/workflow-selection.ts'; -import type { ToolCatalog, ToolDefinition } from './types.ts'; -import { toKebabCase, disambiguateCliNames } from './naming.ts'; - -export async function buildToolCatalog(opts: { - enabledWorkflows: string[]; -}): Promise { - const workflowGroups = await loadWorkflowGroups(); - const selection = resolveSelectedWorkflows(opts.enabledWorkflows, workflowGroups); - - const tools: ToolDefinition[] = []; - - for (const wf of selection.selectedWorkflows) { - for (const tool of wf.tools) { - const baseCliName = tool.cli?.name ?? toKebabCase(tool.name); - tools.push({ - cliName: baseCliName, - mcpName: tool.name, - workflow: wf.directoryName, - description: tool.description, - annotations: tool.annotations, - mcpSchema: tool.schema, - cliSchema: tool.cli?.schema ?? tool.schema, - stateful: Boolean(tool.cli?.stateful), - handler: tool.handler, - }); - } - } - - const disambiguated = disambiguateCliNames(tools); - - return { - tools: disambiguated, - getByCliName(name) { - return disambiguated.find((t) => t.cliName === name) ?? null; - }, - resolve(input) { - const exact = disambiguated.filter((t) => t.cliName === input); - if (exact.length === 1) return { tool: exact[0] }; - - const aliasMatches = disambiguated.filter((t) => toKebabCase(t.mcpName) === input); - if (aliasMatches.length === 1) return { tool: aliasMatches[0] }; - if (aliasMatches.length > 1) return { ambiguous: aliasMatches.map((t) => t.cliName) }; - - return { notFound: true }; - }, - }; -} -``` - -**File:** `src/runtime/naming.ts` - -```typescript -import type { ToolDefinition } from './types.ts'; - -export function toKebabCase(name: string): string { - return name - .trim() - .replace(/_/g, '-') - .replace(/\s+/g, '-') - .replace(/[A-Z]/g, (m) => m.toLowerCase()) - .toLowerCase(); -} - -export function disambiguateCliNames(tools: ToolDefinition[]): ToolDefinition[] { - const groups = new Map(); - for (const t of tools) { - groups.set(t.cliName, [...(groups.get(t.cliName) ?? []), t]); - } - - return tools.map((t) => { - const same = groups.get(t.cliName) ?? []; - if (same.length <= 1) return t; - return { ...t, cliName: `${t.workflow}-${t.cliName}` }; - }); -} -``` - ---- - -## Daemon Architecture - -### Socket Path - -**File:** `src/daemon/socket-path.ts` - -```typescript -import { mkdirSync, existsSync, unlinkSync } from 'node:fs'; -import { homedir } from 'node:os'; -import { join } from 'node:path'; - -export function defaultSocketPath(): string { - return join(homedir(), '.xcodebuildcli', 'daemon.sock'); -} - -export function ensureSocketDir(socketPath: string): void { - const dir = socketPath.split('/').slice(0, -1).join('/'); - if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 }); -} - -export function removeStaleSocket(socketPath: string): void { - if (existsSync(socketPath)) unlinkSync(socketPath); -} -``` - -### Length-Prefixed Framing - -**File:** `src/daemon/framing.ts` - -```typescript -import type net from 'node:net'; - -export function writeFrame(socket: net.Socket, obj: unknown): void { - const json = Buffer.from(JSON.stringify(obj), 'utf8'); - const header = Buffer.alloc(4); - header.writeUInt32BE(json.length, 0); - socket.write(Buffer.concat([header, json])); -} - -export function createFrameReader(onMessage: (msg: unknown) => void) { - let buffer = Buffer.alloc(0); - - return (chunk: Buffer) => { - buffer = Buffer.concat([buffer, chunk]); - - while (buffer.length >= 4) { - const len = buffer.readUInt32BE(0); - if (buffer.length < 4 + len) return; - - const payload = buffer.subarray(4, 4 + len); - buffer = buffer.subarray(4 + len); - - const msg = JSON.parse(payload.toString('utf8')); - onMessage(msg); - } - }; -} -``` - -### Daemon Server - -**File:** `src/daemon/daemon-server.ts` - -```typescript -import net from 'node:net'; -import { writeFrame, createFrameReader } from './framing.ts'; -import type { ToolCatalog } from '../runtime/types.ts'; -import type { DaemonRequest, DaemonResponse, ToolInvokeParams } from './protocol.ts'; -import { DAEMON_PROTOCOL_VERSION } from './protocol.ts'; -import { DefaultToolInvoker } from '../runtime/tool-invoker.ts'; - -export interface DaemonServerContext { - socketPath: string; - startedAt: string; - enabledWorkflows: string[]; - catalog: ToolCatalog; -} - -export function startDaemonServer(ctx: DaemonServerContext): net.Server { - const invoker = new DefaultToolInvoker(ctx.catalog); - - const server = net.createServer((socket) => { - const onData = createFrameReader(async (msg) => { - const req = msg as DaemonRequest; - const base = { v: DAEMON_PROTOCOL_VERSION, id: req?.id ?? 'unknown' }; - - try { - if (req.v !== DAEMON_PROTOCOL_VERSION) { - return writeFrame(socket, { ...base, error: { code: 'BAD_REQUEST', message: 'Unsupported protocol version' } }); - } - - switch (req.method) { - case 'daemon.status': - return writeFrame(socket, { - ...base, - result: { - pid: process.pid, - socketPath: ctx.socketPath, - startedAt: ctx.startedAt, - enabledWorkflows: ctx.enabledWorkflows, - toolCount: ctx.catalog.tools.length, - }, - }); - - case 'daemon.stop': - writeFrame(socket, { ...base, result: { ok: true } }); - server.close(() => process.exit(0)); - return; - - case 'tool.list': - return writeFrame(socket, { - ...base, - result: ctx.catalog.tools.map((t) => ({ - name: t.cliName, - workflow: t.workflow, - description: t.description ?? '', - stateful: t.stateful, - })), - }); - - case 'tool.invoke': { - const params = req.params as ToolInvokeParams; - const response = await invoker.invoke(params.tool, params.args ?? {}, { - runtime: 'daemon', - enabledWorkflows: ctx.enabledWorkflows, - }); - return writeFrame(socket, { ...base, result: { response } }); - } - - default: - return writeFrame(socket, { ...base, error: { code: 'BAD_REQUEST', message: `Unknown method` } }); - } - } catch (error) { - return writeFrame(socket, { - ...base, - error: { code: 'INTERNAL', message: error instanceof Error ? error.message : String(error) }, - }); - } - }); - - socket.on('data', onData); - }); - - return server; -} -``` - -### Daemon Entry Point - -**File:** `src/daemon.ts` - -```typescript -#!/usr/bin/env node -import net from 'node:net'; -import { bootstrapRuntime } from './runtime/bootstrap-runtime.ts'; -import { buildToolCatalog } from './runtime/tool-catalog.ts'; -import { ensureSocketDir, defaultSocketPath, removeStaleSocket } from './daemon/socket-path.ts'; -import { startDaemonServer } from './daemon/daemon-server.ts'; - -async function main(): Promise { - const runtime = await bootstrapRuntime({ runtime: 'daemon' }); - const socketPath = process.env.XCODEBUILDCLI_SOCKET ?? defaultSocketPath(); - - ensureSocketDir(socketPath); - - try { - await new Promise((resolve, reject) => { - const s = net.createConnection(socketPath, () => { - s.end(); - reject(new Error('Daemon already running')); - }); - s.on('error', () => resolve()); - }); - } catch (e) { - throw e; - } - - removeStaleSocket(socketPath); - - const catalog = await buildToolCatalog({ enabledWorkflows: runtime.config.enabledWorkflows }); - - const server = startDaemonServer({ - socketPath, - startedAt: new Date().toISOString(), - enabledWorkflows: runtime.config.enabledWorkflows, - catalog, - }); - - server.listen(socketPath); -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); -``` - ---- - -## CLI Architecture - -### CLI Entry Point - -**File:** `src/cli.ts` - -```typescript -#!/usr/bin/env node -import { bootstrapRuntime } from './runtime/bootstrap-runtime.ts'; -import { buildToolCatalog } from './runtime/tool-catalog.ts'; -import { buildYargsApp } from './cli/yargs-app.ts'; - -async function main(): Promise { - const runtime = await bootstrapRuntime({ runtime: 'cli' }); - const catalog = await buildToolCatalog({ enabledWorkflows: runtime.config.enabledWorkflows }); - - const yargsApp = buildYargsApp({ catalog, runtimeConfig: runtime.config }); - await yargsApp.parseAsync(); -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); -``` - -### Yargs App - -**File:** `src/cli/yargs-app.ts` - -```typescript -import yargs from 'yargs'; -import { hideBin } from 'yargs/helpers'; -import type { ToolCatalog } from '../runtime/types.ts'; -import { registerDaemonCommands } from './commands/daemon.ts'; -import { registerToolsCommand } from './commands/tools.ts'; -import { registerToolCommands } from './register-tool-commands.ts'; -import { version } from '../version.ts'; - -export function buildYargsApp(opts: { - catalog: ToolCatalog; - runtimeConfig: { enabledWorkflows: string[] }; -}) { - const app = yargs(hideBin(process.argv)) - .scriptName('xcodebuildcli') - .strict() - .recommendCommands() - .wrap(Math.min(120, yargs.terminalWidth())) - .parserConfiguration({ - 'camel-case-expansion': true, - 'strip-dashed': true, - }) - .option('socket', { - type: 'string', - describe: 'Override daemon unix socket path', - default: process.env.XCODEBUILDCLI_SOCKET, - }) - .option('daemon', { - type: 'boolean', - describe: 'Force daemon execution even for stateless tools', - default: false, - }) - .version(version) - .help(); - - registerDaemonCommands(app); - registerToolsCommand(app, opts.catalog); - registerToolCommands(app, opts.catalog); - - return app; -} -``` - -### Schema to Yargs Conversion - -**File:** `src/cli/schema-to-yargs.ts` - -```typescript -import * as z from 'zod'; - -export type YargsOpt = - | { type: 'string'; array?: boolean; choices?: string[]; describe?: string } - | { type: 'number'; array?: boolean; describe?: string } - | { type: 'boolean'; describe?: string }; - -function unwrap(t: z.ZodTypeAny): z.ZodTypeAny { - if (t instanceof z.ZodOptional) return unwrap(t.unwrap()); - if (t instanceof z.ZodNullable) return unwrap(t.unwrap()); - if (t instanceof z.ZodDefault) return unwrap(t.removeDefault()); - if (t instanceof z.ZodEffects) return unwrap(t.innerType()); - return t; -} - -export function zodToYargsOption(t: z.ZodTypeAny): YargsOpt | null { - const u = unwrap(t); - - if (u instanceof z.ZodString) return { type: 'string' }; - if (u instanceof z.ZodNumber) return { type: 'number' }; - if (u instanceof z.ZodBoolean) return { type: 'boolean' }; - - if (u instanceof z.ZodEnum) return { type: 'string', choices: u.options }; - if (u instanceof z.ZodNativeEnum) return { type: 'string', choices: Object.values(u.enum) as string[] }; - - if (u instanceof z.ZodArray) { - const inner = unwrap(u.element); - if (inner instanceof z.ZodString) return { type: 'string', array: true }; - if (inner instanceof z.ZodNumber) return { type: 'number', array: true }; - return null; - } - - return null; -} -``` - -### Tool Command Registration - -**File:** `src/cli/register-tool-commands.ts` - -```typescript -import type { Argv } from 'yargs'; -import type { ToolCatalog } from '../runtime/types.ts'; -import { DefaultToolInvoker } from '../runtime/tool-invoker.ts'; -import { zodToYargsOption } from './schema-to-yargs.ts'; -import { toKebabCase } from '../runtime/naming.ts'; -import { printToolResponse } from './output.ts'; - -export function registerToolCommands(app: Argv, catalog: ToolCatalog): void { - const invoker = new DefaultToolInvoker(catalog); - - for (const tool of catalog.tools) { - app.command( - tool.cliName, - tool.description ?? '', - (y) => { - y.option('json', { - type: 'string', - describe: 'JSON object of tool args (merged with flags)', - }); - - for (const [key, zt] of Object.entries(tool.cliSchema)) { - const opt = zodToYargsOption(zt as z.ZodTypeAny); - if (!opt) continue; - - const flag = toKebabCase(key); - y.option(flag, { - type: opt.type, - array: (opt as { array?: boolean }).array, - choices: (opt as { choices?: string[] }).choices, - describe: (opt as { describe?: string }).describe, - }); - } - - return y; - }, - async (argv) => { - const { json, socket, daemon, _, $0, ...rest } = argv as Record; - - const jsonArgs = json ? (JSON.parse(String(json)) as Record) : {}; - const flagArgs = rest as Record; - const args = { ...flagArgs, ...jsonArgs }; - - const response = await invoker.invoke(tool.cliName, args, { - runtime: 'cli', - forceDaemon: Boolean(daemon), - socketPath: socket as string | undefined, - }); - - printToolResponse(response); - }, - ); - } -} -``` - -### CLI Output - -**File:** `src/cli/output.ts` - -```typescript -import type { ToolResponse } from '../types/common.ts'; - -export function printToolResponse(res: ToolResponse): void { - for (const item of res.content ?? []) { - if (item.type === 'text') { - console.log(item.text); - } else if (item.type === 'image') { - console.log(`[image ${item.mimeType}, ${item.data.length} bytes base64]`); - } - } - if (res.isError) process.exitCode = 1; -} -``` - ---- - -## Build Configuration - -### tsup.config.ts - -```typescript -export default defineConfig({ - entry: { - index: 'src/index.ts', - 'doctor-cli': 'src/doctor-cli.ts', - cli: 'src/cli.ts', - daemon: 'src/daemon.ts', - }, - // ...existing config... -}); -``` - -### package.json - -```json -{ - "bin": { - "xcodebuildmcp": "build/cli.js", - "xcodebuildmcp-doctor": "build/doctor-cli.js", - "xcodebuildcli": "build/cli.js" - }, - "dependencies": { - "yargs": "^17.7.2" - } -} -``` - ---- - -## Implementation Phases - -### Phase 1: Foundation - -1. Add `src/runtime/bootstrap-runtime.ts` -2. Refactor `src/server/bootstrap.ts` to call shared bootstrap -3. Add `src/cli.ts`, `src/daemon.ts` entries to `tsup.config.ts` -4. Add `xcodebuildcli` bin + `yargs` dependency in `package.json` - -**Result:** Builds produce `build/cli.js` and `build/daemon.js`, MCP server unchanged. - -### Phase 2: Tool Catalog + Direct CLI Invocation (Stateless) - -1. Implement `src/runtime/naming.ts`, `src/runtime/tool-catalog.ts`, `src/runtime/tool-invoker.ts`, `src/runtime/types.ts` -2. Implement `src/cli/yargs-app.ts`, `src/cli/schema-to-yargs.ts`, `src/cli/register-tool-commands.ts`, `src/cli/output.ts` -3. Add `xcodebuildcli tools` list command - -**Result:** `xcodebuildcli ` works for stateless tools in-process. - -### Phase 3: Daemon Protocol + Server + Client - -1. Implement `src/daemon/protocol.ts`, `src/daemon/framing.ts`, `src/daemon/socket-path.ts` -2. Implement `src/daemon/daemon-server.ts` and wire into `src/daemon.ts` -3. Implement `src/cli/daemon-client.ts` -4. Implement `xcodebuildcli daemon start|stop|status|restart` - -**Result:** Daemon starts, responds to status, can invoke tools. - -### Phase 4: Stateful Routing - -1. Add `cli.stateful = true` metadata to all stateful tools (logging, video, debugging, swift-package background) -2. Modify `DefaultToolInvoker` to require daemon when `tool.stateful === true` -3. Add CLI auto-start behavior: if daemon required and not running, start it programmatically - -**Result:** Stateful commands run through daemon reliably; state persists across CLI invocations. - -### Phase 5: Full CLI Schema Coverage - -1. For all tools, ensure `tool.cli.schema` is present and complete -2. Ensure schema-to-yargs supports all Zod types used (string/number/boolean/enum/array) -3. Require complex/nested values via `--json` fallback - -**Result:** CLI is first-class with full native flags. - ---- - -## Command Examples - -```bash -# List available tools -xcodebuildcli tools - -# Run stateless tool with native flags -xcodebuildcli build-sim --scheme MyApp --project-path ./App.xcodeproj - -# Run tool with JSON input -xcodebuildcli build-sim --json '{"scheme":"MyApp"}' - -# Daemon management -xcodebuildcli daemon start -xcodebuildcli daemon status -xcodebuildcli daemon stop - -# Stateful tools (automatically route to daemon) -xcodebuildcli start-sim-log-cap --simulator-id ABCD-1234 -xcodebuildcli stop-sim-log-cap --session-id xyz - -# Force daemon execution for any tool -xcodebuildcli build-sim --daemon --scheme MyApp - -# Help -xcodebuildcli --help -xcodebuildcli build-sim --help -``` - ---- - -## Invariants - -1. **MCP unchanged**: `xcodebuildmcp` continues to work exactly as before -2. **Smithery unchanged**: `src/smithery.ts` continues to work -3. **No code duplication**: CLI invokes same `PluginMeta.handler` functions -4. **Session defaults identical**: All runtimes use `bootstrapRuntime()` โ†’ `sessionStore` -5. **Tool logic shared**: `src/mcp/tools/*` remains single source of truth -6. **Daemon is macOS-only**: Uses Unix domain sockets; CLI fails with clear error on non-macOS - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/daemon/socket-path.ts -```ts -import { mkdirSync, existsSync, unlinkSync } from 'node:fs'; -import { homedir } from 'node:os'; -import { join, dirname } from 'node:path'; - -/** - * Get the default socket path for the daemon. - * Located in ~/.xcodebuildcli/daemon.sock - */ -export function defaultSocketPath(): string { - return join(homedir(), '.xcodebuildcli', 'daemon.sock'); -} - -/** - * Ensure the directory for the socket exists with proper permissions. - */ -export function ensureSocketDir(socketPath: string): void { - const dir = dirname(socketPath); - if (!existsSync(dir)) { - mkdirSync(dir, { recursive: true, mode: 0o700 }); - } -} - -/** - * Remove a stale socket file if it exists. - * Should only be called after confirming no daemon is running. - */ -export function removeStaleSocket(socketPath: string): void { - if (existsSync(socketPath)) { - unlinkSync(socketPath); - } -} - -/** - * Get the socket path from environment or use default. - */ -export function getSocketPath(): string { - return process.env.XCODEBUILDCLI_SOCKET ?? defaultSocketPath(); -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/runtime/tool-catalog.ts -```ts -import { loadWorkflowGroups } from '../core/plugin-registry.ts'; -import { resolveSelectedWorkflows } from '../utils/workflow-selection.ts'; -import type { ToolCatalog, ToolDefinition, ToolResolution } from './types.ts'; -import { toKebabCase, disambiguateCliNames } from './naming.ts'; - -export async function buildToolCatalog(opts: { - enabledWorkflows: string[]; -}): Promise { - const workflowGroups = await loadWorkflowGroups(); - const selection = resolveSelectedWorkflows(opts.enabledWorkflows, workflowGroups); - - const tools: ToolDefinition[] = []; - - for (const wf of selection.selectedWorkflows) { - for (const tool of wf.tools) { - const baseCliName = tool.cli?.name ?? toKebabCase(tool.name); - tools.push({ - cliName: baseCliName, // Will be disambiguated below - mcpName: tool.name, - workflow: wf.directoryName, - description: tool.description, - annotations: tool.annotations, - mcpSchema: tool.schema, - cliSchema: tool.cli?.schema ?? tool.schema, - stateful: Boolean(tool.cli?.stateful), - handler: tool.handler, - }); - } - } - - const disambiguated = disambiguateCliNames(tools); - - return createCatalog(disambiguated); -} - -function createCatalog(tools: ToolDefinition[]): ToolCatalog { - // Build lookup maps for fast resolution - const byCliName = new Map(); - const byMcpKebab = new Map(); - - for (const tool of tools) { - byCliName.set(tool.cliName, tool); - - // Also index by the kebab-case of MCP name (for aliases) - const mcpKebab = toKebabCase(tool.mcpName); - const existing = byMcpKebab.get(mcpKebab) ?? []; - byMcpKebab.set(mcpKebab, [...existing, tool]); - } - - return { - tools, - - getByCliName(name: string): ToolDefinition | null { - return byCliName.get(name) ?? null; - }, - - resolve(input: string): ToolResolution { - const normalized = input.toLowerCase().trim(); - - // Try exact CLI name match first - const exact = byCliName.get(normalized); - if (exact) { - return { tool: exact }; - } - - // Try kebab-case of MCP name (alias) - const mcpKebab = toKebabCase(normalized); - const aliasMatches = byMcpKebab.get(mcpKebab); - if (aliasMatches && aliasMatches.length === 1) { - return { tool: aliasMatches[0] }; - } - if (aliasMatches && aliasMatches.length > 1) { - return { ambiguous: aliasMatches.map((t) => t.cliName) }; - } - - // Try matching by MCP name directly (for underscore-style names) - const byMcpDirect = tools.find( - (t) => t.mcpName.toLowerCase() === normalized, - ); - if (byMcpDirect) { - return { tool: byMcpDirect }; - } - - return { notFound: true }; - }, - }; -} - -/** - * Get a list of all available tool names for display. - */ -export function listToolNames(catalog: ToolCatalog): string[] { - return catalog.tools.map((t) => t.cliName).sort(); -} - -/** - * Get tools grouped by workflow for display. - */ -export function groupToolsByWorkflow( - catalog: ToolCatalog, -): Map { - const groups = new Map(); - - for (const tool of catalog.tools) { - const existing = groups.get(tool.workflow) ?? []; - groups.set(tool.workflow, [...existing, tool]); - } - - return groups; -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/cli.ts -```ts -#!/usr/bin/env node -import { bootstrapRuntime } from './runtime/bootstrap-runtime.ts'; -import { buildToolCatalog } from './runtime/tool-catalog.ts'; -import { buildYargsApp } from './cli/yargs-app.ts'; - -async function main(): Promise { - // CLI mode uses disableSessionDefaults to show all tool parameters as flags - const result = await bootstrapRuntime({ - runtime: 'cli', - configOverrides: { - disableSessionDefaults: true, - }, - }); - const catalog = await buildToolCatalog({ - enabledWorkflows: result.runtime.config.enabledWorkflows, - }); - - const yargsApp = buildYargsApp({ - catalog, - runtimeConfig: result.runtime.config, - }); - - await yargsApp.parseAsync(); -} - -main().catch((err) => { - console.error(err instanceof Error ? err.message : String(err)); - process.exit(1); -}); - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/runtime/naming.ts -```ts -import type { ToolDefinition } from './types.ts'; - -/** - * Convert a tool name to kebab-case for CLI usage. - * Examples: - * build_sim -> build-sim - * startSimLogCap -> start-sim-log-cap - * BuildSimulator -> build-simulator - */ -export function toKebabCase(name: string): string { - return name - .trim() - // Replace underscores with hyphens - .replace(/_/g, '-') - // Insert hyphen before uppercase letters (for camelCase/PascalCase) - .replace(/([a-z])([A-Z])/g, '$1-$2') - // Replace spaces with hyphens - .replace(/\s+/g, '-') - // Convert to lowercase - .toLowerCase() - // Remove any duplicate hyphens - .replace(/-+/g, '-') - // Trim leading/trailing hyphens - .replace(/^-|-$/g, ''); -} - -/** - * Convert kebab-case CLI flag back to camelCase for tool params. - * Examples: - * project-path -> projectPath - * simulator-name -> simulatorName - */ -export function toCamelCase(kebab: string): string { - return kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()); -} - -/** - * Disambiguate CLI names when duplicates exist across workflows. - * If multiple tools have the same kebab-case name, prefix with workflow name. - */ -export function disambiguateCliNames(tools: ToolDefinition[]): ToolDefinition[] { - // Group tools by their base CLI name - const groups = new Map(); - for (const tool of tools) { - const existing = groups.get(tool.cliName) ?? []; - groups.set(tool.cliName, [...existing, tool]); - } - - // Disambiguate tools that share the same CLI name - return tools.map((tool) => { - const sameNameTools = groups.get(tool.cliName) ?? []; - if (sameNameTools.length <= 1) { - return tool; - } - - // Prefix with workflow name for disambiguation - const disambiguatedName = `${tool.workflow}-${tool.cliName}`; - return { ...tool, cliName: disambiguatedName }; - }); -} - -/** - * Convert CLI argv keys (kebab-case) back to tool param keys (camelCase). - */ -export function convertArgvToToolParams( - argv: Record, -): Record { - const result: Record = {}; - for (const [key, value] of Object.entries(argv)) { - // Skip yargs internal keys - if (key === '_' || key === '$0') continue; - // Convert kebab-case to camelCase - const camelKey = toCamelCase(key); - result[camelKey] = value; - } - return result; -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/cli/commands/daemon.ts -```ts -import type { Argv } from 'yargs'; -import { spawn } from 'node:child_process'; -import { fileURLToPath } from 'node:url'; -import { dirname, resolve } from 'node:path'; -import { DaemonClient } from '../daemon-client.ts'; -import { getSocketPath } from '../../daemon/socket-path.ts'; - -/** - * Get the path to the daemon executable. - */ -function getDaemonPath(): string { - // In the built output, daemon.js is in the same directory as cli.js - const currentFile = fileURLToPath(import.meta.url); - const buildDir = dirname(currentFile); - return resolve(buildDir, 'daemon.js'); -} - -/** - * Register daemon management commands. - */ -export function registerDaemonCommands(app: Argv): void { - app.command( - 'daemon ', - 'Manage the xcodebuildcli daemon', - (yargs) => { - return yargs - .positional('action', { - describe: 'Daemon action', - choices: ['start', 'stop', 'status', 'restart'] as const, - demandOption: true, - }) - .option('foreground', { - alias: 'f', - type: 'boolean', - default: false, - describe: 'Run daemon in foreground (for debugging)', - }); - }, - async (argv) => { - const action = argv.action as string; - const socketPath = (argv.socket as string | undefined) ?? getSocketPath(); - const client = new DaemonClient({ socketPath }); - - switch (action) { - case 'status': - await handleStatus(client); - break; - case 'stop': - await handleStop(client); - break; - case 'start': - await handleStart(socketPath, argv.foreground as boolean); - break; - case 'restart': - await handleRestart(client, socketPath, argv.foreground as boolean); - break; - } - }, - ); -} - -async function handleStatus(client: DaemonClient): Promise { - try { - const status = await client.status(); - console.log('Daemon Status: Running'); - console.log(` PID: ${status.pid}`); - console.log(` Socket: ${status.socketPath}`); - console.log(` Started: ${status.startedAt}`); - console.log(` Tools: ${status.toolCount}`); - console.log(` Workflows: ${status.enabledWorkflows.join(', ') || '(default)'}`); - } catch (err) { - if (err instanceof Error && err.message.includes('not running')) { - console.log('Daemon Status: Not running'); - } else { - console.error('Error:', err instanceof Error ? err.message : String(err)); - process.exitCode = 1; - } - } -} - -async function handleStop(client: DaemonClient): Promise { - try { - await client.stop(); - console.log('Daemon stopped'); - } catch (err) { - if (err instanceof Error && err.message.includes('not running')) { - console.log('Daemon is not running'); - } else { - console.error('Error:', err instanceof Error ? err.message : String(err)); - process.exitCode = 1; - } - } -} - -async function handleStart( - socketPath: string, - foreground: boolean, -): Promise { - const client = new DaemonClient({ socketPath }); - - // Check if already running - const isRunning = await client.isRunning(); - if (isRunning) { - console.log('Daemon is already running'); - return; - } - - const daemonPath = getDaemonPath(); - - if (foreground) { - // Run in foreground (useful for debugging) - console.log('Starting daemon in foreground...'); - console.log(`Socket: ${socketPath}`); - console.log('Press Ctrl+C to stop\n'); - - const child = spawn(process.execPath, [daemonPath], { - stdio: 'inherit', - env: { - ...process.env, - XCODEBUILDCLI_SOCKET: socketPath, - }, - }); - - child.on('exit', (code) => { - process.exit(code ?? 0); - }); - } else { - // Run in background (detached) - const child = spawn(process.execPath, [daemonPath], { - detached: true, - stdio: 'ignore', - env: { - ...process.env, - XCODEBUILDCLI_SOCKET: socketPath, - }, - }); - - child.unref(); - - // Wait a bit and check if it started - await new Promise((resolve) => setTimeout(resolve, 500)); - - const started = await client.isRunning(); - if (started) { - console.log('Daemon started'); - console.log(`Socket: ${socketPath}`); - } else { - console.error('Failed to start daemon'); - process.exitCode = 1; - } - } -} - -async function handleRestart( - client: DaemonClient, - socketPath: string, - foreground: boolean, -): Promise { - // Try to stop existing daemon - try { - const isRunning = await client.isRunning(); - if (isRunning) { - console.log('Stopping existing daemon...'); - await client.stop(); - // Wait for it to fully stop - await new Promise((resolve) => setTimeout(resolve, 500)); - } - } catch { - // Ignore errors during stop - } - - // Start new daemon - await handleStart(socketPath, foreground); -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/daemon/daemon-server.ts -```ts -import net from 'node:net'; -import { writeFrame, createFrameReader } from './framing.ts'; -import type { ToolCatalog } from '../runtime/types.ts'; -import type { - DaemonRequest, - DaemonResponse, - ToolInvokeParams, - DaemonStatusResult, - ToolListItem, -} from './protocol.ts'; -import { DAEMON_PROTOCOL_VERSION } from './protocol.ts'; -import { DefaultToolInvoker } from '../runtime/tool-invoker.ts'; -import { log } from '../utils/logger.ts'; - -export interface DaemonServerContext { - socketPath: string; - startedAt: string; - enabledWorkflows: string[]; - catalog: ToolCatalog; -} - -/** - * Start the daemon server listening on a Unix domain socket. - */ -export function startDaemonServer(ctx: DaemonServerContext): net.Server { - const invoker = new DefaultToolInvoker(ctx.catalog); - - const server = net.createServer((socket) => { - log('info', '[Daemon] Client connected'); - - const onData = createFrameReader( - async (msg) => { - const req = msg as DaemonRequest; - const base: Pick = { - v: DAEMON_PROTOCOL_VERSION, - id: req?.id ?? 'unknown', - }; - - try { - if (!req || typeof req !== 'object') { - return writeFrame(socket, { - ...base, - error: { code: 'BAD_REQUEST', message: 'Invalid request format' }, - }); - } - - if (req.v !== DAEMON_PROTOCOL_VERSION) { - return writeFrame(socket, { - ...base, - error: { - code: 'BAD_REQUEST', - message: `Unsupported protocol version: ${req.v}`, - }, - }); - } - - switch (req.method) { - case 'daemon.status': { - const result: DaemonStatusResult = { - pid: process.pid, - socketPath: ctx.socketPath, - startedAt: ctx.startedAt, - enabledWorkflows: ctx.enabledWorkflows, - toolCount: ctx.catalog.tools.length, - }; - return writeFrame(socket, { ...base, result }); - } - - case 'daemon.stop': { - log('info', '[Daemon] Stop requested'); - writeFrame(socket, { ...base, result: { ok: true } }); - // Close server and exit after a short delay to allow response to be sent - setTimeout(() => { - server.close(() => { - log('info', '[Daemon] Server closed, exiting'); - process.exit(0); - }); - }, 100); - return; - } - - case 'tool.list': { - const result: ToolListItem[] = ctx.catalog.tools.map((t) => ({ - name: t.cliName, - workflow: t.workflow, - description: t.description ?? '', - stateful: t.stateful, - })); - return writeFrame(socket, { ...base, result }); - } - - case 'tool.invoke': { - const params = req.params as ToolInvokeParams; - if (!params?.tool) { - return writeFrame(socket, { - ...base, - error: { code: 'BAD_REQUEST', message: 'Missing tool parameter' }, - }); - } - - log('info', `[Daemon] Invoking tool: ${params.tool}`); - const response = await invoker.invoke(params.tool, params.args ?? {}, { - runtime: 'daemon', - enabledWorkflows: ctx.enabledWorkflows, - }); - - return writeFrame(socket, { ...base, result: { response } }); - } - - default: - return writeFrame(socket, { - ...base, - error: { code: 'BAD_REQUEST', message: `Unknown method: ${req.method}` }, - }); - } - } catch (error) { - log('error', `[Daemon] Error handling request: ${error}`); - return writeFrame(socket, { - ...base, - error: { - code: 'INTERNAL', - message: error instanceof Error ? error.message : String(error), - }, - }); - } - }, - (err) => { - log('error', `[Daemon] Frame parse error: ${err.message}`); - }, - ); - - socket.on('data', onData); - socket.on('close', () => { - log('info', '[Daemon] Client disconnected'); - }); - socket.on('error', (err) => { - log('error', `[Daemon] Socket error: ${err.message}`); - }); - }); - - server.on('error', (err) => { - log('error', `[Daemon] Server error: ${err.message}`); - }); - - return server; -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/cli/yargs-app.ts -```ts -import yargs from 'yargs'; -import { hideBin } from 'yargs/helpers'; -import type { ToolCatalog } from '../runtime/types.ts'; -import type { ResolvedRuntimeConfig } from '../utils/config-store.ts'; -import { registerDaemonCommands } from './commands/daemon.ts'; -import { registerToolsCommand } from './commands/tools.ts'; -import { registerToolCommands } from './register-tool-commands.ts'; -import { version } from '../version.ts'; - -export interface YargsAppOptions { - catalog: ToolCatalog; - runtimeConfig: ResolvedRuntimeConfig; -} - -/** - * Build the main yargs application with all commands registered. - */ -export function buildYargsApp(opts: YargsAppOptions): ReturnType { - const app = yargs(hideBin(process.argv)) - .scriptName('xcodebuildcli') - .strict() - .recommendCommands() - .wrap(Math.min(120, yargs().terminalWidth())) - .parserConfiguration({ - // Accept --derived-data-path -> derivedDataPath - 'camel-case-expansion': true, - // Support kebab-case flags cleanly - 'strip-dashed': true, - }) - .option('socket', { - type: 'string', - describe: 'Override daemon unix socket path', - global: true, - hidden: true, - }) - .option('daemon', { - type: 'boolean', - describe: 'Force daemon execution even for stateless tools', - default: false, - global: true, - hidden: true, - }) - .version(version) - .help() - .alias('h', 'help') - .alias('v', 'version') - .epilogue( - `Run 'xcodebuildcli tools' to see all available tools.\n` + - `Run 'xcodebuildcli --help' for tool-specific help.`, - ); - - // Register command groups - registerDaemonCommands(app); - registerToolsCommand(app, opts.catalog); - registerToolCommands(app, opts.catalog); - - return app; -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/cli/register-tool-commands.ts -```ts -import type { Argv } from 'yargs'; -import type { ToolCatalog } from '../runtime/types.ts'; -import { DefaultToolInvoker } from '../runtime/tool-invoker.ts'; -import { schemaToYargsOptions, getUnsupportedSchemaKeys } from './schema-to-yargs.ts'; -import { convertArgvToToolParams } from '../runtime/naming.ts'; -import { printToolResponse, type OutputFormat } from './output.ts'; - -/** - * Register all tool commands from the catalog with yargs. - */ -export function registerToolCommands( - app: Argv, - catalog: ToolCatalog, -): void { - const invoker = new DefaultToolInvoker(catalog); - - for (const tool of catalog.tools) { - const yargsOptions = schemaToYargsOptions(tool.cliSchema); - const unsupportedKeys = getUnsupportedSchemaKeys(tool.cliSchema); - - app.command( - tool.cliName, - tool.description ?? `Run the ${tool.mcpName} tool`, - (yargs) => { - // Add --json option for complex args or full override - yargs.option('json', { - type: 'string', - describe: 'JSON object of tool args (merged with flags)', - }); - - // Add --output option for format control - yargs.option('output', { - type: 'string', - choices: ['text', 'json'] as const, - default: 'text', - describe: 'Output format', - }); - - // Register schema-derived options - for (const [flagName, config] of yargsOptions) { - yargs.option(flagName, config); - } - - // Add note about unsupported keys if any - if (unsupportedKeys.length > 0) { - yargs.epilogue( - `Note: Complex parameters (${unsupportedKeys.join(', ')}) must be passed via --json`, - ); - } - - return yargs; - }, - async (argv) => { - // Extract our options - const jsonArg = argv.json as string | undefined; - const outputFormat = (argv.output as OutputFormat) ?? 'text'; - const socketPath = argv.socket as string | undefined; - const forceDaemon = argv.daemon as boolean | undefined; - - // Parse JSON args if provided - let jsonArgs: Record = {}; - if (jsonArg) { - try { - jsonArgs = JSON.parse(jsonArg) as Record; - } catch { - console.error(`Error: Invalid JSON in --json argument`); - process.exitCode = 1; - return; - } - } - - // Convert CLI argv to tool params (kebab-case -> camelCase) - // Remove our internal options first - const { json, output, socket, daemon, _, $0, ...flagArgs } = argv as Record; - const toolParams = convertArgvToToolParams(flagArgs); - - // Merge: flag args first, then JSON overrides - const args = { ...toolParams, ...jsonArgs }; - - // Invoke the tool - const response = await invoker.invoke(tool.cliName, args, { - runtime: 'cli', - forceDaemon: Boolean(forceDaemon), - socketPath, - }); - - printToolResponse(response, outputFormat); - }, - ); - } -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/cli/daemon-client.ts -```ts -import net from 'node:net'; -import { randomUUID } from 'node:crypto'; -import { writeFrame, createFrameReader } from '../daemon/framing.ts'; -import { - DAEMON_PROTOCOL_VERSION, - type DaemonRequest, - type DaemonResponse, - type DaemonMethod, - type ToolInvokeParams, - type DaemonStatusResult, - type ToolListItem, -} from '../daemon/protocol.ts'; -import type { ToolResponse } from '../types/common.ts'; -import { getSocketPath } from '../daemon/socket-path.ts'; - -export interface DaemonClientOptions { - socketPath?: string; - timeout?: number; -} - -export class DaemonClient { - private socketPath: string; - private timeout: number; - - constructor(opts: DaemonClientOptions = {}) { - this.socketPath = opts.socketPath ?? getSocketPath(); - this.timeout = opts.timeout ?? 30000; - } - - /** - * Send a request to the daemon and wait for a response. - */ - async request( - method: DaemonMethod, - params?: unknown, - ): Promise { - const id = randomUUID(); - const req: DaemonRequest = { - v: DAEMON_PROTOCOL_VERSION, - id, - method, - params, - }; - - return new Promise((resolve, reject) => { - const socket = net.createConnection(this.socketPath); - let resolved = false; - - const cleanup = () => { - if (!resolved) { - resolved = true; - socket.destroy(); - } - }; - - const timeoutId = setTimeout(() => { - cleanup(); - reject(new Error(`Daemon request timed out after ${this.timeout}ms`)); - }, this.timeout); - - socket.on('error', (err) => { - clearTimeout(timeoutId); - cleanup(); - if (err.message.includes('ECONNREFUSED') || err.message.includes('ENOENT')) { - reject(new Error('Daemon is not running. Start it with: xcodebuildcli daemon start')); - } else { - reject(err); - } - }); - - const onData = createFrameReader( - (msg) => { - const res = msg as DaemonResponse; - if (res.id !== id) return; - - clearTimeout(timeoutId); - resolved = true; - socket.end(); - - if (res.error) { - reject(new Error(`${res.error.code}: ${res.error.message}`)); - } else { - resolve(res.result as TResult); - } - }, - (err) => { - clearTimeout(timeoutId); - cleanup(); - reject(err); - }, - ); - - socket.on('data', onData); - socket.on('connect', () => { - writeFrame(socket, req); - }); - }); - } - - /** - * Get daemon status. - */ - async status(): Promise { - return this.request('daemon.status'); - } - - /** - * Stop the daemon. - */ - async stop(): Promise { - await this.request<{ ok: boolean }>('daemon.stop'); - } - - /** - * List available tools. - */ - async listTools(): Promise { - return this.request('tool.list'); - } - - /** - * Invoke a tool. - */ - async invokeTool( - tool: string, - args: Record, - ): Promise { - const result = await this.request<{ response: ToolResponse }>( - 'tool.invoke', - { tool, args } satisfies ToolInvokeParams, - ); - return result.response; - } - - /** - * Check if daemon is running by attempting to connect. - */ - async isRunning(): Promise { - return new Promise((resolve) => { - const socket = net.createConnection(this.socketPath); - - socket.on('connect', () => { - socket.end(); - resolve(true); - }); - - socket.on('error', () => { - resolve(false); - }); - }); - } -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/runtime/tool-invoker.ts -```ts -import type { ToolCatalog, ToolInvoker, InvokeOptions } from './types.ts'; -import type { ToolResponse } from '../types/common.ts'; -import { createErrorResponse } from '../utils/responses/index.ts'; -import { DaemonClient } from '../cli/daemon-client.ts'; - -export class DefaultToolInvoker implements ToolInvoker { - constructor(private catalog: ToolCatalog) {} - - async invoke( - toolName: string, - args: Record, - opts: InvokeOptions, - ): Promise { - const resolved = this.catalog.resolve(toolName); - - if (resolved.ambiguous) { - return createErrorResponse( - 'Ambiguous tool name', - `Multiple tools match '${toolName}'. Use one of:\n- ${resolved.ambiguous.join('\n- ')}`, - ); - } - - if (resolved.notFound || !resolved.tool) { - return createErrorResponse( - 'Tool not found', - `Unknown tool '${toolName}'. Run 'xcodebuildcli tools' to see available tools.`, - ); - } - - const tool = resolved.tool; - - // Check if tool requires daemon routing - const mustUseDaemon = tool.stateful || Boolean(opts.forceDaemon); - - if (mustUseDaemon && opts.runtime === 'cli') { - // Route through daemon - const client = new DaemonClient({ socketPath: opts.socketPath }); - - // Check if daemon is running - const isRunning = await client.isRunning(); - if (!isRunning) { - return createErrorResponse( - 'Daemon not running', - `Tool '${tool.cliName}' requires the daemon for stateful operations.\n` + - `Start the daemon with: xcodebuildcli daemon start`, - ); - } - - try { - return await client.invokeTool(tool.cliName, args); - } catch (error) { - return createErrorResponse( - 'Daemon invocation failed', - error instanceof Error ? error.message : String(error), - ); - } - } - - // Direct invocation (CLI stateless or daemon internal) - try { - return await tool.handler(args); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - return createErrorResponse('Tool execution failed', message); - } - } -} - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/daemon.ts -```ts -#!/usr/bin/env node -import net from 'node:net'; -import { bootstrapRuntime } from './runtime/bootstrap-runtime.ts'; -import { buildToolCatalog } from './runtime/tool-catalog.ts'; -import { - ensureSocketDir, - removeStaleSocket, - getSocketPath, -} from './daemon/socket-path.ts'; -import { startDaemonServer } from './daemon/daemon-server.ts'; -import { log } from './utils/logger.ts'; -import { version } from './version.ts'; - -async function checkExistingDaemon(socketPath: string): Promise { - return new Promise((resolve) => { - const socket = net.createConnection(socketPath); - - socket.on('connect', () => { - socket.end(); - resolve(true); - }); - - socket.on('error', () => { - resolve(false); - }); - }); -} - -async function main(): Promise { - log('info', `[Daemon] xcodebuildcli daemon ${version} starting...`); - - const socketPath = getSocketPath(); - ensureSocketDir(socketPath); - - // Check if daemon is already running - const isRunning = await checkExistingDaemon(socketPath); - if (isRunning) { - log('error', '[Daemon] Another daemon is already running'); - console.error('Error: Daemon is already running'); - process.exit(1); - } - - // Remove stale socket file - removeStaleSocket(socketPath); - - // Bootstrap runtime with full schema (disableSessionDefaults) - const result = await bootstrapRuntime({ - runtime: 'daemon', - configOverrides: { - disableSessionDefaults: true, - }, - }); - - // Build tool catalog - const catalog = await buildToolCatalog({ - enabledWorkflows: result.runtime.config.enabledWorkflows, - }); - - log('info', `[Daemon] Loaded ${catalog.tools.length} tools`); - - // Start server - const server = startDaemonServer({ - socketPath, - startedAt: new Date().toISOString(), - enabledWorkflows: result.runtime.config.enabledWorkflows, - catalog, - }); - - server.listen(socketPath, () => { - log('info', `[Daemon] Listening on ${socketPath}`); - console.log(`Daemon started (PID: ${process.pid})`); - console.log(`Socket: ${socketPath}`); - console.log(`Tools: ${catalog.tools.length}`); - }); - - // Handle graceful shutdown - const shutdown = () => { - log('info', '[Daemon] Shutting down...'); - server.close(() => { - removeStaleSocket(socketPath); - process.exit(0); - }); - }; - - process.on('SIGTERM', shutdown); - process.on('SIGINT', shutdown); -} - -main().catch((err) => { - console.error('Daemon error:', err instanceof Error ? err.message : String(err)); - process.exit(1); -}); - -``` - -File: /Volumes/Developer/XcodeBuildMCP/src/runtime/types.ts -```ts -import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; -import type { ToolResponse } from '../types/common.ts'; -import type { ToolSchemaShape, PluginMeta } from '../core/plugin-types.ts'; - -export type RuntimeKind = 'cli' | 'daemon' | 'mcp'; - -export interface ToolDefinition { - /** Stable CLI command name (kebab-case, disambiguated) */ - cliName: string; - - /** Original MCP tool name as declared (unchanged) */ - mcpName: string; - - /** Workflow directory name (e.g., "simulator", "device", "logging") */ - workflow: string; - - description?: string; - annotations?: ToolAnnotations; - - /** - * Schema shape used to generate yargs flags for CLI. - * Must include ALL parameters (not the session-default-hidden version). - */ - cliSchema: ToolSchemaShape; - - /** - * Schema shape used for MCP registration. - */ - mcpSchema: ToolSchemaShape; - - /** - * Whether CLI MUST route this tool to the daemon (stateful operations). - */ - stateful: boolean; - - /** - * Shared handler (same used by MCP). No duplication. - */ - handler: PluginMeta['handler']; -} - -export interface ToolResolution { - tool?: ToolDefinition; - ambiguous?: string[]; - notFound?: boolean; -} - -export interface ToolCatalog { - tools: ToolDefinition[]; - - /** Exact match on cliName */ - getByCliName(name: string): ToolDefinition | null; - - /** Resolve user input, supporting aliases + ambiguity reporting */ - resolve(input: string): ToolResolution; -} - -export interface InvokeOptions { - runtime: RuntimeKind; - /** If present, overrides enabled workflows */ - enabledWorkflows?: string[]; - /** If true, route even stateless tools to daemon */ - forceDaemon?: boolean; - /** Socket path override */ - socketPath?: string; -} - -export interface ToolInvoker { - invoke( - toolName: string, - args: Record, - opts: InvokeOptions, - ): Promise; -} - -``` - - - -Create a detailed implementation plan for per-workspace daemon architecture with auto-start by default for xcodebuildcli. Use current daemon/CLI implementation to propose where to change socket-path derivation, auto-start logic, new daemon list command, and --no-daemon opt-out behavior. Include design decisions (socket path strategy, timeout, lifecycle) and how manual daemon commands remain. - -- CLI entry: xcodebuildcli uses `src/cli.ts` -> `buildYargsApp` and `registerToolCommands`. -- Tool invocation: `DefaultToolInvoker` routes to daemon when tool is stateful or forceDaemon is set; currently errors if daemon not running. -- Daemon control: `registerDaemonCommands` provides start/stop/status/restart and uses `getSocketPath` from `src/daemon/socket-path.ts`. -- Daemon runtime: `src/daemon.ts` sets up socket path, checks for existing daemon, removes stale socket, starts `startDaemonServer`. -- Daemon client: `DaemonClient` connects to socket path, provides status/stop/isRunning/invokeTool. -- Socket path: `getSocketPath` reads XCODEBUILDCLI_SOCKET env or defaults to `~/.xcodebuildcli/daemon.sock`. - -XcodeBuildMCP/src/daemon/socket-path.ts: global socket path helpers (default path, ensure dir, remove stale, env override). -XcodeBuildMCP/src/runtime/tool-invoker.ts: daemon routing for stateful tools; current error path when daemon not running. -XcodeBuildMCP/src/cli.ts: CLI bootstrap. -XcodeBuildMCP/src/cli/commands/daemon.ts: start/stop/status/restart; spawn daemon.js, uses XCODEBUILDCLI_SOCKET; 500ms startup wait. -XcodeBuildMCP/src/daemon.ts: daemon entry point, checks existing daemon, removes stale socket, starts server. -XcodeBuildMCP/src/daemon/daemon-server.ts: daemon request router, status/stop/tool.list/tool.invoke. -XcodeBuildMCP/src/cli/daemon-client.ts: request protocol, isRunning, timeouts, error messages. -XcodeBuildMCP/src/cli/yargs-app.ts: global hidden flags --socket and --daemon. -XcodeBuildMCP/src/cli/register-tool-commands.ts: constructs args, passes forceDaemon + socketPath to invoker. -XcodeBuildMCP/src/runtime/naming.ts: argv conversion utilities. -XcodeBuildMCP/src/runtime/tool-catalog.ts: tool catalog with stateful flag from plugin metadata. -XcodeBuildMCP/src/runtime/types.ts: ToolDefinition/InvokeOptions. -XcodeBuildMCP/docs/dev/CLI_CONVERSION_PLAN.md: historical plan; includes phase 4 auto-start note and overall architecture context. - - -- CLI command -> `register-tool-commands` -> `DefaultToolInvoker.invoke()` -> `DaemonClient` for stateful tools in CLI runtime. -- `registerDaemonCommands` + `DaemonClient` + `daemon.ts` all rely on `getSocketPath()` (global path unless env override). -- Auto-start would be triggered from `DefaultToolInvoker` when daemon required, or earlier in CLI handler. -- `--daemon` (forceDaemon) and `--socket` flags are passed into `DefaultToolInvoker` and `DaemonClient`. - -- Socket path strategy not decided (hash of cwd vs encoded path vs registry); need to choose and document. -- Auto-start timeout and readiness detection not defined (currently hardcoded 500ms in daemon start command). -- Daemon lifecycle/idle shutdown policy not defined. - - -Notes: Potentially relevant but not selected: `src/daemon/protocol.ts`, `src/daemon/framing.ts`, `src/cli/commands/tools.ts`, `src/utils/config-store.ts`, `src/utils/session-store.ts` if plan needs broader runtime config or tool listing behaviors. - diff --git a/docs/investigations/issue-163.md b/docs/investigations/issue-163.md deleted file mode 100644 index be2b0996..00000000 --- a/docs/investigations/issue-163.md +++ /dev/null @@ -1,83 +0,0 @@ -# Investigation: UI automation tools unavailable with Smithery install (issue #163) - -## Summary -Smithery installs ship only the compiled entrypoint, while the server hard-requires a bundled `bundled/axe` path derived from `process.argv[1]`. This makes UI automation (and simulator video capture) fail even when system `axe` exists on PATH, and Doctor can report contradictory statuses. - -## Symptoms -- UI automation tools (`snapshot_ui`, `tap`, `swipe`, etc.) fail with "Bundled axe tool not found. UI automation features are not available." -- `doctor` reports system axe present, but UI automation unavailable due to missing bundled binary. -- Smithery cache lacks `bundled/axe` directory; only `index.cjs`, `manifest.json`, `.metadata.json` present. - -## Investigation Log - -### 2026-01-06 - Initial Assessment -**Hypothesis:** Smithery packaging omits bundled binaries and server does not fallback to system axe. -**Findings:** Issue report indicates bundled path is computed relative to `process.argv[1]` and Smithery cache lacks `bundled/`. -**Evidence:** GitHub issue #163 body (Smithery cache contents; bundled path logic). -**Conclusion:** Needs code and packaging investigation. - -### 2026-01-06 - AXe path resolution and bundled-only assumption -**Hypothesis:** AXe resolution is bundled-only, so missing `bundled/axe` disables tools regardless of PATH. -**Findings:** `getAxePath()` computes `bundledAxePath` from `process.argv[1]` and returns it only if it exists; otherwise `null`. No PATH or env override. -**Evidence:** `src/utils/axe-helpers.ts:15-36` -**Conclusion:** Confirmed. Smithery layout lacking `bundled/` will always return null. - -### 2026-01-06 - UI automation and video capture gating -**Hypothesis:** UI tools and video capture preflight fail when `getAxePath()` returns null. -**Findings:** UI tools call `getAxePath()` and throw `DependencyError` if absent; `record_sim_video` preflights `areAxeToolsAvailable()` and `isAxeAtLeastVersion()`; `startSimulatorVideoCapture` returns error if `getAxePath()` is null. -**Evidence:** `src/mcp/tools/ui-testing/snapshot_ui.ts:150-164`, `src/mcp/tools/simulator/record_sim_video.ts:80-88`, `src/utils/video_capture.ts:92-99` -**Conclusion:** Confirmed. Missing bundled binary blocks all UI automation and simulator video capture. - -### 2026-01-06 - Doctor output inconsistency -**Hypothesis:** Doctor uses different checks for dependency presence vs feature availability. -**Findings:** Doctor uses `areAxeToolsAvailable()` (bundled-only) for UI automation feature status, while dependency check can succeed via `which axe` when bundled is missing. -**Evidence:** `src/mcp/tools/doctor/doctor.ts:49-68`, `src/mcp/tools/doctor/lib/doctor.deps.ts:100-132` -**Conclusion:** Confirmed. Doctor can report `axe` dependency present but UI automation unsupported. - -### 2026-01-06 - Packaging/Smithery artifact mismatch -**Hypothesis:** NPM releases include `bundled/`, Smithery builds do not. -**Findings:** `bundle:axe` creates `bundled/` and npm packaging includes it, but Smithery config has no asset inclusion hints. Release workflow bundles AXe before publish. -**Evidence:** `package.json:21-44`, `.github/workflows/release.yml:48-55`, `smithery.yaml:1-3`, `smithery.config.js:1-6` -**Conclusion:** Confirmed. Smithery build output likely omits bundled artifacts unless explicitly configured. - -### 2026-01-06 - Smithery local server deployment flow -**Hypothesis:** Smithery deploys local servers from GitHub pushes and expects build-time packaging to include assets. -**Findings:** README install flow uses Smithery CLI; `smithery.yaml` targets `local`. `bundled/` is gitignored, so it must be produced during Smitheryโ€™s deployment build. Current `npm run build` does not run `bundle:axe`. -**Evidence:** `README.md:11-74`, `smithery.yaml:1-3`, `.github/workflows/release.yml:48-62`, `.gitignore:66-68` -**Conclusion:** Confirmed. Smithery deploy must run `bundle:axe` and explicitly include `bundled/` in the produced bundle. - -### 2026-01-06 - Smithery config constraints and bundling workaround -**Hypothesis:** Adding esbuild plugins in `smithery.config.js` overrides Smitheryโ€™s bootstrap plugin. -**Findings:** Smithery CLI merges config via spread and replaces `plugins`, causing `virtual:bootstrap` resolution to fail when custom plugins are supplied. Side-effect bundling in `smithery.config.js` avoids plugin override and can copy `bundled/` into `.smithery/`. -**Evidence:** `node_modules/@smithery/cli/dist/index.js:~2716600-2717500`, `smithery.config.js:1-47` -**Conclusion:** Confirmed. Bundling must run outside esbuild plugins; Linux builders must skip binary verification. - -### 2026-01-27 - Upstream fix landed (Smithery CLI PR #532) -**Hypothesis:** Smithery CLI now supports bundling non-code assets in stdio deploys, removing the need for the local workaround. -**Findings:** PR #532 adds a `build.assets` array in `smithery.yaml` for stdio bundles, copies matched assets into `.smithery/stdio/` before packing `server.mcpb`, and preserves directory structure. It uses `fast-glob` patterns, excludes `**/node_modules/**` and `**/.git/**` by default, warns when assets are configured for non-stdio transports or when patterns match zero files, and fails the build if patterns escape the project root, hit permission errors, or match reserved root filenames (`index.cjs`, `mcpb-manifest.json`, `manifest.json`, `server.mcpb`). The PR also documents runtime access via `__dirname` with assets available at the same relative paths inside the bundle. -**Evidence:** Smithery issue `smithery-ai/cli#524` (opened Jan 22, 2026) and PR `smithery-ai/cli#532` (merged Jan 27, 2026) summary/usage/behavior sections. -**Conclusion:** We can migrate from the `smithery.config.js` side-effect bundling workaround once we upgrade to a CLI version that includes PR #532 and configure `smithery.yaml` `build.assets` for `bundled/**`. - -## Root Cause -Two coupled assumptions break Smithery installs: -1) `getAxePath()` is bundled-only and derives the path from `process.argv[1]`, which points into Smitheryโ€™s cache (missing `bundled/axe`), so it always returns null. -2) Smithery packaging does not include the `bundled/` directory, so the bundled-only resolver can never succeed under Smithery even if AXe is installed system-wide. - -## Recommendations -1. Add a robust AXe resolver: allow explicit env override and PATH fallback; keep bundled as preferred but not exclusive. -2. Distinguish bundled vs system AXe in UI tools and video capture; only apply bundled-specific env when the bundled binary is used. -3. Align Doctor output: show both bundled availability and PATH availability, and use that in the UI automation supported status. -4. Update Smithery build to run `bundle:axe` and copy `bundled/` into the Smithery bundle output; skip binary verification on non-mac builders to avoid build failures. - -## Migration Plan (official Smithery resource bundling) -1. Upgrade Smithery CLI to `>=3.4.0` on all developer machines and CI that build/deploy via Smithery. -2. Replace the `smithery.config.js` side-effect copy workaround with official bundling config in `smithery.yaml`: - - `build.assets: [ "bundled/**" ]` -3. Remove the Smithery prepack copy step once 3.4.0 is in use. -4. Ensure `bundle:axe` still runs during build so `bundled/` exists before Smithery packages resources. -5. Validate a Smithery install contains `bundled/axe` in its cache and that UI automation + `record_sim_video` work without PATH fallbacks. -6. Remove any Linux-specific skips that were only needed to avoid bundled verification for the workaround, once the official bundling flow proves stable. - -## Preventive Measures -- Add tests for AXe resolution precedence (bundled, env override, PATH) and for Doctor output consistency. -- Document Smithery-specific install requirements and verify `bundled/` presence in Smithery artifacts during CI. diff --git a/package-lock.json b/package-lock.json index 5ba22e09..f241c35f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "@bacons/xcode": "^1.0.0-alpha.24", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.23.0", - "@smithery/cli": "^3.7.0", "@types/chokidar": "^1.7.5", "@types/node": "^22.13.6", "@types/yargs": "^17.0.33", @@ -63,57 +62,6 @@ "node": ">=6.0.0" } }, - "node_modules/@anthropic-ai/mcpb": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/mcpb/-/mcpb-1.2.0.tgz", - "integrity": "sha512-XYVCxQJsr4D4ZecEXVe9PvBpKj2T31KgEiT8K4thoi7krPFIQjXj4yOolAXP8hGSJHrW1Nf4odZES1kjLNDVkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/prompts": "^6.0.1", - "commander": "^13.1.0", - "fflate": "^0.8.2", - "galactus": "^1.0.0", - "ignore": "^7.0.5", - "node-forge": "^1.3.1", - "pretty-bytes": "^5.6.0", - "zod": "^3.25.67", - "zod-to-json-schema": "^3.24.6" - }, - "bin": { - "mcpb": "dist/cli/cli.js" - } - }, - "node_modules/@anthropic-ai/mcpb/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@anthropic-ai/mcpb/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@anthropic-ai/mcpb/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@apm-js-collab/code-transformer": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz", @@ -213,91 +161,6 @@ "node": ">=18" } }, - "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260120.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260120.0.tgz", - "integrity": "sha512-JLHx3p5dpwz4wjVSis45YNReftttnI3ndhdMh5BUbbpdreN/g0jgxNt5Qp9tDFqEKl++N63qv+hxJiIIvSLR+Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260120.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260120.0.tgz", - "integrity": "sha512-1Md2tCRhZjwajsZNOiBeOVGiS3zbpLPzUDjHr4+XGTXWOA6FzzwScJwQZLa0Doc28Cp4Nr1n7xGL0Dwiz1XuOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260120.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260120.0.tgz", - "integrity": "sha512-O0mIfJfvU7F8N5siCoRDaVDuI12wkz2xlG4zK6/Ct7U9c9FiE0ViXNFWXFQm5PPj+qbkNRyhjUwhP+GCKTk5EQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260120.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260120.0.tgz", - "integrity": "sha512-aRHO/7bjxVpjZEmVVcpmhbzpN6ITbFCxuLLZSW0H9O0C0w40cDCClWSi19T87Ax/PQcYjFNT22pTewKsupkckA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260120.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260120.0.tgz", - "integrity": "sha512-ASZIz1E8sqZQqQCgcfY1PJbBpUDrxPt8NZ+lqNil0qxnO4qX38hbCsdDF2/TDAuq0Txh7nu8ztgTelfNDlb4EA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=16" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -322,17 +185,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -993,1177 +845,134 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "node": ">=8" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } + "license": "MIT" }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } } }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", - "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/confirm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", - "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", - "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.5.5", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/editor": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", - "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "external-editor": "^3.1.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/expand": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", - "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/external-editor/node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/input": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", - "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/number": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", - "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/password": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", - "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "ansi-escapes": "^4.3.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/prompts": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", - "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^3.0.1", - "@inquirer/confirm": "^4.0.1", - "@inquirer/editor": "^3.0.1", - "@inquirer/expand": "^3.0.1", - "@inquirer/input": "^3.0.1", - "@inquirer/number": "^2.0.1", - "@inquirer/password": "^3.0.1", - "@inquirer/rawlist": "^3.0.1", - "@inquirer/search": "^2.0.1", - "@inquirer/select": "^3.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/rawlist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", - "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/type": "^2.0.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/search": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", - "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/select": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", - "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.2.1", - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", - "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", - "dev": true, - "license": "MIT", - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", - "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", - "license": "MIT", - "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", - "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/@ngrok/ngrok": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok/-/ngrok-1.7.0.tgz", - "integrity": "sha512-P06o9TpxrJbiRbHQkiwy/rUrlXRupc+Z8KT4MiJfmcdWxvIdzjCaJOdnNkcOTs6DMyzIOefG5tvk/HLdtjqr0g==", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@ngrok/ngrok-android-arm64": "1.7.0", - "@ngrok/ngrok-darwin-arm64": "1.7.0", - "@ngrok/ngrok-darwin-universal": "1.7.0", - "@ngrok/ngrok-darwin-x64": "1.7.0", - "@ngrok/ngrok-freebsd-x64": "1.7.0", - "@ngrok/ngrok-linux-arm-gnueabihf": "1.7.0", - "@ngrok/ngrok-linux-arm64-gnu": "1.7.0", - "@ngrok/ngrok-linux-arm64-musl": "1.7.0", - "@ngrok/ngrok-linux-x64-gnu": "1.7.0", - "@ngrok/ngrok-linux-x64-musl": "1.7.0", - "@ngrok/ngrok-win32-arm64-msvc": "1.7.0", - "@ngrok/ngrok-win32-ia32-msvc": "1.7.0", - "@ngrok/ngrok-win32-x64-msvc": "1.7.0" - } - }, - "node_modules/@ngrok/ngrok-android-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-android-arm64/-/ngrok-android-arm64-1.7.0.tgz", - "integrity": "sha512-8tco3ID6noSaNy+CMS7ewqPoIkIM6XO5COCzsUp3Wv3XEbMSyn65RN6cflX2JdqLfUCHcMyD0ahr9IEiHwqmbQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-arm64/-/ngrok-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-+dmJSOzSO+MNDVrPOca2yYDP1W3KfP4qOlAkarIeFRIfqonQwq3QCBmcR7HAlZocLsSqEwyG6KP4RRvAuT0WGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-darwin-universal": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-universal/-/ngrok-darwin-universal-1.7.0.tgz", - "integrity": "sha512-fDEfewyE2pWGFBhOSwQZObeHUkc65U1l+3HIgSOe094TMHsqmyJD0KTCgW9KSn0VP4OvDZbAISi1T3nvqgZYhQ==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-darwin-x64/-/ngrok-darwin-x64-1.7.0.tgz", - "integrity": "sha512-+fwMi5uHd9G8BS42MMa9ye6exI5lwTcjUO6Ut497Vu0qgLONdVRenRqnEePV+Q3KtQR7NjqkMnomVfkr9MBjtw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-freebsd-x64/-/ngrok-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-2OGgbrjy3yLRrqAz5N6hlUKIWIXSpR5RjQa2chtZMsSbszQ6c9dI+uVQfOKAeo05tHMUgrYAZ7FocC+ig0dzdQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-arm-gnueabihf/-/ngrok-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-SN9YIfEQiR9xN90QVNvdgvAemqMLoFVSeTWZs779145hQMhvF9Qd9rnWi6J+2uNNK10OczdV1oc/nq1es7u/3g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-arm64-gnu/-/ngrok-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-KDMgzPKFU2kbpVSaA2RZBBia5IPdJEe063YlyVFnSMJmPYWCUnMwdybBsucXfV9u1Lw/ZjKTKotIlbTWGn3HGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-arm64-musl/-/ngrok-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-e66vUdVrBlQ0lT9ZdamB4U604zt5Gualt8/WVcUGzbu8s5LajWd6g/mzZCUjK4UepjvMpfgmCp1/+rX7Rk8d5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-x64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-x64-gnu/-/ngrok-linux-x64-gnu-1.7.0.tgz", - "integrity": "sha512-M6gF0DyOEFqXLfWxObfL3bxYZ4+PnKBHuyLVaqNfFN9Y5utY2mdPOn5422Ppbk4XoIK5/YkuhRqPJl/9FivKEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-linux-x64-musl/-/ngrok-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-4Ijm0dKeoyzZTMaYxR2EiNjtlK81ebflg/WYIO1XtleFrVy4UJEGnxtxEidYoT4BfCqi4uvXiK2Mx216xXKvog==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-win32-arm64-msvc/-/ngrok-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-u7qyWIJI2/YG1HTBnHwUR1+Z2tyGfAsUAItJK/+N1G0FeWJhIWQvSIFJHlaPy4oW1Dc8mSDBX9qvVsiQgLaRFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ngrok/ngrok-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-win32-ia32-msvc/-/ngrok-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-/UdYUsLNv/Q8j9YJsyIfq/jLCoD8WP+NidouucTUzSoDtmOsXBBT3itLrmPiZTEdEgKiFYLuC1Zon8XQQvbVLA==", - "cpu": [ - "ia32" - ], - "dev": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@ngrok/ngrok-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@ngrok/ngrok-win32-x64-msvc/-/ngrok-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-UFJg/duEWzZlLkEs61Gz6/5nYhGaKI62I8dvUGdBR3NCtIMagehnFaFxmnXZldyHmCM8U0aCIFNpWRaKcrQkoA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -2732,48 +1541,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@poppinss/colors": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", - "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^4.1.5" - } - }, - "node_modules/@poppinss/dumper": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", - "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@poppinss/colors": "^4.1.5", - "@sindresorhus/is": "^7.0.2", - "supports-color": "^10.0.0" - } - }, - "node_modules/@poppinss/dumper/node_modules/supports-color": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@poppinss/exception": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", - "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", - "dev": true, - "license": "MIT" - }, "node_modules/@prisma/instrumentation": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.2.0.tgz", @@ -3347,151 +2114,49 @@ "@opentelemetry/semantic-conventions": "^1.39.0" } }, - "node_modules/@sentry/node/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@sentry/node/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@sentry/opentelemetry": { - "version": "10.37.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.37.0.tgz", - "integrity": "sha512-elRAsmgAcaprVFV1JoxxqU9eteBz3g9fpmecfDk5KTe/Txr2OOxdIiLF/4I0g4MMib3DKtr6iGgme2J+l8H8cA==", - "license": "MIT", - "dependencies": { - "@sentry/core": "10.37.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", - "@opentelemetry/semantic-conventions": "^1.39.0" - } - }, - "node_modules/@sindresorhus/is": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", - "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@smithery/api": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@smithery/api/-/api-0.37.0.tgz", - "integrity": "sha512-ilA4jF4O4/J5+ERSmAWFt4NWslJ79SJqBVojfIU1ETzoPbRFKeLrwBvpRW8Q0UKT1FOEoH9gX7iv0WpeYdAaSA==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "@modelcontextprotocol/sdk": ">=1.0.0" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/sdk": { - "optional": true - } - } - }, - "node_modules/@smithery/cli": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@smithery/cli/-/cli-3.10.0.tgz", - "integrity": "sha512-vEKvcedSjI8IONTL0hKOrJ/k7h4n6zNwfMXdJ1KSknJUg0FtaJKRJy3qO0L6PAga2NNCOPflH3w096tM/40Bow==", - "dev": true, - "dependencies": { - "@anthropic-ai/mcpb": "^1.1.1", - "@modelcontextprotocol/sdk": "^1.25.3", - "@ngrok/ngrok": "^1.5.1", - "@smithery/api": "0.37.0", - "chalk": "^4.1.2", - "cli-spinners": "^3.3.0", - "commander": "^14.0.0", - "cors": "^2.8.5", - "cross-spawn": "^7.0.6", - "esbuild": "^0.25.10", - "express": "^5.1.0", - "fast-glob": "^3.3.3", - "flexsearch": "^0.7.43", - "inquirer": "^8.2.4", - "inquirer-autocomplete-prompt": "^2.0.0", - "jsonc-parser": "^3.3.1", - "lodash": "^4.17.21", - "miniflare": "^4.20260103.0", - "ora": "^8.2.0", - "shx": "^0.4.0", - "uuid": "^11.1.0", - "uuidv7": "^1.0.2", - "yaml": "^2.3.4", - "zod-to-json-schema": "^3.25.1" - }, - "bin": { - "smithery": "dist/index.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "optionalDependencies": { - "keytar": "^7.9.0" - }, - "peerDependencies": { - "zod": "^3.20.0 || ^4" - } - }, - "node_modules/@smithery/cli/node_modules/cli-spinners": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", - "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", - "dev": true, + "node_modules/@sentry/node/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@sentry/node/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=18.20" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@smithery/cli/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "dev": true, + "node_modules/@sentry/opentelemetry": { + "version": "10.37.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.37.0.tgz", + "integrity": "sha512-elRAsmgAcaprVFV1JoxxqU9eteBz3g9fpmecfDk5KTe/Txr2OOxdIiLF/4I0g4MMib3DKtr6iGgme2J+l8H8cA==", "license": "MIT", + "dependencies": { + "@sentry/core": "10.37.0" + }, "engines": { - "node": ">=20" + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", + "@opentelemetry/semantic-conventions": "^1.39.0" } }, - "node_modules/@speed-highlight/core": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz", - "integrity": "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -3578,16 +2243,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mute-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", - "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mysql": { "version": "2.15.27", "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", @@ -3635,13 +2290,6 @@ "@types/node": "*" } }, - "node_modules/@types/wrap-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", - "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -4225,22 +2873,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -4348,18 +2980,6 @@ "node": ">=0.6" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/body-parser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", @@ -4430,31 +3050,6 @@ "node": ">=8" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -4563,13 +3158,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" - }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -4595,56 +3183,12 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC", - "optional": true - }, "node_modules/cjs-module-lexer": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "license": "MIT" }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -4717,16 +3261,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4869,23 +3403,6 @@ } } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -4896,17 +3413,6 @@ "node": ">=6" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4914,19 +3420,6 @@ "dev": true, "license": "MIT" }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4936,16 +3429,6 @@ "node": ">= 0.8" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, "node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", @@ -4999,26 +3482,6 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/error-stack-parser-es": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", - "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -5394,116 +3857,6 @@ "node": ">=20.0.0" } }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/execa/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/execa/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/execa/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/execa/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "license": "(MIT OR WTFPL)", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -5571,36 +3924,8 @@ "funding": { "url": "https://github.com/sponsors/express-rate-limit" }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/external-editor/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "express": ">= 4.11" } }, "node_modules/fast-deep-equal": { @@ -5693,32 +4018,6 @@ "dev": true, "license": "MIT" }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5812,27 +4111,6 @@ "dev": true, "license": "ISC" }, - "node_modules/flexsearch": { - "version": "0.7.43", - "resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.43.tgz", - "integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/flora-colossus": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", - "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -5874,29 +4152,6 @@ "node": ">= 0.8" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -5921,21 +4176,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/galactus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", - "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "flora-colossus": "^2.0.0", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -5945,19 +4185,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -5995,19 +4222,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/get-tsconfig": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", @@ -6021,14 +4235,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -6114,13 +4320,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -6153,349 +4352,122 @@ "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hono": { - "version": "4.11.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", - "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", - "license": "MIT", - "engines": { - "node": ">=16.9.0" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC", - "optional": true - }, - "node_modules/inquirer": { - "version": "8.2.7", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", - "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/external-editor": "^1.0.0", - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer-autocomplete-prompt": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-2.0.1.tgz", - "integrity": "sha512-jUHrH0btO7j5r8DTQgANf2CBkTZChoVySD8zF/wp5fZCOLIuUbleXhf4ZY5jNBOc1owA3gdfWtfZuppfYBhcUg==", - "dev": true, - "license": "ISC", - "dependencies": { - "ansi-escapes": "^4.3.2", - "figures": "^3.2.0", - "picocolors": "^1.0.0", - "run-async": "^2.4.1", - "rxjs": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "inquirer": "^8.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "node_modules/inquirer/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, + "node_modules/hono": { + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.9.0" } }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/inquirer/node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true, - "license": "ISC" - }, - "node_modules/inquirer/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=10" + "node": ">=0.10.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/inquirer/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", + "node_modules/import-in-the-middle": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", + "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", + "license": "Apache-2.0", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/ip-address": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", @@ -6514,22 +4486,6 @@ "node": ">= 0.10" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -6562,19 +4518,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -6591,29 +4534,6 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -6756,39 +4676,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keytar": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", - "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-addon-api": "^4.3.0", - "prebuild-install": "^7.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6799,16 +4686,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6869,13 +4746,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6890,49 +4760,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/loupe": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", @@ -7059,81 +4886,12 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/miniflare": { - "version": "4.20260120.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260120.0.tgz", - "integrity": "sha512-XXZyE2pDKMtP5OLuv0LPHEAzIYhov4jrYjcqrhhqtxGGtXneWOHvXIPo+eV8sqwqWd3R7j4DlEKcyb+87BR49Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "0.8.1", - "sharp": "^0.34.5", - "undici": "7.18.2", - "workerd": "1.20260120.0", - "ws": "8.18.0", - "youch": "4.1.0-beta.10", - "zod": "^3.25.76" - }, - "bin": { - "miniflare": "bootstrap.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/miniflare/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/minimatch": { @@ -7149,16 +4907,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -7169,14 +4917,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/mlly": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", @@ -7212,16 +4952,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -7253,14 +4983,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7277,68 +4999,6 @@ "node": ">= 0.6" } }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-abi": { - "version": "3.87.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", - "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", - "dev": true, - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7381,22 +5041,6 @@ "wrappy": "1" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -7415,137 +5059,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/ora/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -7626,13 +5139,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -7936,34 +5442,6 @@ "node": ">=0.10.0" } }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8003,19 +5481,6 @@ "node": ">=6.0.0" } }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -8044,17 +5509,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8125,49 +5579,6 @@ "node": ">= 0.10" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", @@ -8181,18 +5592,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8224,27 +5623,6 @@ "node": ">=9.3.0 || >=8.10.0 <9.0.0" } }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -8252,39 +5630,18 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "license": "ISC" + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } }, "node_modules/reusify": { "version": "1.1.0", @@ -8353,16 +5710,6 @@ "node": ">= 18" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8387,16 +5734,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -8479,51 +5816,6 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8545,42 +5837,6 @@ "node": ">=8" } }, - "node_modules/shelljs": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.9.2.tgz", - "integrity": "sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "execa": "^1.0.0", - "fast-glob": "^3.3.2", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/shx": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.4.0.tgz", - "integrity": "sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.8", - "shelljs": "^0.9.2" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -8673,55 +5929,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/simple-plist": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", @@ -8838,19 +6045,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stream-buffers": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", @@ -8861,16 +6055,6 @@ "node": ">= 0.10.0" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -8975,16 +6159,6 @@ "node": ">=8" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -9047,19 +6221,6 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/synckit": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", @@ -9076,38 +6237,6 @@ "url": "https://opencollective.com/synckit" } }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/test-exclude": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", @@ -9172,13 +6301,6 @@ "node": ">=0.8" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -9271,19 +6393,6 @@ "node": ">=14.0.0" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9390,13 +6499,6 @@ } } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/tsup": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", @@ -9525,20 +6627,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9552,19 +6640,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -9624,32 +6699,12 @@ "dev": true, "license": "MIT" }, - "node_modules/undici": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz", - "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -9669,13 +6724,6 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", @@ -9689,16 +6737,6 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/uuidv7": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/uuidv7/-/uuidv7-1.1.0.tgz", - "integrity": "sha512-2VNnOC0+XQlwogChUDzy6pe8GQEys9QFZBGOh54l6qVfwoCUwwRvk7rDTgaIsRgsF5GFa5oiNg8LqXE3jofBBg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "uuidv7": "cli.js" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -9945,16 +6983,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9997,27 +7025,6 @@ "node": ">=0.10.0" } }, - "node_modules/workerd": { - "version": "1.20260120.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260120.0.tgz", - "integrity": "sha512-R6X/VQOkwLTBGLp4VRUwLQZZVxZ9T9J8pGiJ6GQUMaRkY7TVWrCSkVfoNMM1/YyFsY5UYhhPoQe5IehnhZ3Pdw==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "bin": { - "workerd": "bin/workerd" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260120.0", - "@cloudflare/workerd-darwin-arm64": "1.20260120.0", - "@cloudflare/workerd-linux-64": "1.20260120.0", - "@cloudflare/workerd-linux-arm64": "1.20260120.0", - "@cloudflare/workerd-windows-64": "1.20260120.0" - } - }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -10119,28 +7126,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/xcode": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", @@ -10299,58 +7284,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/youch": { - "version": "4.1.0-beta.10", - "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", - "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@poppinss/colors": "^4.1.5", - "@poppinss/dumper": "^0.6.4", - "@speed-highlight/core": "^1.2.7", - "cookie": "^1.0.2", - "youch-core": "^0.3.3" - } - }, - "node_modules/youch-core": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", - "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@poppinss/exception": "^1.2.2", - "error-stack-parser-es": "^1.0.5" - } - }, - "node_modules/youch/node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/zod": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.2.tgz", diff --git a/package.json b/package.json index 95193bf1..50f48a88 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "iOSTemplateVersion": "v1.0.8", "macOSTemplateVersion": "v1.0.5", "type": "module", - "module": "src/smithery.ts", "exports": { ".": "./build/cli.js", "./package.json": "./package.json" @@ -15,8 +14,8 @@ "xcodebuildmcp-doctor": "build/doctor-cli.js" }, "scripts": { - "build": "npm run build:tsup && npx smithery build --transport stdio", - "dev": "npm run generate:version && npx smithery dev", + "build": "npm run build:tsup", + "dev": "npm run build:tsup && node build/cli.js mcp", "build:tsup": "npm run generate:version && tsup", "dev:tsup": "npm run build:tsup && tsup --watch", "prepare": "node scripts/install-git-hooks.js", @@ -33,7 +32,6 @@ "format:check": "prettier --check 'src/**/*.{js,ts}'", "typecheck": "npx tsc --noEmit && npx tsc -p tsconfig.test.json", "typecheck:tests": "npx tsc -p tsconfig.test.json", - "verify:smithery-bundle": "bash scripts/verify-smithery-bundle.sh", "inspect": "npx @modelcontextprotocol/inspector node build/cli.js mcp", "doctor": "node build/doctor-cli.js", "docs:update": "npx tsx scripts/update-tools-docs.ts", @@ -86,7 +84,6 @@ "@bacons/xcode": "^1.0.0-alpha.24", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.23.0", - "@smithery/cli": "^3.7.0", "@types/chokidar": "^1.7.5", "@types/node": "^22.13.6", "@types/yargs": "^17.0.33", diff --git a/scripts/verify-smithery-bundle.sh b/scripts/verify-smithery-bundle.sh deleted file mode 100755 index 91f70483..00000000 --- a/scripts/verify-smithery-bundle.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -SMITHERY_DIR="$PROJECT_ROOT/.smithery" -STDIO_DIR="$SMITHERY_DIR/stdio" -BUNDLE_DIR="$STDIO_DIR/bundled" - -if [ ! -f "$BUNDLE_DIR/axe" ]; then - echo "Missing bundled AXe artifacts under $STDIO_DIR" - if [ -d "$SMITHERY_DIR" ]; then - echo ".smithery contents:" - ls -la "$SMITHERY_DIR" - fi - exit 1 -fi -AXE_BIN="$BUNDLE_DIR/axe" -FRAMEWORK_DIR="$BUNDLE_DIR/Frameworks" - -if [ ! -f "$AXE_BIN" ]; then - echo "Missing AXe binary at $AXE_BIN" - if [ -d "$PROJECT_ROOT/.smithery" ]; then - echo ".smithery contents:" - ls -la "$PROJECT_ROOT/.smithery" - fi - exit 1 -fi - -if [ ! -d "$FRAMEWORK_DIR" ]; then - echo "Missing Frameworks directory at $FRAMEWORK_DIR" - if [ -d "$BUNDLE_DIR" ]; then - echo "bundled contents:" - ls -la "$BUNDLE_DIR" - fi - exit 1 -fi - -FRAMEWORK_COUNT="$(find "$FRAMEWORK_DIR" -maxdepth 2 -type d -name "*.framework" | wc -l | tr -d ' ')" -if [ "$FRAMEWORK_COUNT" -eq 0 ]; then - echo "No frameworks found in $FRAMEWORK_DIR" - find "$FRAMEWORK_DIR" -maxdepth 2 -type d | head -n 50 - exit 1 -fi - -echo "Smithery bundle includes AXe binary and $FRAMEWORK_COUNT frameworks" diff --git a/smithery.yaml b/smithery.yaml deleted file mode 100644 index 03671534..00000000 --- a/smithery.yaml +++ /dev/null @@ -1,5 +0,0 @@ -runtime: typescript -target: local -build: - assets: - - "bundled/**" diff --git a/src/mcp/tools/ui-automation/__tests__/button.test.ts b/src/mcp/tools/ui-automation/__tests__/button.test.ts index 62f89079..08305d4a 100644 --- a/src/mcp/tools/ui-automation/__tests__/button.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/button.test.ts @@ -323,7 +323,7 @@ describe('Button Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -343,7 +343,7 @@ describe('Button Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/gesture.test.ts b/src/mcp/tools/ui-automation/__tests__/gesture.test.ts index 29ff4cb5..7cfaccd6 100644 --- a/src/mcp/tools/ui-automation/__tests__/gesture.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/gesture.test.ts @@ -342,7 +342,7 @@ describe('Gesture Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -362,7 +362,7 @@ describe('Gesture Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/key_press.test.ts b/src/mcp/tools/ui-automation/__tests__/key_press.test.ts index aaec8033..30586239 100644 --- a/src/mcp/tools/ui-automation/__tests__/key_press.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/key_press.test.ts @@ -86,7 +86,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -130,7 +130,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -177,7 +177,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -221,7 +221,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -265,7 +265,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -301,7 +301,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -332,7 +332,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -352,7 +352,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -373,7 +373,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -412,7 +412,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -446,7 +446,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -480,7 +480,7 @@ describe('Key Press Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/key_sequence.test.ts b/src/mcp/tools/ui-automation/__tests__/key_sequence.test.ts index 1483e616..20e6798b 100644 --- a/src/mcp/tools/ui-automation/__tests__/key_sequence.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/key_sequence.test.ts @@ -86,7 +86,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -131,7 +131,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -179,7 +179,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -224,7 +224,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -277,7 +277,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -316,7 +316,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -346,7 +346,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -366,7 +366,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -387,7 +387,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -426,7 +426,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -460,7 +460,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -494,7 +494,7 @@ describe('Key Sequence Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/long_press.test.ts b/src/mcp/tools/ui-automation/__tests__/long_press.test.ts index dc07be0b..a1c2fb25 100644 --- a/src/mcp/tools/ui-automation/__tests__/long_press.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/long_press.test.ts @@ -343,7 +343,7 @@ describe('Long Press Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -365,7 +365,7 @@ describe('Long Press Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts b/src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts index 7f8d096e..f8dca02e 100644 --- a/src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/snapshot_ui.test.ts @@ -133,7 +133,7 @@ describe('Snapshot UI Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -152,7 +152,7 @@ describe('Snapshot UI Plugin', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/swipe.test.ts b/src/mcp/tools/ui-automation/__tests__/swipe.test.ts index 8a6c9543..5a388947 100644 --- a/src/mcp/tools/ui-automation/__tests__/swipe.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/swipe.test.ts @@ -19,7 +19,7 @@ function createMockAxeHelpers(): AxeHelpers { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -36,7 +36,7 @@ function createMockAxeHelpersWithNullPath(): AxeHelpers { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -423,7 +423,7 @@ describe('Swipe Tool', () => { content: [ { type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/tap.test.ts b/src/mcp/tools/ui-automation/__tests__/tap.test.ts index a5338132..24c6c714 100644 --- a/src/mcp/tools/ui-automation/__tests__/tap.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/tap.test.ts @@ -18,7 +18,7 @@ function createMockAxeHelpers(): AxeHelpers { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -35,7 +35,7 @@ function createMockAxeHelpersWithNullPath(): AxeHelpers { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -803,7 +803,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -833,7 +833,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -863,7 +863,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -891,7 +891,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -919,7 +919,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -947,7 +947,7 @@ describe('Tap Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/touch.test.ts b/src/mcp/tools/ui-automation/__tests__/touch.test.ts index fa24971a..fa955e37 100644 --- a/src/mcp/tools/ui-automation/__tests__/touch.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/touch.test.ts @@ -124,7 +124,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -174,7 +174,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -224,7 +224,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -276,7 +276,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -367,7 +367,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -389,7 +389,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -405,7 +405,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -443,7 +443,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -504,7 +504,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -547,7 +547,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -590,7 +590,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -630,7 +630,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -652,7 +652,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -673,7 +673,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -714,7 +714,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -757,7 +757,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -800,7 +800,7 @@ describe('Touch Plugin', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/mcp/tools/ui-automation/__tests__/type_text.test.ts b/src/mcp/tools/ui-automation/__tests__/type_text.test.ts index adb24e9c..10940956 100644 --- a/src/mcp/tools/ui-automation/__tests__/type_text.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/type_text.test.ts @@ -27,7 +27,7 @@ function createMockAxeHelpers( content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -306,7 +306,7 @@ describe('Type Text Tool', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, @@ -384,7 +384,7 @@ describe('Type Text Tool', () => { content: [ { type: 'text', - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nIf you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', }, ], isError: true, diff --git a/src/smithery.ts b/src/smithery.ts deleted file mode 100644 index 824e942c..00000000 --- a/src/smithery.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { z } from 'zod'; -import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { bootstrapServer } from './server/bootstrap.ts'; -import { createServer } from './server/server.ts'; -import { log } from './utils/logger.ts'; -import { initSentry } from './utils/sentry.ts'; - -// Empty config schema - all configuration comes from config.yaml and env vars -export const configSchema = z.object({}); - -export type SmitheryConfig = z.infer; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export default function createSmitheryServer(options: { config: SmitheryConfig }): McpServer { - const server = createServer(); - const bootstrapPromise: Promise = (async (): Promise => { - initSentry(); - await bootstrapServer(server); - })().catch((error) => { - log( - 'error', - `Failed to bootstrap Smithery server: ${error instanceof Error ? error.message : String(error)}`, - ); - throw error; - }); - - const handler: ProxyHandler = { - get(target, prop, receiver): unknown { - if (prop === 'connect') { - return async (...args: unknown[]): Promise => { - await bootstrapPromise; - const connect = target.connect.bind(target) as (...connectArgs: unknown[]) => unknown; - return connect(...args); - }; - } - return Reflect.get(target, prop, receiver) as unknown; - }, - }; - - return new Proxy(server, handler); -} diff --git a/src/utils/axe-helpers.ts b/src/utils/axe-helpers.ts index 8cb0d78c..b091b620 100644 --- a/src/utils/axe-helpers.ts +++ b/src/utils/axe-helpers.ts @@ -121,7 +121,7 @@ export function createAxeNotAvailableResponse(): ToolResponse { return createTextResponse( 'AXe tool not found. UI automation features are not available.\n\n' + 'Install AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\n' + - 'If you installed via Smithery, ensure bundled artifacts are included or PATH is configured.', + 'Ensure bundled artifacts are included or PATH is configured.', true, ); } diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts index e9888e7d..8c7af75b 100644 --- a/src/utils/sentry.ts +++ b/src/utils/sentry.ts @@ -2,7 +2,7 @@ * Sentry instrumentation for XcodeBuildMCP * * This file initializes Sentry when explicitly called to avoid side effects - * during module import (needed for Smithery's module-based entry). + * during module import. */ import * as Sentry from '@sentry/node'; From c537138681fe872afb2cc73bf6dd45b2597ea629 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 11:19:52 +0000 Subject: [PATCH 26/28] ci: Remove stale Smithery check from CI workflow Remove the CI step that runs the deleted verify:smithery-bundle script\nand rename the build step label to match current packaging flow.\n\nInclude the .gitignore update with this CI cleanup to keep the branch\nstate consistent. Co-Authored-By: Claude --- .github/workflows/ci.yml | 5 +---- .gitignore | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 856ad130..26dc83b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,15 +29,12 @@ jobs: - name: Bundle AXe artifacts run: npm run bundle:axe - - name: Build (Smithery) + - name: Build run: npm run build - name: Validate docs CLI command references run: npm run docs:check - - name: Verify Smithery bundle - run: npm run verify:smithery-bundle - - name: Lint run: npm run lint diff --git a/.gitignore b/.gitignore index ce712e26..ba1b4eb4 100644 --- a/.gitignore +++ b/.gitignore @@ -100,7 +100,6 @@ buildServer.json # Bundled AXe artifacts (generated during build) bundled/ -.smithery/ /.mcpregistry_github_token /.mcpregistry_registry_token From c9bd012856037469acd1204ec25940592e92eb53 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 11:42:54 +0000 Subject: [PATCH 27/28] fix(release): Address PR review findings for portable packaging Align npm tag resolution between dry-run and production publish, enforce\nrelease asset ordering before Homebrew tap updates, and harden portable\npackaging scripts with checksum verification and safer argument parsing.\n\nAlso improve docs command fence parsing, memoize resource-root resolution\nwith test reset hooks, and reduce duplicated AXe helper fixtures in key\npress UI automation tests. Co-Authored-By: Claude --- .github/workflows/release.yml | 42 +++--- docs/dev/PROJECT_CONFIG_PLAN.md | 2 +- scripts/check-docs-cli-commands.js | 2 +- scripts/create-homebrew-formula.sh | 25 ++- scripts/package-macos-portable.sh | 98 ++++++++---- scripts/verify-portable-install.sh | 18 +-- src/core/__tests__/resource-root.test.ts | 3 + src/core/resource-root.ts | 29 +++- .../ui-automation/__tests__/key_press.test.ts | 142 +++--------------- src/utils/__tests__/axe-helpers.test.ts | 7 +- src/utils/axe-helpers.ts | 2 +- 11 files changed, 179 insertions(+), 191 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96a78a2b..58b9542f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,6 +74,22 @@ jobs: # For tag-based releases, package.json was already updated by release script fi + - name: Resolve npm tag from version + id: resolve_npm_tag + run: | + VERSION="${{ steps.get_version.outputs.VERSION }}" + if [[ "$VERSION" == *"-beta"* ]]; then + NPM_TAG="beta" + elif [[ "$VERSION" == *"-alpha"* ]]; then + NPM_TAG="alpha" + elif [[ "$VERSION" == *"-rc"* ]]; then + NPM_TAG="rc" + else + NPM_TAG="latest" + fi + echo "NPM_TAG=$NPM_TAG" >> "$GITHUB_OUTPUT" + echo "Resolved npm tag: $NPM_TAG" + - name: Generate GitHub release notes (production releases only) if: github.event_name == 'push' run: | @@ -88,13 +104,7 @@ jobs: if: github.event_name == 'workflow_dispatch' run: | echo "๐Ÿงช Testing package creation (dry run)" - VERSION="${{ steps.get_version.outputs.VERSION }}" - if [[ "$VERSION" == *"-"* ]]; then - NPM_TAG="next" - else - NPM_TAG="latest" - fi - npm publish --dry-run --access public --tag "$NPM_TAG" + npm publish --dry-run --access public --tag "${{ steps.resolve_npm_tag.outputs.NPM_TAG }}" - name: Publish to NPM (production releases only) if: github.event_name == 'push' @@ -105,17 +115,7 @@ jobs: echo "โœ… xcodebuildmcp@$VERSION already on NPM. Skipping publish." exit 0 fi - # Determine the appropriate npm tag based on version - if [[ "$VERSION" == *"-beta"* ]]; then - NPM_TAG="beta" - elif [[ "$VERSION" == *"-alpha"* ]]; then - NPM_TAG="alpha" - elif [[ "$VERSION" == *"-rc"* ]]; then - NPM_TAG="rc" - else - # For stable releases, explicitly use latest tag - NPM_TAG="latest" - fi + NPM_TAG="${{ steps.resolve_npm_tag.outputs.NPM_TAG }}" echo "๐Ÿ“ฆ Publishing to NPM with tag: $NPM_TAG" npm publish --access public --tag "$NPM_TAG" @@ -314,7 +314,7 @@ jobs: publish_portable_assets: if: github.event_name == 'push' - needs: [release, build_universal_and_verify] + needs: [release, build_and_package_macos, build_universal_and_verify] runs-on: ubuntu-latest steps: - name: Download arm64 artifact @@ -350,7 +350,7 @@ jobs: update_homebrew_tap: if: github.event_name == 'push' - needs: [release, build_and_package_macos] + needs: [release, build_and_package_macos, publish_portable_assets] runs-on: ubuntu-latest env: HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} @@ -400,7 +400,7 @@ jobs: run: | VERSION="${{ needs.release.outputs.version }}" DEFAULT_BRANCH="main" - git clone "https://x-access-token:${GH_TOKEN}@github.com/cameroncooke/homebrew-xcodebuildmcp.git" tap-repo + gh repo clone cameroncooke/homebrew-xcodebuildmcp tap-repo mkdir -p tap-repo/Formula cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb cd tap-repo diff --git a/docs/dev/PROJECT_CONFIG_PLAN.md b/docs/dev/PROJECT_CONFIG_PLAN.md index 7db8577e..66327422 100644 --- a/docs/dev/PROJECT_CONFIG_PLAN.md +++ b/docs/dev/PROJECT_CONFIG_PLAN.md @@ -119,7 +119,7 @@ Responsibilities: ### 7) Runtime overrides **File:** runtime entrypoints -- Pass overrides into bootstrap/config store, so explicit runtime overrides have highest precedence. +- Pass overrides into bootstrap/config store, so explicit runtime overrides have the highest precedence. ### 8) Documentation updates - Update `docs/CONFIGURATION.md`, `docs/GETTING_STARTED.md`, `docs/SESSION_DEFAULTS.md`. diff --git a/scripts/check-docs-cli-commands.js b/scripts/check-docs-cli-commands.js index 49e117e7..3498c831 100755 --- a/scripts/check-docs-cli-commands.js +++ b/scripts/check-docs-cli-commands.js @@ -85,7 +85,7 @@ function extractCommandCandidates(content) { const lines = content.split(/\r?\n/u); const candidates = []; const inlineCodeRegex = /`([^`\n]+)`/g; - const fenceHeaderRegex = /^\s*```([a-z0-9_-]*)\s*$/iu; + const fenceHeaderRegex = /^\s*(?:```|~~~)([a-z0-9_-]*)\s*$/iu; const codeFenceLanguages = new Set(['', 'bash', 'sh', 'zsh', 'shell', 'console']); let inFence = false; diff --git a/scripts/create-homebrew-formula.sh b/scripts/create-homebrew-formula.sh index f72c3427..5d7a007d 100755 --- a/scripts/create-homebrew-formula.sh +++ b/scripts/create-homebrew-formula.sh @@ -15,26 +15,41 @@ Usage: EOF } +require_arg_value() { + local flag_name="$1" + local value="${2:-}" + if [[ -z "$value" || "$value" == -* ]]; then + echo "Error: $flag_name requires a value" + usage + exit 1 + fi +} + while [[ $# -gt 0 ]]; do case "$1" in --version) - VERSION="${2:-}" + require_arg_value "--version" "${2:-}" + VERSION="$2" shift 2 ;; --arm64-sha) - ARM64_SHA="${2:-}" + require_arg_value "--arm64-sha" "${2:-}" + ARM64_SHA="$2" shift 2 ;; --x64-sha) - X64_SHA="${2:-}" + require_arg_value "--x64-sha" "${2:-}" + X64_SHA="$2" shift 2 ;; --base-url) - BASE_URL="${2:-}" + require_arg_value "--base-url" "${2:-}" + BASE_URL="$2" shift 2 ;; --out) - OUT_PATH="${2:-}" + require_arg_value "--out" "${2:-}" + OUT_PATH="$2" shift 2 ;; -h|--help) diff --git a/scripts/package-macos-portable.sh b/scripts/package-macos-portable.sh index 42fb5d2d..ee9f332f 100755 --- a/scripts/package-macos-portable.sh +++ b/scripts/package-macos-portable.sh @@ -25,10 +25,21 @@ Notes: EOF } +require_arg_value() { + local flag_name="$1" + local value="${2:-}" + if [[ -z "$value" || "$value" == -* ]]; then + echo "Missing value for $flag_name" + usage + exit 1 + fi +} + while [[ $# -gt 0 ]]; do case "$1" in --arch) - ARCH="${2:-}" + require_arg_value "--arch" "${2:-}" + ARCH="$2" shift 2 ;; --universal) @@ -36,19 +47,23 @@ while [[ $# -gt 0 ]]; do shift ;; --arm64-root) - ARM64_ROOT="${2:-}" + require_arg_value "--arm64-root" "${2:-}" + ARM64_ROOT="$2" shift 2 ;; --x64-root) - X64_ROOT="${2:-}" + require_arg_value "--x64-root" "${2:-}" + X64_ROOT="$2" shift 2 ;; --dist-dir) - DIST_DIR="${2:-}" + require_arg_value "--dist-dir" "${2:-}" + DIST_DIR="$2" shift 2 ;; --version) - VERSION="${2:-}" + require_arg_value "--version" "${2:-}" + VERSION="$2" shift 2 ;; -h|--help) @@ -153,23 +168,47 @@ install_node_runtime_for_arch() { esac local archive_name="node-v${node_version}-darwin-${node_arch}.tar.gz" + local checksums_name="SHASUMS256.txt" local download_url="https://nodejs.org/dist/v${node_version}/${archive_name}" + local checksums_url="https://nodejs.org/dist/v${node_version}/${checksums_name}" local temp_dir temp_dir="$(mktemp -d)" + cleanup_temp_dir() { + if [[ -d "$temp_dir" ]]; then + rm -r "$temp_dir" + fi + } + trap cleanup_temp_dir RETURN curl -fLsS "$download_url" -o "$temp_dir/$archive_name" + curl -fLsS "$checksums_url" -o "$temp_dir/$checksums_name" + + local expected_sha + expected_sha="$(awk -v target="$archive_name" '$2 == target {print $1}' "$temp_dir/$checksums_name")" + if [[ -z "$expected_sha" ]]; then + echo "Unable to find checksum for $archive_name in ${checksums_name}" + exit 1 + fi + + local actual_sha + actual_sha="$(shasum -a 256 "$temp_dir/$archive_name" | awk '{print $1}')" + if [[ "$actual_sha" != "$expected_sha" ]]; then + echo "Node runtime checksum mismatch for $archive_name" + echo "Expected: $expected_sha" + echo "Actual: $actual_sha" + exit 1 + fi + tar -xzf "$temp_dir/$archive_name" -C "$temp_dir" local extracted_node="$temp_dir/node-v${node_version}-darwin-${node_arch}/bin/node" if [[ ! -x "$extracted_node" ]]; then echo "Failed to locate extracted Node runtime at $extracted_node" - rm -r "$temp_dir" exit 1 fi cp "$extracted_node" "$output_path" chmod +x "$output_path" - rm -r "$temp_dir" } write_wrapper_scripts() { @@ -177,14 +216,7 @@ write_wrapper_scripts() { local bin_dir="$root/bin" local libexec_dir="$root/libexec" - cat > "$libexec_dir/xcodebuildmcp" <<'EOF' -#!/usr/bin/env bash -set -euo pipefail -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -exec "$ROOT/node-runtime" "$ROOT/build/cli.js" "$@" -EOF - - cat > "$bin_dir/xcodebuildmcp" <<'EOF' + cat > "$libexec_dir/_resolve-resource-root.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail SOURCE="${BASH_SOURCE[0]}" @@ -194,29 +226,39 @@ while [[ -L "$SOURCE" ]]; do [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" done SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" -RESOURCE_ROOT="$(cd "$SCRIPT_DIR/../libexec" && pwd)" +RESOURCE_ROOT="$(cd "$SCRIPT_DIR" && pwd)" export XCODEBUILDMCP_RESOURCE_ROOT="$RESOURCE_ROOT" export DYLD_FRAMEWORK_PATH="$RESOURCE_ROOT/bundled/Frameworks${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH}" +EOF + + cat > "$libexec_dir/xcodebuildmcp" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$ROOT/node-runtime" "$ROOT/build/cli.js" "$@" +EOF + + cat > "$bin_dir/xcodebuildmcp" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../libexec/_resolve-resource-root.sh" exec "$RESOURCE_ROOT/xcodebuildmcp" "$@" EOF cat > "$bin_dir/xcodebuildmcp-doctor" <<'EOF' #!/usr/bin/env bash set -euo pipefail -SOURCE="${BASH_SOURCE[0]}" -while [[ -L "$SOURCE" ]]; do - DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" - SOURCE="$(readlink "$SOURCE")" - [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" -done -SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" -RESOURCE_ROOT="$(cd "$SCRIPT_DIR/../libexec" && pwd)" -export XCODEBUILDMCP_RESOURCE_ROOT="$RESOURCE_ROOT" -export DYLD_FRAMEWORK_PATH="$RESOURCE_ROOT/bundled/Frameworks${DYLD_FRAMEWORK_PATH:+:$DYLD_FRAMEWORK_PATH}" +SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../libexec/_resolve-resource-root.sh" exec "$RESOURCE_ROOT/xcodebuildmcp" doctor "$@" EOF - chmod +x "$libexec_dir/xcodebuildmcp" "$bin_dir/xcodebuildmcp" "$bin_dir/xcodebuildmcp-doctor" + chmod +x \ + "$libexec_dir/_resolve-resource-root.sh" \ + "$libexec_dir/xcodebuildmcp" \ + "$bin_dir/xcodebuildmcp" \ + "$bin_dir/xcodebuildmcp-doctor" } create_tarball_and_checksum() { @@ -229,7 +271,7 @@ create_tarball_and_checksum() { cd "$(dirname "$portable_root")" tar -czf "$tarball_path" "$(basename "$portable_root")" ) - shasum -a 256 "$tarball_path" > "$checksum_path" + shasum -a 256 "$tarball_path" | awk '{print $1}' > "$checksum_path" echo "Created artifact: $tarball_path" echo "Created checksum: $checksum_path" } diff --git a/scripts/verify-portable-install.sh b/scripts/verify-portable-install.sh index 91c6510f..3bb97c78 100755 --- a/scripts/verify-portable-install.sh +++ b/scripts/verify-portable-install.sh @@ -36,6 +36,12 @@ while [[ $# -gt 0 ]]; do esac done +cleanup() { + if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then + rm -r "$TEMP_DIR" + fi +} + if [[ -z "$ARCHIVE_PATH" && -z "$PORTABLE_ROOT" ]]; then usage exit 1 @@ -43,6 +49,7 @@ fi if [[ -n "$ARCHIVE_PATH" ]]; then TEMP_DIR="$(mktemp -d)" + trap cleanup EXIT tar -xzf "$ARCHIVE_PATH" -C "$TEMP_DIR" extracted_count="$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" if [[ "$extracted_count" -ne 1 ]]; then @@ -52,13 +59,6 @@ if [[ -n "$ARCHIVE_PATH" ]]; then PORTABLE_ROOT="$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d | head -1)" fi -cleanup() { - if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then - rm -r "$TEMP_DIR" - fi -} -trap cleanup EXIT - if [[ ! -d "$PORTABLE_ROOT/bin" || ! -d "$PORTABLE_ROOT/libexec" ]]; then echo "Portable layout missing bin/ or libexec/: $PORTABLE_ROOT" exit 1 @@ -105,8 +105,8 @@ if [[ -z "$RUNTIME_ARCHS" ]]; then fi NORMALIZED_HOST_ARCH="$HOST_ARCH" -if [[ "$HOST_ARCH" == "x86_64" ]]; then - NORMALIZED_HOST_ARCH="x86_64" +if [[ "$HOST_ARCH" == "aarch64" ]]; then + NORMALIZED_HOST_ARCH="arm64" fi CAN_EXECUTE="false" diff --git a/src/core/__tests__/resource-root.test.ts b/src/core/__tests__/resource-root.test.ts index 439f9f40..167143d9 100644 --- a/src/core/__tests__/resource-root.test.ts +++ b/src/core/__tests__/resource-root.test.ts @@ -7,6 +7,7 @@ import { getBundledFrameworksDir, getManifestsDir, getResourceRoot, + resetResourceRootCacheForTests, } from '../resource-root.ts'; describe('resource-root', () => { @@ -18,6 +19,7 @@ describe('resource-root', () => { originalExecPath = process.execPath; originalResourceRoot = process.env.XCODEBUILDMCP_RESOURCE_ROOT; tempDir = mkdtempSync(join(tmpdir(), 'xbmcp-resource-root-')); + resetResourceRootCacheForTests(); }); afterEach(() => { @@ -28,6 +30,7 @@ describe('resource-root', () => { process.env.XCODEBUILDMCP_RESOURCE_ROOT = originalResourceRoot; } rmSync(tempDir, { recursive: true, force: true }); + resetResourceRootCacheForTests(); }); it('uses XCODEBUILDMCP_RESOURCE_ROOT when set', () => { diff --git a/src/core/resource-root.ts b/src/core/resource-root.ts index 80e3cb6f..7005a1ec 100644 --- a/src/core/resource-root.ts +++ b/src/core/resource-root.ts @@ -3,6 +3,13 @@ import * as path from 'node:path'; import { fileURLToPath } from 'node:url'; const RESOURCE_ROOT_ENV_VAR = 'XCODEBUILDMCP_RESOURCE_ROOT'; +let cachedPackageRoot: string | null = null; +let cachedResourceRoot: string | null = null; + +export function resetResourceRootCacheForTests(): void { + cachedPackageRoot = null; + cachedResourceRoot = null; +} function hasResourceLayout(root: string): boolean { return fs.existsSync(path.join(root, 'manifests')) || fs.existsSync(path.join(root, 'bundled')); @@ -20,6 +27,10 @@ function findPackageRootFrom(startDir: string): string | null { } export function getPackageRoot(): string { + if (cachedPackageRoot) { + return cachedPackageRoot; + } + const candidates: string[] = []; const importMetaUrl = typeof import.meta.url === 'string' ? import.meta.url : null; if (importMetaUrl) { @@ -34,6 +45,7 @@ export function getPackageRoot(): string { for (const candidate of candidates) { const found = findPackageRootFrom(candidate); if (found) { + cachedPackageRoot = found; return found; } } @@ -43,10 +55,6 @@ export function getPackageRoot(): string { function getExecutableResourceRoot(): string | null { const execPath = process.execPath; - if (!execPath) { - return null; - } - const candidateDirs = [path.dirname(execPath), path.dirname(path.dirname(execPath))]; for (const candidate of candidateDirs) { if (hasResourceLayout(candidate)) { @@ -58,17 +66,24 @@ function getExecutableResourceRoot(): string | null { } export function getResourceRoot(): string { + if (cachedResourceRoot) { + return cachedResourceRoot; + } + const explicitRoot = process.env[RESOURCE_ROOT_ENV_VAR]; if (explicitRoot) { - return path.resolve(explicitRoot); + cachedResourceRoot = path.resolve(explicitRoot); + return cachedResourceRoot; } const executableRoot = getExecutableResourceRoot(); if (executableRoot) { - return executableRoot; + cachedResourceRoot = executableRoot; + return cachedResourceRoot; } - return getPackageRoot(); + cachedResourceRoot = getPackageRoot(); + return cachedResourceRoot; } export function getManifestsDir(): string { diff --git a/src/mcp/tools/ui-automation/__tests__/key_press.test.ts b/src/mcp/tools/ui-automation/__tests__/key_press.test.ts index 30586239..8d2c551a 100644 --- a/src/mcp/tools/ui-automation/__tests__/key_press.test.ts +++ b/src/mcp/tools/ui-automation/__tests__/key_press.test.ts @@ -13,6 +13,22 @@ import { import { sessionStore } from '../../../../utils/session-store.ts'; import { schema, handler, key_pressLogic } from '../key_press.ts'; +function createDefaultMockAxeHelpers() { + return { + getAxePath: () => '/usr/local/bin/axe', + getBundledAxeEnvironment: () => ({}), + createAxeNotAvailableResponse: () => ({ + content: [ + { + type: 'text' as const, + text: 'AXe tool not found. UI automation features are not available.\\n\\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\\nEnsure bundled artifacts are included or PATH is configured.', + }, + ], + isError: true, + }), + }; +} + describe('Key Press Tool', () => { beforeEach(() => { sessionStore.clear(); @@ -79,19 +95,7 @@ describe('Key Press Tool', () => { }); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); await key_pressLogic( { @@ -123,19 +127,7 @@ describe('Key Press Tool', () => { }); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); await key_pressLogic( { @@ -170,19 +162,7 @@ describe('Key Press Tool', () => { }); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); await key_pressLogic( { @@ -258,19 +238,7 @@ describe('Key Press Tool', () => { error: '', }); - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -294,19 +262,7 @@ describe('Key Press Tool', () => { error: '', }); - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -366,19 +322,7 @@ describe('Key Press Tool', () => { error: 'axe command failed', }); - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -405,19 +349,7 @@ describe('Key Press Tool', () => { throw new Error('System error occurred'); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -439,19 +371,7 @@ describe('Key Press Tool', () => { throw new Error('Unexpected error'); }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { @@ -473,19 +393,7 @@ describe('Key Press Tool', () => { throw 'String error'; }; - const mockAxeHelpers = { - getAxePath: () => '/usr/local/bin/axe', - getBundledAxeEnvironment: () => ({}), - createAxeNotAvailableResponse: () => ({ - content: [ - { - type: 'text' as const, - text: 'AXe tool not found. UI automation features are not available.\n\nInstall AXe (brew tap cameroncooke/axe && brew install axe) or set XCODEBUILDMCP_AXE_PATH.\nEnsure bundled artifacts are included or PATH is configured.', - }, - ], - isError: true, - }), - }; + const mockAxeHelpers = createDefaultMockAxeHelpers(); const result = await key_pressLogic( { diff --git a/src/utils/__tests__/axe-helpers.test.ts b/src/utils/__tests__/axe-helpers.test.ts index 2c7dc1ee..468a48e4 100644 --- a/src/utils/__tests__/axe-helpers.test.ts +++ b/src/utils/__tests__/axe-helpers.test.ts @@ -1,8 +1,9 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { chmodSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { getBundledAxeEnvironment } from '../axe-helpers.ts'; +import { resetResourceRootCacheForTests } from '../../core/resource-root.ts'; describe('axe-helpers', () => { let originalResourceRoot: string | undefined; @@ -13,6 +14,7 @@ describe('axe-helpers', () => { originalResourceRoot = process.env.XCODEBUILDMCP_RESOURCE_ROOT; originalDyldFrameworkPath = process.env.DYLD_FRAMEWORK_PATH; tempDir = mkdtempSync(join(tmpdir(), 'xbmcp-axe-helpers-')); + resetResourceRootCacheForTests(); }); afterEach(() => { @@ -29,6 +31,7 @@ describe('axe-helpers', () => { } rmSync(tempDir, { recursive: true, force: true }); + resetResourceRootCacheForTests(); }); it('returns DYLD_FRAMEWORK_PATH when bundled axe is resolved from resource root', () => { @@ -37,6 +40,7 @@ describe('axe-helpers', () => { const frameworksDir = join(resourceRoot, 'bundled', 'Frameworks'); mkdirSync(frameworksDir, { recursive: true }); writeFileSync(axePath, ''); + chmodSync(axePath, 0o755); process.env.XCODEBUILDMCP_RESOURCE_ROOT = resourceRoot; delete process.env.DYLD_FRAMEWORK_PATH; @@ -52,6 +56,7 @@ describe('axe-helpers', () => { const frameworksDir = join(resourceRoot, 'bundled', 'Frameworks'); mkdirSync(frameworksDir, { recursive: true }); writeFileSync(axePath, ''); + chmodSync(axePath, 0o755); process.env.XCODEBUILDMCP_RESOURCE_ROOT = resourceRoot; process.env.DYLD_FRAMEWORK_PATH = '/existing/frameworks'; diff --git a/src/utils/axe-helpers.ts b/src/utils/axe-helpers.ts index b091b620..c9950f2d 100644 --- a/src/utils/axe-helpers.ts +++ b/src/utils/axe-helpers.ts @@ -43,7 +43,7 @@ function resolveBundledAxePath(): string | null { candidates.add(join(process.cwd(), 'bundled', 'axe')); for (const candidate of candidates) { - if (existsSync(candidate)) { + if (existsSync(candidate) && isExecutable(candidate)) { return candidate; } } From 5619bbd2128cbe3d03b9d8fc8b3d279e1cda0faa Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 10 Feb 2026 12:00:51 +0000 Subject: [PATCH 28/28] fix(portable): Resolve symlinked Homebrew bin launch paths Restore symlink resolution in generated bin wrappers before sourcing resource-root helpers. This preserves correct libexec lookup when xcodebuildmcp is invoked via /opt/homebrew/bin symlinks. Co-Authored-By: Claude --- scripts/package-macos-portable.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/package-macos-portable.sh b/scripts/package-macos-portable.sh index ee9f332f..7aebe4dd 100755 --- a/scripts/package-macos-portable.sh +++ b/scripts/package-macos-portable.sh @@ -241,7 +241,13 @@ EOF cat > "$bin_dir/xcodebuildmcp" <<'EOF' #!/usr/bin/env bash set -euo pipefail -SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE="${BASH_SOURCE[0]}" +while [[ -L "$SOURCE" ]]; do + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" source "$SCRIPT_DIR/../libexec/_resolve-resource-root.sh" exec "$RESOURCE_ROOT/xcodebuildmcp" "$@" EOF @@ -249,7 +255,13 @@ EOF cat > "$bin_dir/xcodebuildmcp-doctor" <<'EOF' #!/usr/bin/env bash set -euo pipefail -SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE="${BASH_SOURCE[0]}" +while [[ -L "$SOURCE" ]]; do + DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" + SOURCE="$(readlink "$SOURCE")" + [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" +done +SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)" source "$SCRIPT_DIR/../libexec/_resolve-resource-root.sh" exec "$RESOURCE_ROOT/xcodebuildmcp" doctor "$@" EOF