mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
Migrate to Microsoft node-pty v1.1.0-beta34 (#87)
This commit is contained in:
parent
5a4b939564
commit
c70330bcfd
17 changed files with 1033 additions and 866 deletions
148
.github/workflows/README.md
vendored
Normal file
148
.github/workflows/README.md
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# VibeTunnel CI/CD Workflows
|
||||
|
||||
This directory contains GitHub Actions workflows for continuous integration and testing.
|
||||
|
||||
## Workflows
|
||||
|
||||
### 1. Web CI (`web-ci.yml`)
|
||||
Basic CI workflow that runs on every push and PR affecting the web directory.
|
||||
|
||||
**Jobs:**
|
||||
- **Lint and Type Check**: Runs biome linting and TypeScript type checking
|
||||
- **Build**: Builds the project and uploads artifacts
|
||||
- **Test**: Runs the test suite
|
||||
|
||||
**Triggers:**
|
||||
- Push to `main` or `ms-pty` branches
|
||||
- Pull requests to `main`
|
||||
- Only when files in `web/` directory change
|
||||
|
||||
### 2. SEA Build Test (`sea-build-test.yml`)
|
||||
Advanced workflow for testing Single Executable Application (SEA) builds with custom Node.js.
|
||||
|
||||
**Features:**
|
||||
- Builds custom Node.js from source with optimizations
|
||||
- Uses Blacksmith runners for significantly faster builds
|
||||
- Caches custom Node.js builds for faster subsequent runs
|
||||
- Tests SEA builds with both system and custom Node.js
|
||||
- Supports manual triggers with custom Node.js versions
|
||||
|
||||
**Jobs:**
|
||||
1. **build-custom-node**:
|
||||
- Runs on `blacksmith-32vcpu-ubuntu-2404-arm` for fast compilation
|
||||
- Builds minimal Node.js without npm, intl, inspector, etc.
|
||||
- Uses Blacksmith cache for persistence
|
||||
- Outputs the custom Node.js path for downstream jobs
|
||||
|
||||
2. **test-sea-build**:
|
||||
- Runs on `blacksmith-8vcpu-ubuntu-2404-arm`
|
||||
- Matrix build testing both system and custom Node.js
|
||||
- Builds SEA executable with node-pty patches
|
||||
- Performs smoke tests on the generated executable
|
||||
- Uploads artifacts for inspection
|
||||
|
||||
3. **test-github-runners**:
|
||||
- Uses standard `ubuntu-latest` runners for comparison
|
||||
- Helps identify any Blacksmith-specific issues
|
||||
- Runs only on push events
|
||||
|
||||
### 3. Xcode SEA Test (`xcode-sea-test.yml`)
|
||||
Tests the macOS Xcode build with custom Node.js to ensure the VibeTunnel.app works correctly with SEA executables.
|
||||
|
||||
**Features:**
|
||||
- Builds custom Node.js on macOS using self-hosted runners
|
||||
- Tests integration of SEA executable into macOS app bundle
|
||||
- Verifies the app launches and contains the correct binaries
|
||||
- Supports manual triggers with custom Node.js versions
|
||||
|
||||
**Jobs:**
|
||||
1. **build-custom-node-mac**:
|
||||
- Runs on self-hosted macOS runner
|
||||
- Builds custom Node.js for macOS
|
||||
- Uses GitHub Actions cache (appropriate for self-hosted)
|
||||
- Outputs node path and size information
|
||||
|
||||
2. **test-xcode-build**:
|
||||
- Builds SEA executable with custom Node.js
|
||||
- Copies SEA and native modules to app resources
|
||||
- Builds VibeTunnel.app using Xcode
|
||||
- Verifies SEA executable is correctly bundled
|
||||
- Tests basic app functionality
|
||||
- Uploads built app as artifact
|
||||
|
||||
## Runner Strategy
|
||||
|
||||
### Blacksmith Runners (Linux)
|
||||
- **Custom Node.js Build**: `blacksmith-32vcpu-ubuntu-2404-arm` (high CPU for compilation)
|
||||
- **Other CI Jobs**: `blacksmith-8vcpu-ubuntu-2404-arm` (standard workloads)
|
||||
- Benefits: Significantly faster builds, better caching, ARM64 architecture
|
||||
|
||||
### Self-Hosted Runners (macOS)
|
||||
- Used for Xcode builds and macOS-specific testing
|
||||
- Access to Xcode and macOS-specific tools
|
||||
- Can test code signing and notarization
|
||||
|
||||
### GitHub Runners (Comparison)
|
||||
- `ubuntu-latest` used in test job for baseline comparison
|
||||
- Helps identify Blacksmith-specific issues
|
||||
|
||||
## Caching Strategy
|
||||
|
||||
### Blacksmith Cache
|
||||
**IMPORTANT**: When using Blacksmith runners, you MUST use `useblacksmith/cache@v1`
|
||||
- Used for all jobs running on Blacksmith runners
|
||||
- Provides faster cache operations
|
||||
- Better persistence than GitHub Actions cache
|
||||
- Cache key: `custom-node-linux-x64-v{version}-{hash}`
|
||||
|
||||
### GitHub Actions Cache
|
||||
**Only used for self-hosted runners and standard GitHub runners**
|
||||
- Self-hosted macOS runners use `actions/cache@v4`
|
||||
- Standard GitHub runners use `actions/cache@v4`
|
||||
- Cache key format same as Blacksmith
|
||||
|
||||
### Cache Performance
|
||||
- Initial custom Node.js build: ~10-15 minutes on 32vCPU
|
||||
- Cached builds: ~1 minute
|
||||
- Blacksmith cache restoration: 2-3x faster than GitHub Actions cache
|
||||
|
||||
## Manual Triggers
|
||||
|
||||
The SEA build workflow supports manual triggers via GitHub UI:
|
||||
```yaml
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
node_version:
|
||||
description: 'Node.js version to build'
|
||||
default: '24.2.0'
|
||||
```
|
||||
|
||||
## Local Testing
|
||||
|
||||
To test the SEA build locally:
|
||||
```bash
|
||||
# Build custom Node.js
|
||||
cd web
|
||||
node build-custom-node.js
|
||||
|
||||
# Build SEA with custom Node.js
|
||||
node build-native.js --custom-node=".node-builds/node-v24.2.0-minimal/out/Release/node"
|
||||
```
|
||||
|
||||
## Optimization Details
|
||||
|
||||
The custom Node.js build removes:
|
||||
- International support (`--without-intl`)
|
||||
- npm and corepack (`--without-npm --without-corepack`)
|
||||
- Inspector/debugging (`--without-inspector`)
|
||||
- Code cache and snapshots
|
||||
- Uses `-Os` optimization for size
|
||||
|
||||
This reduces the Node.js binary from ~120MB to ~50-60MB.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
- [ ] Add Windows and macOS to the build matrix
|
||||
- [ ] Implement release workflow for automated releases
|
||||
- [ ] Add performance benchmarks
|
||||
- [ ] Integrate with release signing process
|
||||
36
.github/workflows/claude-code-review.yml
vendored
36
.github/workflows/claude-code-review.yml
vendored
|
|
@ -148,38 +148,4 @@ jobs:
|
|||
HEAD_BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
CHANGED_FILES: ${{ github.event.pull_request.changed_files }}
|
||||
ADDITIONS: ${{ github.event.pull_request.additions }}
|
||||
DELETIONS: ${{ github.event.pull_request.deletions }}
|
||||
|
||||
# Optional: Post a summary comment if Claude's review is very long
|
||||
- name: Create summary if needed
|
||||
if: steps.check-review.outputs.skip != 'true' && always()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// Wait a bit for Claude's comment to appear
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
// Find Claude's latest comment
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
per_page: 10,
|
||||
sort: 'created',
|
||||
direction: 'desc'
|
||||
});
|
||||
|
||||
const claudeComment = comments.data.find(c => c.user.login === 'claude[bot]');
|
||||
|
||||
if (claudeComment && claudeComment.body.length > 10000) {
|
||||
// If the review is very long, add a summary at the top
|
||||
const summary = `## 📊 Review Summary\n\n**Review length**: ${claudeComment.body.length} characters\n**Commit**: ${context.payload.pull_request.head.sha.substring(0, 7)}\n\n> 💡 Tip: Use the table of contents below to navigate this review.\n\n---\n\n`;
|
||||
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: claudeComment.id,
|
||||
body: summary + claudeComment.body
|
||||
});
|
||||
}
|
||||
DELETIONS: ${{ github.event.pull_request.deletions }}
|
||||
234
.github/workflows/sea-build-test.yml
vendored
Normal file
234
.github/workflows/sea-build-test.yml
vendored
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
name: SEA Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/sea-build-test.yml'
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/sea-build-test.yml'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
node_version:
|
||||
description: 'Node.js version to build'
|
||||
required: false
|
||||
default: '24.2.0'
|
||||
type: string
|
||||
|
||||
env:
|
||||
NODE_VERSION: ${{ github.event.inputs.node_version || '24.2.0' }}
|
||||
CUSTOM_NODE_CACHE_KEY: custom-node-linux-x64
|
||||
|
||||
jobs:
|
||||
build-custom-node:
|
||||
name: Build Custom Node.js
|
||||
# DISABLED: Custom Node.js compilation temporarily disabled
|
||||
if: false
|
||||
runs-on: blacksmith-32vcpu-ubuntu-2404-arm
|
||||
outputs:
|
||||
cache-hit: ${{ steps.cache-custom-node.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
python3 \
|
||||
ninja-build \
|
||||
ccache \
|
||||
libpam0g-dev
|
||||
|
||||
- name: Cache custom Node.js build (Blacksmith)
|
||||
id: cache-custom-node
|
||||
uses: useblacksmith/cache@v1
|
||||
with:
|
||||
path: |
|
||||
web/.node-builds/node-v${{ env.NODE_VERSION }}-minimal
|
||||
key: ${{ env.CUSTOM_NODE_CACHE_KEY }}-v${{ env.NODE_VERSION }}-${{ hashFiles('web/build-custom-node.js') }}
|
||||
restore-keys: |
|
||||
${{ env.CUSTOM_NODE_CACHE_KEY }}-v${{ env.NODE_VERSION }}-
|
||||
|
||||
- name: Build custom Node.js
|
||||
if: steps.cache-custom-node.outputs.cache-hit != 'true'
|
||||
working-directory: web
|
||||
run: |
|
||||
node build-custom-node.js --version=${{ env.NODE_VERSION }}
|
||||
|
||||
test-sea-build:
|
||||
name: Test SEA Build
|
||||
# DISABLED: Removed dependency on build-custom-node since it's disabled
|
||||
# needs: build-custom-node
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404-arm
|
||||
strategy:
|
||||
matrix:
|
||||
# DISABLED: Only testing with system Node.js, custom disabled
|
||||
node-type: [system] # was: [system, custom]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.12.1
|
||||
|
||||
- name: Setup Node.js (system)
|
||||
if: matrix.node-type == 'system'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'web/pnpm-lock.yaml'
|
||||
|
||||
- name: Restore custom Node.js from cache (Blacksmith)
|
||||
if: matrix.node-type == 'custom'
|
||||
id: restore-custom-node
|
||||
uses: useblacksmith/cache@v1
|
||||
with:
|
||||
path: |
|
||||
web/.node-builds/node-v${{ env.NODE_VERSION }}-minimal
|
||||
key: ${{ env.CUSTOM_NODE_CACHE_KEY }}-v${{ env.NODE_VERSION }}-${{ hashFiles('web/build-custom-node.js') }}
|
||||
restore-keys: |
|
||||
${{ env.CUSTOM_NODE_CACHE_KEY }}-v${{ env.NODE_VERSION }}-
|
||||
|
||||
- name: Build custom Node.js if not cached
|
||||
# DISABLED: Custom Node.js compilation temporarily disabled
|
||||
if: false && matrix.node-type == 'custom' && steps.restore-custom-node.outputs.cache-hit != 'true'
|
||||
working-directory: web
|
||||
run: |
|
||||
echo "Custom Node.js not found in cache, building..."
|
||||
node build-custom-node.js --version=${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libpam0g-dev
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: web
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build SEA executable (system Node.js)
|
||||
if: matrix.node-type == 'system'
|
||||
working-directory: web
|
||||
run: |
|
||||
echo "Building SEA with system Node.js..."
|
||||
node --version
|
||||
node build-native.js
|
||||
|
||||
- name: Build SEA executable (custom Node.js)
|
||||
# DISABLED: Custom Node.js test temporarily disabled
|
||||
if: false && matrix.node-type == 'custom'
|
||||
working-directory: web
|
||||
run: |
|
||||
# Use auto-discovery since we know the custom Node.js is in .node-builds
|
||||
echo "Building SEA with custom Node.js (auto-discovery)..."
|
||||
node build-native.js --custom-node
|
||||
|
||||
- name: Test SEA executable
|
||||
working-directory: web
|
||||
run: |
|
||||
echo "Testing SEA executable..."
|
||||
./native/vibetunnel --version || true
|
||||
|
||||
# Basic smoke test - check if it starts
|
||||
timeout 5s ./native/vibetunnel --help || true
|
||||
|
||||
# Check binary size
|
||||
ls -lh native/
|
||||
size_mb=$(du -m native/vibetunnel | cut -f1)
|
||||
echo "SEA executable size: ${size_mb} MB"
|
||||
|
||||
# Ensure native modules are present
|
||||
test -f native/pty.node || (echo "ERROR: pty.node not found" && exit 1)
|
||||
test -f native/authenticate_pam.node || (echo "ERROR: authenticate_pam.node not found" && exit 1)
|
||||
# spawn-helper is only needed on macOS
|
||||
if [[ "$RUNNER_OS" == "macOS" ]]; then
|
||||
test -f native/spawn-helper || (echo "ERROR: spawn-helper not found" && exit 1)
|
||||
fi
|
||||
|
||||
- name: Upload SEA artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sea-build-${{ matrix.node-type }}-linux
|
||||
path: |
|
||||
web/native/
|
||||
retention-days: 7
|
||||
|
||||
# Test on standard GitHub runners for comparison
|
||||
test-github-runners:
|
||||
name: Test on GitHub Runners
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
python3 \
|
||||
ninja-build \
|
||||
ccache \
|
||||
libpam0g-dev
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.12.1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'web/pnpm-lock.yaml'
|
||||
|
||||
- name: Cache custom Node.js build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
web/.node-builds/node-v${{ env.NODE_VERSION }}-minimal
|
||||
key: blacksmith-${{ env.CUSTOM_NODE_CACHE_KEY }}-v${{ env.NODE_VERSION }}-${{ hashFiles('web/build-custom-node.js') }}
|
||||
restore-keys: |
|
||||
blacksmith-${{ env.CUSTOM_NODE_CACHE_KEY }}-v${{ env.NODE_VERSION }}-
|
||||
|
||||
- name: Build and test everything
|
||||
working-directory: web
|
||||
run: |
|
||||
# Install dependencies
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# Build custom Node.js if not cached
|
||||
# DISABLED: Custom Node.js compilation temporarily disabled
|
||||
# if [ ! -f ".node-builds/node-v${NODE_VERSION}-minimal/out/Release/node" ]; then
|
||||
# node build-custom-node.js --version=${NODE_VERSION}
|
||||
# fi
|
||||
|
||||
# Test both builds
|
||||
echo "=== Testing with system Node.js ==="
|
||||
node build-native.js
|
||||
./native/vibetunnel --version || true
|
||||
|
||||
# DISABLED: Custom Node.js test temporarily disabled
|
||||
# echo "=== Testing with custom Node.js ==="
|
||||
# CUSTOM_NODE=".node-builds/node-v${NODE_VERSION}-minimal/out/Release/node"
|
||||
# node build-native.js --custom-node="${CUSTOM_NODE}"
|
||||
# ./native/vibetunnel --version || true
|
||||
|
||||
- name: Compare sizes
|
||||
working-directory: web
|
||||
run: |
|
||||
echo "Binary sizes comparison:"
|
||||
ls -lh native/vibetunnel
|
||||
echo "System Node.js based: $(du -h native/vibetunnel | cut -f1)"
|
||||
131
.github/workflows/web-ci.yml
vendored
Normal file
131
.github/workflows/web-ci.yml
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
name: Web CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, ms-pty ]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/web-ci.yml'
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/web-ci.yml'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
|
||||
jobs:
|
||||
lint-and-type-check:
|
||||
name: Lint and Type Check
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404-arm
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.12.1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'web/pnpm-lock.yaml'
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libpam0g-dev
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run linting
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Run type checking
|
||||
run: pnpm run typecheck
|
||||
|
||||
- name: Check formatting
|
||||
run: pnpm run format:check
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404-arm
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.12.1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'web/pnpm-lock.yaml'
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libpam0g-dev
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: web-build
|
||||
path: |
|
||||
web/dist/
|
||||
web/public/
|
||||
retention-days: 7
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: blacksmith-8vcpu-ubuntu-2404-arm
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.12.1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'web/pnpm-lock.yaml'
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libpam0g-dev
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm run test:ci
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: coverage-report
|
||||
path: web/coverage/
|
||||
retention-days: 7
|
||||
|
|
@ -31,6 +31,19 @@ By building a custom Node.js without these features, we achieve a significantly
|
|||
- Uses the custom Node.js to create a smaller executable
|
||||
- Build output shows version and size comparison
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Build Tools
|
||||
For optimal build performance, the following tools are required:
|
||||
- **Ninja**: Build system for faster compilation (significantly faster than Make)
|
||||
- **ccache**: Compiler cache to speed up rebuilds
|
||||
|
||||
#### Installation
|
||||
- **macOS**: `brew install ninja ccache`
|
||||
- **Linux**: `apt-get install ninja-build ccache` (or equivalent for your distribution)
|
||||
|
||||
The build script will automatically use these tools if available, falling back to Make if Ninja is not found.
|
||||
|
||||
## Build Automation
|
||||
|
||||
### Release Builds
|
||||
|
|
|
|||
|
|
@ -13,24 +13,7 @@ struct DockIconManagerTests {
|
|||
let instance2 = DockIconManager.shared
|
||||
#expect(instance1 === instance2)
|
||||
}
|
||||
|
||||
@Test("User preference for dock icon")
|
||||
func userPreferenceForDockIcon() {
|
||||
// Store current value to restore later
|
||||
let currentValue = UserDefaults.standard.bool(forKey: "showInDock")
|
||||
|
||||
// Test with dock icon enabled
|
||||
UserDefaults.standard.set(true, forKey: "showInDock")
|
||||
#expect(UserDefaults.standard.bool(forKey: "showInDock") == true)
|
||||
|
||||
// Test with dock icon disabled
|
||||
UserDefaults.standard.set(false, forKey: "showInDock")
|
||||
#expect(UserDefaults.standard.bool(forKey: "showInDock") == false)
|
||||
|
||||
// Restore original value
|
||||
UserDefaults.standard.set(currentValue, forKey: "showInDock")
|
||||
}
|
||||
|
||||
@Test("Update dock visibility based on windows")
|
||||
@MainActor
|
||||
func updateDockVisibilityBasedOnWindows() {
|
||||
|
|
@ -108,4 +91,4 @@ struct DockIconManagerTests {
|
|||
// Restore
|
||||
UserDefaults.standard.set(originalPref, forKey: "showInDock")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,18 @@
|
|||
/**
|
||||
* Build a custom Node.js binary with reduced size by excluding features we don't need.
|
||||
*
|
||||
* See custom-node-build-flags.md for detailed documentation and size optimization results.
|
||||
* This script automatically adapts to CI and local environments.
|
||||
*
|
||||
* Quick usage:
|
||||
* node build-custom-node.js # Builds Node.js 24.2.0 (recommended)
|
||||
* node build-custom-node.js --latest # Latest version
|
||||
* node build-custom-node.js --version=24.2.0 # Specific version
|
||||
* Usage:
|
||||
* node build-custom-node.js # Builds Node.js 24.2.0 (recommended)
|
||||
* node build-custom-node.js --latest # Latest version
|
||||
* node build-custom-node.js --version=24.2.0 # Specific version
|
||||
* NODE_VERSION=24.2.0 node build-custom-node.js # Via environment variable (CI)
|
||||
*
|
||||
* In CI environments:
|
||||
* - Outputs GitHub Actions variables
|
||||
* - Uses ccache if available
|
||||
* - Creates build summary files
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
|
|
@ -16,6 +22,9 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
// Detect if running in CI
|
||||
const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
let targetVersion = null;
|
||||
|
|
@ -29,6 +38,13 @@ for (const arg of args) {
|
|||
}
|
||||
}
|
||||
|
||||
// Helper for GitHub Actions output
|
||||
function setOutput(name, value) {
|
||||
if (process.env.GITHUB_OUTPUT) {
|
||||
fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to download files
|
||||
function downloadFile(url, destPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
@ -70,7 +86,7 @@ async function getLatestNodeVersion() {
|
|||
}
|
||||
|
||||
async function buildCustomNode() {
|
||||
// Determine version to build
|
||||
// Determine version to build (CLI args take precedence over env vars)
|
||||
let nodeSourceVersion;
|
||||
if (useLatest) {
|
||||
console.log('Fetching latest Node.js version...');
|
||||
|
|
@ -78,18 +94,25 @@ async function buildCustomNode() {
|
|||
console.log(`Latest Node.js version: ${nodeSourceVersion}`);
|
||||
} else if (targetVersion) {
|
||||
nodeSourceVersion = targetVersion;
|
||||
} else if (process.env.NODE_VERSION) {
|
||||
// Support CI environment variable
|
||||
nodeSourceVersion = process.env.NODE_VERSION;
|
||||
} else {
|
||||
// Default to Node.js 24.2.0 (recommended version)
|
||||
nodeSourceVersion = '24.2.0';
|
||||
}
|
||||
|
||||
console.log(`Building custom Node.js ${nodeSourceVersion} with all feature removals (-Os)...`);
|
||||
const platform = process.platform;
|
||||
const arch = process.arch;
|
||||
|
||||
console.log(`Building custom Node.js ${nodeSourceVersion} for ${platform}-${arch}...`);
|
||||
console.log('This will take 10-20 minutes on first run, but will be cached for future builds.');
|
||||
|
||||
const nodeSourceUrl = `https://nodejs.org/dist/v${nodeSourceVersion}/node-v${nodeSourceVersion}.tar.gz`;
|
||||
const majorVersion = nodeSourceVersion.split('.')[0];
|
||||
|
||||
const buildDir = path.join(__dirname, '.node-builds');
|
||||
// In CI scripts directory, go up one level to find web root
|
||||
const buildDir = path.join(__dirname, __dirname.endsWith('scripts') ? '..' : '.', '.node-builds');
|
||||
const versionDir = path.join(buildDir, `node-v${nodeSourceVersion}-minimal`);
|
||||
const markerFile = path.join(versionDir, '.build-complete');
|
||||
const customNodePath = path.join(versionDir, 'out', 'Release', 'node');
|
||||
|
|
@ -99,8 +122,16 @@ async function buildCustomNode() {
|
|||
console.log(`Using cached custom Node.js build from ${customNodePath}`);
|
||||
const stats = fs.statSync(customNodePath);
|
||||
console.log(`Cached custom Node.js size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
|
||||
console.log(`\nTo use this custom Node.js with build-native.js:`);
|
||||
console.log(`node build-native.js --custom-node="${customNodePath}"`);
|
||||
|
||||
if (isCI) {
|
||||
// Set outputs for GitHub Actions
|
||||
setOutput('node-path', customNodePath);
|
||||
setOutput('node-size', stats.size);
|
||||
setOutput('cache-hit', 'true');
|
||||
} else {
|
||||
console.log(`\nTo use this custom Node.js with build-native.js:`);
|
||||
console.log(`node build-native.js --custom-node="${customNodePath}"`);
|
||||
}
|
||||
return customNodePath;
|
||||
}
|
||||
|
||||
|
|
@ -109,8 +140,8 @@ async function buildCustomNode() {
|
|||
fs.mkdirSync(buildDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Clean up old version directory if exists
|
||||
if (fs.existsSync(versionDir)) {
|
||||
// Clean up incomplete builds (check for marker file)
|
||||
if (fs.existsSync(versionDir) && !fs.existsSync(markerFile)) {
|
||||
console.log('Cleaning up incomplete build...');
|
||||
fs.rmSync(versionDir, { recursive: true, force: true });
|
||||
}
|
||||
|
|
@ -131,12 +162,14 @@ async function buildCustomNode() {
|
|||
|
||||
// Rename to version-specific directory
|
||||
const extractedDir = path.join(buildDir, `node-v${nodeSourceVersion}`);
|
||||
fs.renameSync(extractedDir, versionDir);
|
||||
if (fs.existsSync(extractedDir)) {
|
||||
fs.renameSync(extractedDir, versionDir);
|
||||
}
|
||||
|
||||
// Configure and build
|
||||
process.chdir(versionDir);
|
||||
|
||||
console.log('Configuring Node.js build (all feature removals, -Os only)...');
|
||||
console.log('Configuring Node.js build...');
|
||||
const configureArgs = [
|
||||
'--without-intl', // Remove internationalization support
|
||||
'--without-npm', // Don't include npm
|
||||
|
|
@ -144,42 +177,49 @@ async function buildCustomNode() {
|
|||
'--without-inspector', // Remove debugging/profiling features
|
||||
'--without-node-code-cache', // Disable code cache
|
||||
'--without-node-snapshot', // Don't create/use startup snapshot
|
||||
'--ninja', // Use ninja if available for faster builds
|
||||
];
|
||||
|
||||
// Check if ninja is available
|
||||
try {
|
||||
execSync('which ninja', { stdio: 'ignore' });
|
||||
configureArgs.push('--ninja');
|
||||
console.log('Using Ninja for faster builds...');
|
||||
} catch {
|
||||
console.log('Ninja not found, using Make...');
|
||||
}
|
||||
|
||||
// Enable ccache if available
|
||||
try {
|
||||
execSync('which ccache', { stdio: 'ignore' });
|
||||
process.env.CC = 'ccache gcc';
|
||||
process.env.CXX = 'ccache g++';
|
||||
console.log('Using ccache for faster rebuilds...');
|
||||
} catch {
|
||||
console.log('ccache not found, proceeding without it...');
|
||||
}
|
||||
|
||||
// Use -Os optimization which is proven to be safe
|
||||
process.env.CFLAGS = '-Os';
|
||||
process.env.CXXFLAGS = '-Os';
|
||||
// Clear LDFLAGS to avoid any issues
|
||||
delete process.env.LDFLAGS;
|
||||
|
||||
// Check if ninja is available, install if not
|
||||
try {
|
||||
execSync('which ninja', { stdio: 'ignore' });
|
||||
console.log('Using Ninja for faster builds...');
|
||||
} catch {
|
||||
console.log('Ninja not found, installing via Homebrew...');
|
||||
try {
|
||||
execSync('brew install ninja', { stdio: 'inherit' });
|
||||
console.log('Ninja installed successfully');
|
||||
} catch (brewError) {
|
||||
console.log('Failed to install ninja, falling back to Make...');
|
||||
// Remove --ninja if not available
|
||||
configureArgs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
execSync(`./configure ${configureArgs.join(' ')}`, { stdio: 'inherit' });
|
||||
|
||||
console.log('Building Node.js (this will take a while)...');
|
||||
const cores = require('os').cpus().length;
|
||||
const startTime = Date.now();
|
||||
|
||||
// Check if we're using ninja or make
|
||||
const buildSystem = configureArgs.includes('--ninja') ? 'ninja' : 'make';
|
||||
if (buildSystem === 'ninja') {
|
||||
execSync(`ninja -C out/Release -j ${cores}`, { stdio: 'inherit' });
|
||||
} else {
|
||||
execSync(`make -j${cores}`, { stdio: 'inherit' });
|
||||
const buildCmd = configureArgs.includes('--ninja')
|
||||
? `ninja -C out/Release -j ${cores}`
|
||||
: `make -j${cores}`;
|
||||
|
||||
execSync(buildCmd, { stdio: 'inherit' });
|
||||
|
||||
const buildTime = Math.round((Date.now() - startTime) / 1000);
|
||||
if (isCI) {
|
||||
console.log(`Build completed in ${buildTime} seconds`);
|
||||
}
|
||||
|
||||
// Verify the build
|
||||
|
|
@ -187,9 +227,14 @@ async function buildCustomNode() {
|
|||
throw new Error('Node.js build failed - binary not found');
|
||||
}
|
||||
|
||||
// Strip the binary
|
||||
// Test the binary
|
||||
const version = execSync(`"${customNodePath}" --version`, { encoding: 'utf8' }).trim();
|
||||
console.log(`Built Node.js version: ${version}`);
|
||||
|
||||
// Strip the binary (different command for Linux vs macOS)
|
||||
console.log('Stripping Node.js binary...');
|
||||
execSync(`strip -S "${customNodePath}"`, { stdio: 'inherit' });
|
||||
const stripCmd = platform === 'darwin' ? 'strip -S' : 'strip -s';
|
||||
execSync(`${stripCmd} "${customNodePath}"`, { stdio: 'inherit' });
|
||||
|
||||
// Check final size
|
||||
const stats = fs.statSync(customNodePath);
|
||||
|
|
@ -206,31 +251,71 @@ async function buildCustomNode() {
|
|||
}
|
||||
|
||||
// Mark build as complete
|
||||
fs.writeFileSync(markerFile, JSON.stringify({
|
||||
const buildInfo = {
|
||||
version: nodeSourceVersion,
|
||||
buildDate: new Date().toISOString(),
|
||||
size: stats.size,
|
||||
configureArgs: configureArgs
|
||||
}, null, 2));
|
||||
platform: platform,
|
||||
arch: arch,
|
||||
configureArgs: configureArgs,
|
||||
buildTime: buildTime
|
||||
};
|
||||
|
||||
fs.writeFileSync(markerFile, JSON.stringify(buildInfo, null, 2));
|
||||
|
||||
// Create a summary file
|
||||
const summaryPath = path.join(versionDir, 'build-summary.txt');
|
||||
const summary = `
|
||||
Custom Node.js Build Summary
|
||||
============================
|
||||
Version: ${nodeSourceVersion}
|
||||
Platform: ${platform}-${arch}
|
||||
Size: ${(stats.size / 1024 / 1024).toFixed(2)} MB
|
||||
Build Time: ${buildTime} seconds
|
||||
Configure Args: ${configureArgs.join(' ')}
|
||||
Path: ${customNodePath}
|
||||
`;
|
||||
fs.writeFileSync(summaryPath, summary);
|
||||
|
||||
// Change back to original directory
|
||||
process.chdir(originalCwd);
|
||||
|
||||
if (isCI) {
|
||||
// Set outputs for GitHub Actions
|
||||
setOutput('node-path', customNodePath);
|
||||
setOutput('node-size', stats.size);
|
||||
setOutput('node-version', version);
|
||||
setOutput('build-time', buildTime);
|
||||
setOutput('cache-hit', 'false');
|
||||
}
|
||||
|
||||
// Output for both CI and local use
|
||||
console.log(`\nCustom Node.js location: ${customNodePath}`);
|
||||
console.log(`\nTo use this custom Node.js with build-native.js:`);
|
||||
console.log(`To use this custom Node.js with build-native.js:`);
|
||||
console.log(`node build-native.js --custom-node="${customNodePath}"`);
|
||||
|
||||
return customNodePath;
|
||||
|
||||
} catch (error) {
|
||||
process.chdir(originalCwd);
|
||||
console.error('Failed to build custom Node.js:', error.message);
|
||||
console.error('Failed to build custom Node.js:', error.message || error);
|
||||
|
||||
// Set error output for CI
|
||||
if (isCI) {
|
||||
setOutput('build-error', error.message || 'Unknown error');
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the build
|
||||
buildCustomNode().catch(err => {
|
||||
console.error('Build failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
// Run the build if called directly
|
||||
if (require.main === module) {
|
||||
buildCustomNode().catch(err => {
|
||||
console.error('Build failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
// Export for use as a module
|
||||
module.exports = { buildCustomNode };
|
||||
|
|
@ -13,51 +13,14 @@
|
|||
* - `pty.node` - Native binding for terminal emulation
|
||||
* - `spawn-helper` - Helper binary for spawning processes (Unix only)
|
||||
*
|
||||
* ## How it works
|
||||
*
|
||||
* 1. **Patches node-pty** to work with SEA's limitations:
|
||||
* - SEA's require() can only load built-in Node.js modules, not external files
|
||||
* - We patch node-pty to use `process.dlopen()` instead of `require()` for native modules
|
||||
* - All file lookups are changed to look next to the executable, not in node_modules
|
||||
*
|
||||
* 2. **Bundles TypeScript** using esbuild:
|
||||
* - Compiles and bundles all TypeScript/JavaScript into a single file
|
||||
* - Includes inline sourcemaps for better debugging
|
||||
* - Source map support can be enabled with --sourcemap flag
|
||||
*
|
||||
* 3. **Creates SEA blob**:
|
||||
* - Uses Node.js's experimental SEA config to generate a blob from the bundle
|
||||
* - The blob contains all the JavaScript code and can be injected into a Node binary
|
||||
*
|
||||
* 4. **Injects into Node.js binary**:
|
||||
* - Copies the Node.js executable and injects the SEA blob using postject
|
||||
* - Signs the binary on macOS to avoid security warnings
|
||||
*
|
||||
* ## Portability
|
||||
* The resulting executable is fully portable:
|
||||
* - No absolute paths are embedded
|
||||
* - Native modules are loaded relative to the executable location
|
||||
* - Can be moved to any directory or machine with the same OS/architecture
|
||||
*
|
||||
* ## Usage
|
||||
* ```bash
|
||||
* node build-native.js # Build with system Node.js
|
||||
* node build-native.js --sourcemap # Build with inline sourcemaps
|
||||
* node build-native.js --custom-node=/path/to/node # Use custom Node.js binary
|
||||
*
|
||||
* # Build custom Node.js first:
|
||||
* node build-custom-node.js # Build minimal Node.js for current version
|
||||
* node build-custom-node.js --version=24.2.0 # Build specific version
|
||||
* node build-native.js --custom-node # Auto-discover custom Node.js (uses most recent)
|
||||
* node build-native.js --custom-node=/path/to/node # Use specific custom Node.js binary
|
||||
* node build-native.js --custom-node /path/to/node # Alternative syntax
|
||||
* ```
|
||||
*
|
||||
* ## Requirements
|
||||
* - Node.js 20+ (for SEA support)
|
||||
* - postject (installed automatically if needed)
|
||||
*
|
||||
* ## Known Limitations
|
||||
* - The SEA warning about require() limitations is expected and harmless
|
||||
* - Native modules must be distributed alongside the executable
|
||||
* - Cross-platform builds are not supported (build on the target platform)
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
|
|
@ -74,31 +37,12 @@ for (let i = 0; i < process.argv.length; i++) {
|
|||
if (arg.startsWith('--custom-node=')) {
|
||||
customNodePath = arg.split('=')[1];
|
||||
} else if (arg === '--custom-node') {
|
||||
// Check if next argument is a path
|
||||
if (i + 1 < process.argv.length && !process.argv[i + 1].startsWith('--')) {
|
||||
// Next argument is the path
|
||||
customNodePath = process.argv[i + 1];
|
||||
} else {
|
||||
// No path provided, search for custom Node.js build
|
||||
console.log('Searching for custom Node.js build...');
|
||||
const customBuildsDir = path.join(__dirname, '.node-builds');
|
||||
if (fs.existsSync(customBuildsDir)) {
|
||||
const dirs = fs.readdirSync(customBuildsDir)
|
||||
.filter(dir => dir.startsWith('node-v') && dir.endsWith('-minimal'))
|
||||
.map(dir => ({
|
||||
name: dir,
|
||||
path: path.join(customBuildsDir, dir, 'out/Release/node'),
|
||||
mtime: fs.statSync(path.join(customBuildsDir, dir)).mtime
|
||||
}))
|
||||
.filter(item => fs.existsSync(item.path))
|
||||
.sort((a, b) => b.mtime - a.mtime); // Sort by modification time, newest first
|
||||
|
||||
if (dirs.length > 0) {
|
||||
customNodePath = dirs[0].path;
|
||||
console.log(`Found custom Node.js at: ${customNodePath}`);
|
||||
} else {
|
||||
console.log('No custom Node.js builds found in .node-builds/');
|
||||
}
|
||||
}
|
||||
// No path provided, use auto-discovery
|
||||
customNodePath = 'auto';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -116,340 +60,6 @@ if (nodeVersion < 20) {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
function patchNodePty() {
|
||||
console.log('Preparing node-pty for SEA build...');
|
||||
|
||||
// Always reinstall to ensure clean state
|
||||
console.log('Reinstalling node-pty to ensure clean state...');
|
||||
execSync('rm -rf node_modules/@homebridge/node-pty-prebuilt-multiarch', { stdio: 'inherit' });
|
||||
|
||||
// Suppress npm warnings during installation
|
||||
execSync('NODE_NO_WARNINGS=1 pnpm install @homebridge/node-pty-prebuilt-multiarch --silent', {
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_NO_WARNINGS: '1',
|
||||
npm_config_loglevel: 'error'
|
||||
}
|
||||
});
|
||||
|
||||
// Also ensure authenticate-pam is installed
|
||||
console.log('Ensuring authenticate-pam is installed...');
|
||||
execSync('pnpm install authenticate-pam --silent 2>/dev/null || pnpm install authenticate-pam --silent', {
|
||||
stdio: ['inherit', 'inherit', 'pipe'],
|
||||
shell: true
|
||||
});
|
||||
|
||||
// If using custom Node.js, rebuild native modules
|
||||
if (customNodePath) {
|
||||
console.log('Custom Node.js detected - rebuilding native modules...');
|
||||
|
||||
// Get versions
|
||||
const customVersion = execSync(`"${customNodePath}" --version`, { encoding: 'utf8' }).trim();
|
||||
const systemVersion = process.version;
|
||||
|
||||
console.log(`Custom Node.js: ${customVersion}`);
|
||||
console.log(`System Node.js: ${systemVersion}`);
|
||||
|
||||
// Rebuild node-pty with the custom Node using pnpm rebuild
|
||||
console.log('Rebuilding @homebridge/node-pty-prebuilt-multiarch with custom Node.js...');
|
||||
|
||||
try {
|
||||
// Use system Node to run pnpm, but rebuild for custom Node version
|
||||
// The key is to use system Node.js to run pnpm (which needs regex support),
|
||||
// but tell node-gyp to build against the custom Node.js headers
|
||||
console.log('Using system Node.js to run pnpm for compatibility...');
|
||||
|
||||
// First rebuild node-pty which is critical
|
||||
execSync(`pnpm rebuild @homebridge/node-pty-prebuilt-multiarch`, {
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_runtime: 'node',
|
||||
npm_config_target: customVersion.substring(1), // Remove 'v' prefix
|
||||
npm_config_arch: process.arch,
|
||||
npm_config_target_arch: process.arch,
|
||||
npm_config_disturl: 'https://nodejs.org/dist',
|
||||
npm_config_build_from_source: 'true',
|
||||
CXXFLAGS: '-std=c++20 -stdlib=libc++ -mmacosx-version-min=14.0',
|
||||
MACOSX_DEPLOYMENT_TARGET: '14.0'
|
||||
}
|
||||
});
|
||||
console.log('node-pty rebuilt successfully');
|
||||
|
||||
// Rebuild authenticate-pam (required for authentication)
|
||||
console.log('Rebuilding authenticate-pam...');
|
||||
|
||||
// Create a wrapper script to filter warnings
|
||||
const wrapperScript = `#!/bin/bash
|
||||
# Filter out specific warnings while preserving errors
|
||||
pnpm rebuild authenticate-pam "$@" 2>&1 | grep -v "cast from 'typename" | grep -v "converts to incompatible function type" | grep -v "expanded from macro" | grep -v "~~~" | grep -v "In file included from" | grep -v "warnings generated" | grep -v "In instantiation of" | grep -v "requested here" || true
|
||||
`;
|
||||
fs.writeFileSync('build-wrapper.sh', wrapperScript, { mode: 0o755 });
|
||||
|
||||
try {
|
||||
execSync(`./build-wrapper.sh`, {
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_runtime: 'node',
|
||||
npm_config_target: customVersion.substring(1),
|
||||
npm_config_arch: process.arch,
|
||||
npm_config_target_arch: process.arch,
|
||||
npm_config_disturl: 'https://nodejs.org/dist',
|
||||
npm_config_build_from_source: 'true',
|
||||
CXXFLAGS: '-std=c++20 -stdlib=libc++ -mmacosx-version-min=14.0 -Wno-cast-function-type -Wno-incompatible-function-pointer-types',
|
||||
MACOSX_DEPLOYMENT_TARGET: '14.0'
|
||||
}
|
||||
});
|
||||
console.log('authenticate-pam rebuilt successfully');
|
||||
} finally {
|
||||
// Clean up wrapper script
|
||||
if (fs.existsSync('build-wrapper.sh')) {
|
||||
fs.unlinkSync('build-wrapper.sh');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Native modules rebuilt successfully with custom Node.js');
|
||||
} catch (error) {
|
||||
console.error('Failed to rebuild native module:', error.message);
|
||||
console.error('Trying alternative rebuild method...');
|
||||
|
||||
// Alternative: Force reinstall and rebuild
|
||||
try {
|
||||
console.log('Forcing reinstall and rebuild...');
|
||||
execSync(`rm -rf node_modules/@homebridge/node-pty-prebuilt-multiarch`, { stdio: 'inherit' });
|
||||
execSync(`rm -rf node_modules/authenticate-pam`, { stdio: 'inherit' });
|
||||
|
||||
// First install the packages
|
||||
execSync(`pnpm install @homebridge/node-pty-prebuilt-multiarch authenticate-pam --force`, { stdio: 'inherit' });
|
||||
|
||||
// Then rebuild them with custom Node settings
|
||||
// Create a wrapper script to filter warnings
|
||||
const rebuildWrapperScript = `#!/bin/bash
|
||||
# Filter out specific warnings while preserving errors
|
||||
pnpm rebuild @homebridge/node-pty-prebuilt-multiarch authenticate-pam "$@" 2>&1 | grep -v "cast from 'typename" | grep -v "converts to incompatible function type" | grep -v "expanded from macro" | grep -v "~~~" | grep -v "In file included from" | grep -v "warnings generated" | grep -v "In instantiation of" | grep -v "requested here" || true
|
||||
`;
|
||||
fs.writeFileSync('rebuild-wrapper.sh', rebuildWrapperScript, { mode: 0o755 });
|
||||
|
||||
try {
|
||||
execSync(`./rebuild-wrapper.sh`, {
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_runtime: 'node',
|
||||
npm_config_target: customVersion.substring(1),
|
||||
npm_config_arch: process.arch,
|
||||
npm_config_target_arch: process.arch,
|
||||
npm_config_disturl: 'https://nodejs.org/dist',
|
||||
CXXFLAGS: '-std=c++20 -stdlib=libc++ -mmacosx-version-min=14.0 -Wno-cast-function-type -Wno-incompatible-function-pointer-types',
|
||||
MACOSX_DEPLOYMENT_TARGET: '14.0'
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
// Clean up wrapper script
|
||||
if (fs.existsSync('rebuild-wrapper.sh')) {
|
||||
fs.unlinkSync('rebuild-wrapper.sh');
|
||||
}
|
||||
}
|
||||
console.log('Native module rebuilt from source successfully');
|
||||
} catch (error2) {
|
||||
console.error('Alternative rebuild also failed:', error2.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Patching node-pty for SEA build...');
|
||||
|
||||
// Marker to detect if files have been patched
|
||||
const PATCH_MARKER = '/* VIBETUNNEL_SEA_PATCHED */';
|
||||
|
||||
// Helper function to check if file is already patched
|
||||
function isFilePatched(filePath) {
|
||||
if (!fs.existsSync(filePath)) return false;
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
return content.includes(PATCH_MARKER);
|
||||
}
|
||||
|
||||
// Patch prebuild-loader.js to use process.dlopen instead of require
|
||||
const prebuildLoaderFile = path.join(__dirname, 'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/prebuild-loader.js');
|
||||
const prebuildLoaderContent = `"use strict";
|
||||
${PATCH_MARKER}
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
// Custom loader for SEA that uses process.dlopen
|
||||
var pty;
|
||||
|
||||
// Helper function to load native module using dlopen
|
||||
function loadNativeModule(modulePath) {
|
||||
const module = { exports: {} };
|
||||
process.dlopen(module, modulePath);
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
// Determine the path to pty.node
|
||||
function getPtyPath() {
|
||||
const execDir = path.dirname(process.execPath);
|
||||
// Look for pty.node next to the executable first
|
||||
const ptyPath = path.join(execDir, 'pty.node');
|
||||
|
||||
if (fs.existsSync(ptyPath)) {
|
||||
return ptyPath;
|
||||
}
|
||||
|
||||
// If not found, throw error with helpful message
|
||||
throw new Error('Could not find pty.node next to executable at: ' + ptyPath);
|
||||
}
|
||||
|
||||
try {
|
||||
const ptyPath = getPtyPath();
|
||||
|
||||
// Set spawn-helper path for Unix systems
|
||||
if (process.platform !== 'win32') {
|
||||
const execDir = path.dirname(process.execPath);
|
||||
const spawnHelperPath = path.join(execDir, 'spawn-helper');
|
||||
if (fs.existsSync(spawnHelperPath)) {
|
||||
process.env.NODE_PTY_SPAWN_HELPER_PATH = spawnHelperPath;
|
||||
}
|
||||
}
|
||||
|
||||
pty = loadNativeModule(ptyPath);
|
||||
} catch (error) {
|
||||
console.error('Failed to load pty.node:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
exports.default = pty;
|
||||
//# sourceMappingURL=prebuild-loader.js.map`;
|
||||
|
||||
if (isFilePatched(prebuildLoaderFile)) {
|
||||
console.log('prebuild-loader.js is already patched, skipping...');
|
||||
} else {
|
||||
fs.writeFileSync(prebuildLoaderFile, prebuildLoaderContent.trimEnd() + '\n');
|
||||
console.log('Patched prebuild-loader.js');
|
||||
}
|
||||
|
||||
// Also patch windowsPtyAgent.js if it exists
|
||||
const windowsPtyAgentFile = path.join(__dirname, 'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/windowsPtyAgent.js');
|
||||
if (fs.existsSync(windowsPtyAgentFile)) {
|
||||
if (isFilePatched(windowsPtyAgentFile)) {
|
||||
console.log('windowsPtyAgent.js is already patched, skipping...');
|
||||
} else {
|
||||
let content = fs.readFileSync(windowsPtyAgentFile, 'utf8');
|
||||
// Add patch marker at the beginning
|
||||
content = `${PATCH_MARKER}\n` + content;
|
||||
// Replace direct require of .node files with our loader
|
||||
content = content.replace(
|
||||
/require\(['"]\.\.\/build\/Release\/pty\.node['"]\)/g,
|
||||
"require('./prebuild-loader').default"
|
||||
);
|
||||
fs.writeFileSync(windowsPtyAgentFile, content.trimEnd() + '\n');
|
||||
console.log('Patched windowsPtyAgent.js');
|
||||
}
|
||||
}
|
||||
|
||||
// Patch index.js exports.native line
|
||||
const indexFile = path.join(__dirname, 'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/index.js');
|
||||
if (fs.existsSync(indexFile)) {
|
||||
if (isFilePatched(indexFile)) {
|
||||
console.log('index.js is already patched, skipping...');
|
||||
} else {
|
||||
let content = fs.readFileSync(indexFile, 'utf8');
|
||||
// Add patch marker at the beginning
|
||||
content = `${PATCH_MARKER}\n` + content;
|
||||
// Replace the exports.native line that directly requires .node
|
||||
content = content.replace(
|
||||
/exports\.native = \(process\.platform !== 'win32' \? require\(prebuild_file_path_1\.ptyPath \|\| '\.\.\/build\/Release\/pty\.node'\) : null\);/,
|
||||
"exports.native = (process.platform !== 'win32' ? require('./prebuild-loader').default : null);"
|
||||
);
|
||||
fs.writeFileSync(indexFile, content.trimEnd() + '\n');
|
||||
console.log('Patched index.js');
|
||||
}
|
||||
}
|
||||
|
||||
// Patch unixTerminal.js to fix spawn-helper path resolution
|
||||
const unixTerminalFile = path.join(__dirname, 'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/unixTerminal.js');
|
||||
if (fs.existsSync(unixTerminalFile)) {
|
||||
if (isFilePatched(unixTerminalFile)) {
|
||||
console.log('unixTerminal.js is already patched, skipping...');
|
||||
} else {
|
||||
let content = fs.readFileSync(unixTerminalFile, 'utf8');
|
||||
// Add patch marker at the beginning
|
||||
content = `${PATCH_MARKER}\n` + content;
|
||||
|
||||
// Replace the helperPath resolution logic
|
||||
const helperPathPatch = `var helperPath;
|
||||
// For SEA, use spawn-helper from environment or next to executable
|
||||
if (process.env.NODE_PTY_SPAWN_HELPER_PATH) {
|
||||
helperPath = process.env.NODE_PTY_SPAWN_HELPER_PATH;
|
||||
} else {
|
||||
// In SEA context, look next to the executable
|
||||
const execDir = path.dirname(process.execPath);
|
||||
const spawnHelperPath = path.join(execDir, 'spawn-helper');
|
||||
if (require('fs').existsSync(spawnHelperPath)) {
|
||||
helperPath = spawnHelperPath;
|
||||
} else {
|
||||
// Fallback to original logic
|
||||
helperPath = '../build/Release/spawn-helper';
|
||||
helperPath = path.resolve(__dirname, helperPath);
|
||||
helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
|
||||
helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
|
||||
}
|
||||
}`;
|
||||
|
||||
// Find and replace the helperPath section
|
||||
content = content.replace(
|
||||
/var helperPath;[\s\S]*?helperPath = helperPath\.replace\('node_modules\.asar', 'node_modules\.asar\.unpacked'\);/m,
|
||||
helperPathPatch
|
||||
);
|
||||
|
||||
fs.writeFileSync(unixTerminalFile, content.trimEnd() + '\n');
|
||||
console.log('Patched unixTerminal.js');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('node-pty patching complete.');
|
||||
}
|
||||
|
||||
// Function to clean patches from node-pty
|
||||
function cleanPatches() {
|
||||
console.log('Cleaning patches from node-pty...');
|
||||
|
||||
const filesToClean = [
|
||||
'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/prebuild-loader.js',
|
||||
'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/windowsPtyAgent.js',
|
||||
'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/index.js',
|
||||
'node_modules/@homebridge/node-pty-prebuilt-multiarch/lib/unixTerminal.js'
|
||||
];
|
||||
|
||||
filesToClean.forEach(file => {
|
||||
const filePath = path.join(__dirname, file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
console.log(`Removed patched file: ${file}`);
|
||||
} catch (err) {
|
||||
console.error(`Failed to remove ${file}:`, err.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Run pnpm install to restore original files
|
||||
console.log('Running pnpm install to restore original files...');
|
||||
try {
|
||||
execSync('pnpm install @homebridge/node-pty-prebuilt-multiarch --force', {
|
||||
cwd: __dirname,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
console.log('Original files restored.');
|
||||
} catch (err) {
|
||||
console.error('Failed to restore original files:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup function
|
||||
function cleanup() {
|
||||
if (fs.existsSync('build') && !process.argv.includes('--keep-build')) {
|
||||
|
|
@ -469,34 +79,173 @@ process.on('SIGTERM', () => {
|
|||
process.exit(1);
|
||||
});
|
||||
|
||||
function applyMinimalPatches() {
|
||||
console.log('Applying minimal SEA patches to node-pty...');
|
||||
|
||||
// Create sea-loader.js
|
||||
const seaLoaderPath = path.join(__dirname, 'node_modules/node-pty/lib/sea-loader.js');
|
||||
if (!fs.existsSync(seaLoaderPath)) {
|
||||
const seaLoaderContent = `"use strict";
|
||||
/* VIBETUNNEL_SEA_LOADER */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
// Custom loader for SEA that uses process.dlopen
|
||||
var pty;
|
||||
|
||||
// Helper function to load native module using dlopen
|
||||
function loadNativeModule(modulePath) {
|
||||
const module = { exports: {} };
|
||||
process.dlopen(module, modulePath);
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
// Determine the path to pty.node
|
||||
function getPtyPath() {
|
||||
const execDir = path.dirname(process.execPath);
|
||||
// Look for pty.node next to the executable first
|
||||
const ptyPath = path.join(execDir, 'pty.node');
|
||||
|
||||
if (fs.existsSync(ptyPath)) {
|
||||
// Add path validation for security
|
||||
const resolvedPath = path.resolve(ptyPath);
|
||||
const resolvedExecDir = path.resolve(execDir);
|
||||
if (!resolvedPath.startsWith(resolvedExecDir)) {
|
||||
throw new Error('Invalid pty.node path detected');
|
||||
}
|
||||
return ptyPath;
|
||||
}
|
||||
|
||||
// If not found, throw error with helpful message
|
||||
throw new Error('Could not find pty.node next to executable at: ' + ptyPath);
|
||||
}
|
||||
|
||||
try {
|
||||
const ptyPath = getPtyPath();
|
||||
|
||||
// Set spawn-helper path for macOS only
|
||||
// Linux uses forkpty() directly and doesn't need spawn-helper
|
||||
if (process.platform === 'darwin') {
|
||||
const execDir = path.dirname(process.execPath);
|
||||
const spawnHelperPath = path.join(execDir, 'spawn-helper');
|
||||
if (fs.existsSync(spawnHelperPath)) {
|
||||
process.env.NODE_PTY_SPAWN_HELPER_PATH = spawnHelperPath;
|
||||
}
|
||||
}
|
||||
|
||||
pty = loadNativeModule(ptyPath);
|
||||
} catch (error) {
|
||||
console.error('Failed to load pty.node:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
exports.default = pty;
|
||||
`;
|
||||
fs.writeFileSync(seaLoaderPath, seaLoaderContent);
|
||||
}
|
||||
|
||||
// Patch index.js
|
||||
const indexPath = path.join(__dirname, 'node_modules/node-pty/lib/index.js');
|
||||
if (fs.existsSync(indexPath)) {
|
||||
let content = fs.readFileSync(indexPath, 'utf8');
|
||||
if (!content.includes('VIBETUNNEL_SEA')) {
|
||||
content = content.replace(
|
||||
"exports.native = (process.platform !== 'win32' ? require('../build/Release/pty.node') : null);",
|
||||
"exports.native = (process.platform !== 'win32' ? (process.env.VIBETUNNEL_SEA ? require('./sea-loader').default : require('../build/Release/pty.node')) : null);"
|
||||
);
|
||||
fs.writeFileSync(indexPath, content);
|
||||
}
|
||||
}
|
||||
|
||||
// Patch unixTerminal.js
|
||||
const unixPath = path.join(__dirname, 'node_modules/node-pty/lib/unixTerminal.js');
|
||||
if (fs.existsSync(unixPath)) {
|
||||
let content = fs.readFileSync(unixPath, 'utf8');
|
||||
if (!content.includes('VIBETUNNEL_SEA')) {
|
||||
// Find and replace the pty loading section
|
||||
const startMarker = 'var pty;\nvar helperPath;';
|
||||
const endMarker = 'var DEFAULT_FILE = \'sh\';';
|
||||
const startIdx = content.indexOf(startMarker);
|
||||
const endIdx = content.indexOf(endMarker);
|
||||
|
||||
if (startIdx !== -1 && endIdx !== -1) {
|
||||
const newSection = `var pty;
|
||||
var helperPath;
|
||||
// For SEA, check environment variables
|
||||
if (process.env.VIBETUNNEL_SEA) {
|
||||
pty = require('./sea-loader').default;
|
||||
// In SEA context, look for spawn-helper on macOS only (Linux doesn't use it)
|
||||
if (process.platform === 'darwin') {
|
||||
const execDir = path.dirname(process.execPath);
|
||||
const spawnHelperPath = path.join(execDir, 'spawn-helper');
|
||||
if (require('fs').existsSync(spawnHelperPath)) {
|
||||
helperPath = spawnHelperPath;
|
||||
} else if (process.env.NODE_PTY_SPAWN_HELPER_PATH) {
|
||||
helperPath = process.env.NODE_PTY_SPAWN_HELPER_PATH;
|
||||
}
|
||||
}
|
||||
// On Linux, helperPath remains undefined which is fine
|
||||
} else {
|
||||
// Original loading logic
|
||||
try {
|
||||
pty = require('../build/Release/pty.node');
|
||||
helperPath = '../build/Release/spawn-helper';
|
||||
}
|
||||
catch (outerError) {
|
||||
try {
|
||||
pty = require('../build/Debug/pty.node');
|
||||
helperPath = '../build/Debug/spawn-helper';
|
||||
}
|
||||
catch (innerError) {
|
||||
console.error('innerError', innerError);
|
||||
// Re-throw the exception from the Release require if the Debug require fails as well
|
||||
throw outerError;
|
||||
}
|
||||
}
|
||||
helperPath = path.resolve(__dirname, helperPath);
|
||||
helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
|
||||
helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
|
||||
}
|
||||
`;
|
||||
content = content.substring(0, startIdx) + newSection + content.substring(endIdx);
|
||||
fs.writeFileSync(unixPath, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('SEA patches applied successfully');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// Set up environment to suppress warnings
|
||||
process.env.NODE_NO_WARNINGS = '1';
|
||||
process.env.npm_config_loglevel = 'error';
|
||||
// Apply minimal patches to node-pty
|
||||
applyMinimalPatches();
|
||||
|
||||
// Handle command line arguments
|
||||
if (process.argv.includes('--help')) {
|
||||
console.log('VibeTunnel Native Build Script\n');
|
||||
console.log('Usage: node build-native.js [options]\n');
|
||||
console.log('Options:');
|
||||
console.log(' --help Show this help message');
|
||||
console.log(' --clean-patches Remove all patches from node-pty and restore original files');
|
||||
console.log(' --force-patch Force re-patching even if files are already patched');
|
||||
console.log(' --keep-build Keep the build directory after completion');
|
||||
console.log(' --node <path> Use a custom Node.js binary for SEA\n');
|
||||
process.exit(0);
|
||||
// Ensure native modules are built (in case postinstall didn't run)
|
||||
const nativePtyDir = 'node_modules/node-pty/build/Release';
|
||||
const nativeAuthDir = 'node_modules/authenticate-pam/build/Release';
|
||||
|
||||
if (!fs.existsSync(nativePtyDir)) {
|
||||
console.log('Building node-pty native module...');
|
||||
// Find the actual node-pty path (could be in .pnpm directory)
|
||||
const nodePtyPath = require.resolve('node-pty/package.json');
|
||||
const nodePtyDir = path.dirname(nodePtyPath);
|
||||
console.log(`Found node-pty at: ${nodePtyDir}`);
|
||||
|
||||
// Build node-pty using node-gyp directly to avoid TypeScript compilation
|
||||
execSync(`cd "${nodePtyDir}" && npx node-gyp rebuild`, {
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
}
|
||||
|
||||
if (process.argv.includes('--clean-patches')) {
|
||||
cleanPatches();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const forcePatching = process.argv.includes('--force-patch');
|
||||
if (forcePatching) {
|
||||
console.log('Force patching enabled - will re-patch even if already patched');
|
||||
cleanPatches();
|
||||
if (!fs.existsSync(nativeAuthDir)) {
|
||||
console.log('Building authenticate-pam native module...');
|
||||
execSync('npm rebuild authenticate-pam', {
|
||||
stdio: 'inherit',
|
||||
cwd: __dirname
|
||||
});
|
||||
}
|
||||
|
||||
// Create build directory
|
||||
|
|
@ -512,11 +261,52 @@ async function main() {
|
|||
// 0. Determine which Node.js to use
|
||||
let nodeExe = process.execPath;
|
||||
if (customNodePath) {
|
||||
// Validate custom node exists
|
||||
if (!fs.existsSync(customNodePath)) {
|
||||
console.error(`Error: Custom Node.js not found at ${customNodePath}`);
|
||||
console.error('Build one using: node build-custom-node.js');
|
||||
process.exit(1);
|
||||
if (customNodePath === 'auto') {
|
||||
// Auto-discover custom Node.js build
|
||||
const buildDir = path.join(__dirname, '.node-builds');
|
||||
if (fs.existsSync(buildDir)) {
|
||||
// Find the most recent custom Node.js build
|
||||
const builds = fs.readdirSync(buildDir)
|
||||
.filter(name => name.startsWith('node-v') && name.endsWith('-minimal'))
|
||||
.map(name => {
|
||||
const nodePath = path.join(buildDir, name, 'out', 'Release', 'node');
|
||||
if (fs.existsSync(nodePath)) {
|
||||
const match = name.match(/node-v(.+)-minimal/);
|
||||
if (!match || !match[1]) {
|
||||
console.warn(`Warning: Skipping directory with invalid name format: ${name}`);
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
path: nodePath,
|
||||
version: match[1],
|
||||
mtime: fs.statSync(nodePath).mtime
|
||||
};
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => b.mtime - a.mtime);
|
||||
|
||||
if (builds.length > 0) {
|
||||
customNodePath = builds[0].path;
|
||||
console.log(`Auto-discovered custom Node.js v${builds[0].version} at ${customNodePath}`);
|
||||
} else {
|
||||
console.error('Error: No custom Node.js builds found in .node-builds/');
|
||||
console.error('Build one using: node build-custom-node.js');
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.error('Error: No .node-builds directory found');
|
||||
console.error('Build a custom Node.js using: node build-custom-node.js');
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
// Validate custom node exists at specified path
|
||||
if (!fs.existsSync(customNodePath)) {
|
||||
console.error(`Error: Custom Node.js not found at ${customNodePath}`);
|
||||
console.error('Build one using: node build-custom-node.js');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
nodeExe = customNodePath;
|
||||
}
|
||||
|
|
@ -525,26 +315,35 @@ async function main() {
|
|||
const nodeStats = fs.statSync(nodeExe);
|
||||
console.log(`Node.js binary size: ${(nodeStats.size / 1024 / 1024).toFixed(2)} MB`);
|
||||
|
||||
// Get version of the Node.js we're using
|
||||
// 1. Rebuild native modules if using custom Node.js
|
||||
if (customNodePath) {
|
||||
try {
|
||||
const customVersion = execSync(`"${nodeExe}" --version`, { encoding: 'utf8' }).trim();
|
||||
console.log(`Custom Node.js version: ${customVersion}`);
|
||||
console.log('This minimal build excludes intl, npm, inspector, and other unused features.');
|
||||
} catch (e) {
|
||||
console.log('Could not determine custom Node.js version');
|
||||
}
|
||||
console.log('\nCustom Node.js detected - rebuilding native modules...');
|
||||
const customVersion = execSync(`"${nodeExe}" --version`, { encoding: 'utf8' }).trim();
|
||||
console.log(`Custom Node.js version: ${customVersion}`);
|
||||
|
||||
execSync(`pnpm rebuild node-pty authenticate-pam`, {
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_runtime: 'node',
|
||||
npm_config_target: customVersion.substring(1), // Remove 'v' prefix
|
||||
npm_config_arch: process.arch,
|
||||
npm_config_target_arch: process.arch,
|
||||
npm_config_disturl: 'https://nodejs.org/dist',
|
||||
npm_config_build_from_source: 'true',
|
||||
// Node.js 24 requires C++20
|
||||
CXXFLAGS: '-std=c++20',
|
||||
npm_config_cxxflags: '-std=c++20'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 1. Patch node-pty
|
||||
patchNodePty();
|
||||
|
||||
// 2. Bundle TypeScript with esbuild using custom loader
|
||||
// 2. Bundle TypeScript with esbuild
|
||||
console.log('\nBundling TypeScript with esbuild...');
|
||||
|
||||
// Use deterministic timestamps based on git commit or source
|
||||
let buildDate;
|
||||
let buildTimestamp;
|
||||
let buildDate = new Date().toISOString();
|
||||
let buildTimestamp = Date.now();
|
||||
|
||||
try {
|
||||
// Try to use the last commit date for reproducible builds
|
||||
|
|
@ -553,20 +352,10 @@ async function main() {
|
|||
buildTimestamp = new Date(gitDate).getTime();
|
||||
console.log(`Using git commit date for reproducible build: ${buildDate}`);
|
||||
} catch (e) {
|
||||
// Fallback to SOURCE_DATE_EPOCH if set (for reproducible builds)
|
||||
if (process.env.SOURCE_DATE_EPOCH) {
|
||||
buildTimestamp = parseInt(process.env.SOURCE_DATE_EPOCH) * 1000;
|
||||
buildDate = new Date(buildTimestamp).toISOString();
|
||||
console.log(`Using SOURCE_DATE_EPOCH for reproducible build: ${buildDate}`);
|
||||
} else {
|
||||
// Only use current time as last resort
|
||||
buildDate = new Date().toISOString();
|
||||
buildTimestamp = Date.now();
|
||||
console.warn('Warning: Using current time for build - output will not be reproducible');
|
||||
}
|
||||
// Fallback to current time
|
||||
console.warn('Warning: Using current time for build - output will not be reproducible');
|
||||
}
|
||||
|
||||
// Use esbuild directly without custom loader since we're patching node-pty
|
||||
let esbuildCmd = `NODE_NO_WARNINGS=1 npx esbuild src/cli.ts \\
|
||||
--bundle \\
|
||||
--platform=node \\
|
||||
|
|
@ -575,15 +364,17 @@ async function main() {
|
|||
--format=cjs \\
|
||||
--keep-names \\
|
||||
--external:authenticate-pam \\
|
||||
--external:../build/Release/pty.node \\
|
||||
--external:./build/Release/pty.node \\
|
||||
--define:process.env.BUILD_DATE='"${buildDate}"' \\
|
||||
--define:process.env.BUILD_TIMESTAMP='"${buildTimestamp}"'`;
|
||||
--define:process.env.BUILD_TIMESTAMP='"${buildTimestamp}"' \\
|
||||
--define:process.env.VIBETUNNEL_SEA='"true"'`;
|
||||
|
||||
// Also inject git commit hash for version tracking
|
||||
try {
|
||||
const gitCommit = execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim();
|
||||
esbuildCmd += ` \\\n --define:process.env.GIT_COMMIT='"${gitCommit}"'`;
|
||||
} catch (e) {
|
||||
// Not in a git repo or git not available
|
||||
esbuildCmd += ` \\\n --define:process.env.GIT_COMMIT='"unknown"'`;
|
||||
}
|
||||
|
||||
|
|
@ -600,7 +391,30 @@ async function main() {
|
|||
}
|
||||
});
|
||||
|
||||
// 2. Create SEA configuration
|
||||
// 2b. Post-process bundle to ensure VIBETUNNEL_SEA is properly set
|
||||
console.log('\nPost-processing bundle for SEA compatibility...');
|
||||
let bundleContent = fs.readFileSync('build/bundle.js', 'utf8');
|
||||
|
||||
// Remove shebang line if present (not valid in SEA bundles)
|
||||
if (bundleContent.startsWith('#!')) {
|
||||
bundleContent = bundleContent.substring(bundleContent.indexOf('\n') + 1);
|
||||
}
|
||||
|
||||
// Add VIBETUNNEL_SEA environment variable at the top of the bundle
|
||||
// This ensures the patched node-pty knows it's running in SEA mode
|
||||
const seaEnvSetup = `// Set VIBETUNNEL_SEA environment variable for SEA mode
|
||||
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
||||
process.env.VIBETUNNEL_SEA = 'true';
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
bundleContent = seaEnvSetup + bundleContent;
|
||||
|
||||
fs.writeFileSync('build/bundle.js', bundleContent);
|
||||
console.log('Bundle post-processing complete');
|
||||
|
||||
// 3. Create SEA configuration
|
||||
console.log('\nCreating SEA configuration...');
|
||||
const seaConfig = {
|
||||
main: 'build/bundle.js',
|
||||
|
|
@ -612,11 +426,11 @@ async function main() {
|
|||
|
||||
fs.writeFileSync('build/sea-config.json', JSON.stringify(seaConfig, null, 2));
|
||||
|
||||
// 3. Generate SEA blob
|
||||
// 4. Generate SEA blob
|
||||
console.log('Generating SEA blob...');
|
||||
execSync('node --experimental-sea-config build/sea-config.json', { stdio: 'inherit' });
|
||||
|
||||
// 4. Create executable
|
||||
// 5. Create executable
|
||||
console.log('\nCreating executable...');
|
||||
const targetExe = process.platform === 'win32' ? 'native/vibetunnel.exe' : 'native/vibetunnel';
|
||||
|
||||
|
|
@ -626,7 +440,7 @@ async function main() {
|
|||
fs.chmodSync(targetExe, 0o755);
|
||||
}
|
||||
|
||||
// 5. Inject the blob
|
||||
// 6. Inject the blob
|
||||
console.log('Injecting SEA blob...');
|
||||
let postjectCmd = `npx postject ${targetExe} NODE_SEA_BLOB build/sea-prep.blob \\
|
||||
--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2`;
|
||||
|
|
@ -637,16 +451,14 @@ async function main() {
|
|||
|
||||
execSync(postjectCmd, { stdio: 'inherit' });
|
||||
|
||||
// 6. Strip the executable first (before signing)
|
||||
// 7. Strip the executable first (before signing)
|
||||
console.log('Stripping final executable...');
|
||||
// Note: This will show a warning about invalidating code signature, which is expected
|
||||
// since we're modifying a signed Node.js binary. We'll re-sign it in the next step.
|
||||
execSync(`strip -S ${targetExe} 2>&1 | grep -v "warning: changes being made" || true`, {
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
|
||||
// 7. Sign on macOS (after stripping)
|
||||
// 8. Sign on macOS (after stripping)
|
||||
if (process.platform === 'darwin') {
|
||||
console.log('Signing executable...');
|
||||
execSync(`codesign --sign - ${targetExe}`, { stdio: 'inherit' });
|
||||
|
|
@ -657,9 +469,13 @@ async function main() {
|
|||
console.log(`Final executable size: ${(finalStats.size / 1024 / 1024).toFixed(2)} MB`);
|
||||
console.log(`Size reduction: ${((nodeStats.size - finalStats.size) / 1024 / 1024).toFixed(2)} MB`);
|
||||
|
||||
// 8. Copy native modules BEFORE restoring (to preserve custom-built versions)
|
||||
console.log('Copying native modules...');
|
||||
const nativeModulesDir = 'node_modules/@homebridge/node-pty-prebuilt-multiarch/build/Release';
|
||||
// 9. Copy native modules
|
||||
console.log('\nCopying native modules...');
|
||||
|
||||
// Find the actual node-pty build directory (could be in .pnpm directory)
|
||||
const nodePtyPath = require.resolve('node-pty/package.json');
|
||||
const nodePtyBaseDir = path.dirname(nodePtyPath);
|
||||
const nativeModulesDir = path.join(nodePtyBaseDir, 'build/Release');
|
||||
|
||||
// Check if native modules exist
|
||||
if (!fs.existsSync(nativeModulesDir)) {
|
||||
|
|
@ -677,8 +493,10 @@ async function main() {
|
|||
fs.copyFileSync(ptyNodePath, 'native/pty.node');
|
||||
console.log(' - Copied pty.node');
|
||||
|
||||
// Copy spawn-helper (Unix only)
|
||||
if (process.platform !== 'win32') {
|
||||
// Copy spawn-helper (macOS only)
|
||||
// Note: spawn-helper is only built and required on macOS where it's used for pty_posix_spawn()
|
||||
// On Linux, node-pty uses forkpty() directly and doesn't need spawn-helper
|
||||
if (process.platform === 'darwin') {
|
||||
const spawnHelperPath = path.join(nativeModulesDir, 'spawn-helper');
|
||||
if (!fs.existsSync(spawnHelperPath)) {
|
||||
console.error('Error: spawn-helper not found. Native module build may have failed.');
|
||||
|
|
@ -699,18 +517,14 @@ async function main() {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
// 9. Restore original node-pty (AFTER copying the custom-built version)
|
||||
console.log('\nRestoring original node-pty for development...');
|
||||
execSync('rm -rf node_modules/@homebridge/node-pty-prebuilt-multiarch', { stdio: 'inherit' });
|
||||
execSync('pnpm install @homebridge/node-pty-prebuilt-multiarch --silent', { stdio: 'inherit' });
|
||||
|
||||
console.log('\n✅ Build complete!');
|
||||
console.log(`\nPortable executable created in native/ directory:`);
|
||||
console.log(` - vibetunnel (executable)`);
|
||||
console.log(` - pty.node`);
|
||||
if (process.platform !== 'win32') {
|
||||
if (process.platform === 'darwin') {
|
||||
console.log(` - spawn-helper`);
|
||||
}
|
||||
console.log(` - authenticate_pam.node`);
|
||||
console.log('\nAll files must be kept together in the same directory.');
|
||||
console.log('This bundle will work on any machine with the same OS/architecture.');
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* Tests PTY spawning, terminal raw mode, stdin/stdout forwarding
|
||||
*/
|
||||
|
||||
import * as pty from '@homebridge/node-pty-prebuilt-multiarch';
|
||||
import { which } from '@homebridge/node-pty-prebuilt-multiarch/lib/utils';
|
||||
import * as pty from 'node-pty';
|
||||
import { which } from 'node-pty/lib/utils';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@
|
|||
"lint:fix": "biome check src --write",
|
||||
"lint:biome": "biome check src",
|
||||
"typecheck": "concurrently -n server,client,sw \"tsc --noEmit --project tsconfig.server.json\" \"tsc --noEmit --project tsconfig.client.json\" \"tsc --noEmit --project tsconfig.sw.json\"",
|
||||
"pretest": "node scripts/ensure-native-modules.js",
|
||||
"test": "vitest",
|
||||
"test:ci": "vitest run --reporter=verbose",
|
||||
"test:ci": "npm run pretest && vitest run --reporter=verbose",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:client": "vitest run --mode=client",
|
||||
"test:server": "vitest run --mode=server",
|
||||
|
|
@ -31,7 +32,6 @@
|
|||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@homebridge/node-pty-prebuilt-multiarch",
|
||||
"authenticate-pam",
|
||||
"esbuild",
|
||||
"puppeteer"
|
||||
|
|
@ -48,7 +48,6 @@
|
|||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@codemirror/view": "^6.28.0",
|
||||
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0",
|
||||
"@xterm/headless": "^5.5.0",
|
||||
"authenticate-pam": "^1.0.5",
|
||||
"chalk": "^4.1.2",
|
||||
|
|
@ -57,6 +56,7 @@
|
|||
"lit": "^3.3.0",
|
||||
"mime-types": "^3.0.1",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"node-pty": "github:microsoft/node-pty#v1.1.0-beta34",
|
||||
"postject": "^1.0.0-alpha.6",
|
||||
"signal-exit": "^4.1.0",
|
||||
"web-push": "^3.6.7",
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@ importers:
|
|||
'@codemirror/view':
|
||||
specifier: ^6.28.0
|
||||
version: 6.37.2
|
||||
'@homebridge/node-pty-prebuilt-multiarch':
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0
|
||||
'@xterm/headless':
|
||||
specifier: ^5.5.0
|
||||
version: 5.5.0
|
||||
|
|
@ -65,6 +62,9 @@ importers:
|
|||
monaco-editor:
|
||||
specifier: ^0.52.2
|
||||
version: 0.52.2
|
||||
node-pty:
|
||||
specifier: github:microsoft/node-pty#v1.1.0-beta34
|
||||
version: https://codeload.github.com/microsoft/node-pty/tar.gz/d738123f1faf7287513b0df8b9e327be54702e94
|
||||
postject:
|
||||
specifier: ^1.0.0-alpha.6
|
||||
version: 1.0.0-alpha.6
|
||||
|
|
@ -468,9 +468,6 @@ packages:
|
|||
'@hapi/bourne@3.0.0':
|
||||
resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
|
||||
|
||||
'@homebridge/node-pty-prebuilt-multiarch@0.12.0':
|
||||
resolution: {integrity: sha512-hJCGcfOnMeRh2KUdWPlVN/1egnfqI4yxgpDhqHSkF2DLn5fiJNdjEHHlcM1K2w9+QBmRE2D/wfmM4zUOb8aMyQ==}
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -1141,9 +1138,6 @@ packages:
|
|||
bare-events:
|
||||
optional: true
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
basic-ftp@5.0.5:
|
||||
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
|
@ -1152,9 +1146,6 @@ packages:
|
|||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
|
||||
bn.js@4.12.2:
|
||||
resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==}
|
||||
|
||||
|
|
@ -1180,9 +1171,6 @@ packages:
|
|||
buffer-equal-constant-time@1.0.1:
|
||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||
|
||||
buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
|
||||
bytes@3.1.2:
|
||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
|
@ -1246,9 +1234,6 @@ packages:
|
|||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
|
||||
chownr@1.1.4:
|
||||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||
|
||||
chromium-bidi@5.1.0:
|
||||
resolution: {integrity: sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==}
|
||||
peerDependencies:
|
||||
|
|
@ -1396,10 +1381,6 @@ packages:
|
|||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
deep-eql@5.0.2:
|
||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -1407,10 +1388,6 @@ packages:
|
|||
deep-equal@1.0.1:
|
||||
resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==}
|
||||
|
||||
deep-extend@0.6.0:
|
||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
||||
default-gateway@6.0.3:
|
||||
resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==}
|
||||
engines: {node: '>= 10'}
|
||||
|
|
@ -1450,10 +1427,6 @@ packages:
|
|||
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
||||
detect-libc@2.0.4:
|
||||
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
devtools-protocol@0.0.1452169:
|
||||
resolution: {integrity: sha512-FOFDVMGrAUNp0dDKsAU1TorWJUx2JOU1k9xdgBKKJF3IBh/Uhl2yswG5r3TEAOrCiGY2QRp1e6LVDQrCsTKO4g==}
|
||||
|
||||
|
|
@ -1579,10 +1552,6 @@ packages:
|
|||
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
expand-template@2.0.3:
|
||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
expect-type@1.2.1:
|
||||
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
|
@ -1669,9 +1638,6 @@ packages:
|
|||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
fs-constants@1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
|
|
@ -1707,9 +1673,6 @@ packages:
|
|||
resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
github-from-package@0.0.0:
|
||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
|
|
@ -1789,9 +1752,6 @@ packages:
|
|||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
ignore@5.3.2:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
engines: {node: '>= 4'}
|
||||
|
|
@ -1810,9 +1770,6 @@ packages:
|
|||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
ini@1.3.8:
|
||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||
|
||||
internal-ip@6.2.0:
|
||||
resolution: {integrity: sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -2111,10 +2068,6 @@ packages:
|
|||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
minimalistic-assert@1.0.1:
|
||||
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
|
||||
|
||||
|
|
@ -2132,9 +2085,6 @@ packages:
|
|||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
mkdirp-classic@0.5.3:
|
||||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||
|
||||
mkdirp@1.0.4:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -2167,9 +2117,6 @@ packages:
|
|||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
napi-build-utils@2.0.0:
|
||||
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
||||
|
||||
negotiator@0.6.3:
|
||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -2178,10 +2125,6 @@ packages:
|
|||
resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
node-abi@3.75.0:
|
||||
resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
|
|
@ -2194,6 +2137,10 @@ packages:
|
|||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
node-pty@https://codeload.github.com/microsoft/node-pty/tar.gz/d738123f1faf7287513b0df8b9e327be54702e94:
|
||||
resolution: {tarball: https://codeload.github.com/microsoft/node-pty/tar.gz/d738123f1faf7287513b0df8b9e327be54702e94}
|
||||
version: 1.0.0
|
||||
|
||||
node-releases@2.0.19:
|
||||
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
|
||||
|
||||
|
|
@ -2394,11 +2341,6 @@ packages:
|
|||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
prettier@3.6.1:
|
||||
resolution: {integrity: sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==}
|
||||
engines: {node: '>=14'}
|
||||
|
|
@ -2454,20 +2396,12 @@ packages:
|
|||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
rc@1.2.8:
|
||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||
hasBin: true
|
||||
|
||||
react-is@17.0.2:
|
||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||
|
||||
read-cache@1.0.0:
|
||||
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
||||
|
||||
readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
|
@ -2588,12 +2522,6 @@ packages:
|
|||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
simple-concat@1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||
|
||||
simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
|
||||
sirv@3.0.1:
|
||||
resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -2662,9 +2590,6 @@ packages:
|
|||
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
|
||||
strip-ansi@5.2.0:
|
||||
resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -2681,10 +2606,6 @@ packages:
|
|||
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
strip-json-comments@2.0.1:
|
||||
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
strip-literal@3.0.0:
|
||||
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
|
||||
|
||||
|
|
@ -2721,16 +2642,9 @@ packages:
|
|||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
tar-fs@2.1.3:
|
||||
resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==}
|
||||
|
||||
tar-fs@3.0.10:
|
||||
resolution: {integrity: sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==}
|
||||
|
||||
tar-stream@2.2.0:
|
||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tar-stream@3.1.7:
|
||||
resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
|
||||
|
||||
|
|
@ -2801,9 +2715,6 @@ packages:
|
|||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
|
||||
type-fest@0.21.3:
|
||||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -3299,11 +3210,6 @@ snapshots:
|
|||
|
||||
'@hapi/bourne@3.0.0': {}
|
||||
|
||||
'@homebridge/node-pty-prebuilt-multiarch@0.12.0':
|
||||
dependencies:
|
||||
node-addon-api: 7.1.1
|
||||
prebuild-install: 7.1.3
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
string-width: 5.1.2
|
||||
|
|
@ -4042,18 +3948,10 @@ snapshots:
|
|||
bare-events: 2.5.4
|
||||
optional: true
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
basic-ftp@5.0.5: {}
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
bl@4.1.0:
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
|
||||
bn.js@4.12.2: {}
|
||||
|
||||
body-parser@1.20.3:
|
||||
|
|
@ -4092,11 +3990,6 @@ snapshots:
|
|||
|
||||
buffer-equal-constant-time@1.0.1: {}
|
||||
|
||||
buffer@5.7.1:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
bytes@3.1.2: {}
|
||||
|
||||
cac@6.7.14: {}
|
||||
|
|
@ -4166,8 +4059,6 @@ snapshots:
|
|||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
|
||||
chownr@1.1.4: {}
|
||||
|
||||
chromium-bidi@5.1.0(devtools-protocol@0.0.1452169):
|
||||
dependencies:
|
||||
devtools-protocol: 0.0.1452169
|
||||
|
|
@ -4292,16 +4183,10 @@ snapshots:
|
|||
|
||||
decamelize@1.2.0: {}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
dependencies:
|
||||
mimic-response: 3.1.0
|
||||
|
||||
deep-eql@5.0.2: {}
|
||||
|
||||
deep-equal@1.0.1: {}
|
||||
|
||||
deep-extend@0.6.0: {}
|
||||
|
||||
default-gateway@6.0.3:
|
||||
dependencies:
|
||||
execa: 5.1.1
|
||||
|
|
@ -4328,8 +4213,6 @@ snapshots:
|
|||
|
||||
destroy@1.2.0: {}
|
||||
|
||||
detect-libc@2.0.4: {}
|
||||
|
||||
devtools-protocol@0.0.1452169: {}
|
||||
|
||||
dezalgo@1.0.4:
|
||||
|
|
@ -4466,8 +4349,6 @@ snapshots:
|
|||
signal-exit: 3.0.7
|
||||
strip-final-newline: 2.0.0
|
||||
|
||||
expand-template@2.0.3: {}
|
||||
|
||||
expect-type@1.2.1: {}
|
||||
|
||||
express@4.21.2:
|
||||
|
|
@ -4598,8 +4479,6 @@ snapshots:
|
|||
|
||||
fresh@0.5.2: {}
|
||||
|
||||
fs-constants@1.0.0: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
|
|
@ -4643,8 +4522,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
github-from-package@0.0.0: {}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
|
|
@ -4743,8 +4620,6 @@ snapshots:
|
|||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
|
|
@ -4758,8 +4633,6 @@ snapshots:
|
|||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
ini@1.3.8: {}
|
||||
|
||||
internal-ip@6.2.0:
|
||||
dependencies:
|
||||
default-gateway: 6.0.3
|
||||
|
|
@ -5073,8 +4946,6 @@ snapshots:
|
|||
|
||||
mimic-fn@2.1.0: {}
|
||||
|
||||
mimic-response@3.1.0: {}
|
||||
|
||||
minimalistic-assert@1.0.1: {}
|
||||
|
||||
minimatch@9.0.5:
|
||||
|
|
@ -5087,8 +4958,6 @@ snapshots:
|
|||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mkdirp-classic@0.5.3: {}
|
||||
|
||||
mkdirp@1.0.4: {}
|
||||
|
||||
monaco-editor@0.52.2: {}
|
||||
|
|
@ -5111,16 +4980,10 @@ snapshots:
|
|||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
napi-build-utils@2.0.0: {}
|
||||
|
||||
negotiator@0.6.3: {}
|
||||
|
||||
netmask@2.0.2: {}
|
||||
|
||||
node-abi@3.75.0:
|
||||
dependencies:
|
||||
semver: 7.7.2
|
||||
|
||||
node-addon-api@7.1.1: {}
|
||||
|
||||
node-domexception@1.0.0: {}
|
||||
|
|
@ -5131,6 +4994,10 @@ snapshots:
|
|||
fetch-blob: 3.2.0
|
||||
formdata-polyfill: 4.0.10
|
||||
|
||||
node-pty@https://codeload.github.com/microsoft/node-pty/tar.gz/d738123f1faf7287513b0df8b9e327be54702e94:
|
||||
dependencies:
|
||||
node-addon-api: 7.1.1
|
||||
|
||||
node-releases@2.0.19: {}
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
|
@ -5316,21 +5183,6 @@ snapshots:
|
|||
dependencies:
|
||||
commander: 9.5.0
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
dependencies:
|
||||
detect-libc: 2.0.4
|
||||
expand-template: 2.0.3
|
||||
github-from-package: 0.0.0
|
||||
minimist: 1.2.8
|
||||
mkdirp-classic: 0.5.3
|
||||
napi-build-utils: 2.0.0
|
||||
node-abi: 3.75.0
|
||||
pump: 3.0.3
|
||||
rc: 1.2.8
|
||||
simple-get: 4.0.1
|
||||
tar-fs: 2.1.3
|
||||
tunnel-agent: 0.6.0
|
||||
|
||||
prettier@3.6.1: {}
|
||||
|
||||
pretty-format@27.5.1:
|
||||
|
|
@ -5414,25 +5266,12 @@ snapshots:
|
|||
iconv-lite: 0.4.24
|
||||
unpipe: 1.0.0
|
||||
|
||||
rc@1.2.8:
|
||||
dependencies:
|
||||
deep-extend: 0.6.0
|
||||
ini: 1.3.8
|
||||
minimist: 1.2.8
|
||||
strip-json-comments: 2.0.1
|
||||
|
||||
react-is@17.0.2: {}
|
||||
|
||||
read-cache@1.0.0:
|
||||
dependencies:
|
||||
pify: 2.3.0
|
||||
|
||||
readable-stream@3.6.2:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
|
@ -5586,14 +5425,6 @@ snapshots:
|
|||
|
||||
signal-exit@4.1.0: {}
|
||||
|
||||
simple-concat@1.0.1: {}
|
||||
|
||||
simple-get@4.0.1:
|
||||
dependencies:
|
||||
decompress-response: 6.0.0
|
||||
once: 1.4.0
|
||||
simple-concat: 1.0.1
|
||||
|
||||
sirv@3.0.1:
|
||||
dependencies:
|
||||
'@polka/url': 1.0.0-next.29
|
||||
|
|
@ -5665,10 +5496,6 @@ snapshots:
|
|||
emoji-regex: 9.2.2
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
strip-ansi@5.2.0:
|
||||
dependencies:
|
||||
ansi-regex: 4.1.1
|
||||
|
|
@ -5683,8 +5510,6 @@ snapshots:
|
|||
|
||||
strip-final-newline@2.0.0: {}
|
||||
|
||||
strip-json-comments@2.0.1: {}
|
||||
|
||||
strip-literal@3.0.0:
|
||||
dependencies:
|
||||
js-tokens: 9.0.1
|
||||
|
|
@ -5759,13 +5584,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
|
||||
tar-fs@2.1.3:
|
||||
dependencies:
|
||||
chownr: 1.1.4
|
||||
mkdirp-classic: 0.5.3
|
||||
pump: 3.0.3
|
||||
tar-stream: 2.2.0
|
||||
|
||||
tar-fs@3.0.10:
|
||||
dependencies:
|
||||
pump: 3.0.3
|
||||
|
|
@ -5776,14 +5594,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- bare-buffer
|
||||
|
||||
tar-stream@2.2.0:
|
||||
dependencies:
|
||||
bl: 4.1.0
|
||||
end-of-stream: 1.4.5
|
||||
fs-constants: 1.0.0
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
|
||||
tar-stream@3.1.7:
|
||||
dependencies:
|
||||
b4a: 1.6.7
|
||||
|
|
@ -5846,10 +5656,6 @@ snapshots:
|
|||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
type-fest@0.21.3: {}
|
||||
|
||||
type-is@1.6.18:
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ async function build() {
|
|||
format: 'cjs',
|
||||
outfile: 'dist/vibetunnel-cli',
|
||||
external: [
|
||||
'@homebridge/node-pty-prebuilt-multiarch',
|
||||
'node-pty',
|
||||
'authenticate-pam',
|
||||
],
|
||||
minify: true,
|
||||
|
|
|
|||
66
web/scripts/ensure-native-modules.js
Executable file
66
web/scripts/ensure-native-modules.js
Executable file
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Ensures native modules are built and available for tests
|
||||
* This script handles pnpm's symlink structure where node-pty might be in .pnpm directory
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
console.log('Ensuring native modules are built for tests...');
|
||||
|
||||
// Find the actual node-pty location (could be in .pnpm directory)
|
||||
let nodePtyPath;
|
||||
try {
|
||||
nodePtyPath = require.resolve('node-pty/package.json');
|
||||
} catch (e) {
|
||||
console.error('Could not find node-pty module');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const nodePtyDir = path.dirname(nodePtyPath);
|
||||
const buildDir = path.join(nodePtyDir, 'build');
|
||||
const releaseDir = path.join(buildDir, 'Release');
|
||||
|
||||
console.log(`Found node-pty at: ${nodePtyDir}`);
|
||||
|
||||
// Check if native modules are built
|
||||
if (!fs.existsSync(releaseDir) || !fs.existsSync(path.join(releaseDir, 'pty.node'))) {
|
||||
console.log('Native modules not found, building...');
|
||||
|
||||
try {
|
||||
// Build using node-gyp directly to avoid TypeScript issues
|
||||
execSync(`cd "${nodePtyDir}" && npx node-gyp rebuild`, {
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to build native modules:', e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// For pnpm, ensure the symlinked node_modules/node-pty has the build directory
|
||||
const symlinkNodePty = path.join(__dirname, '../node_modules/node-pty');
|
||||
if (fs.existsSync(symlinkNodePty) && fs.lstatSync(symlinkNodePty).isSymbolicLink()) {
|
||||
const symlinkBuildDir = path.join(symlinkNodePty, 'build');
|
||||
|
||||
// If the symlinked location doesn't have a build directory, create a symlink to the real one
|
||||
if (!fs.existsSync(symlinkBuildDir) && fs.existsSync(buildDir)) {
|
||||
console.log('Creating symlink for build directory in node_modules/node-pty...');
|
||||
try {
|
||||
const symlinkType = process.platform === 'win32' ? 'junction' : 'dir';
|
||||
fs.symlinkSync(buildDir, symlinkBuildDir, symlinkType);
|
||||
console.log('Created build directory symlink');
|
||||
} catch (e) {
|
||||
// If symlink fails, try copying instead
|
||||
console.log('Symlink failed, trying to copy build directory...');
|
||||
fs.cpSync(buildDir, symlinkBuildDir, { recursive: true });
|
||||
console.log('Copied build directory');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Native modules are ready for tests');
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
import { html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { HeaderBase } from './header-base.js';
|
||||
import type { Session } from './session-list.js';
|
||||
import './terminal-icon.js';
|
||||
|
||||
@customElement('sidebar-header')
|
||||
|
|
@ -58,83 +57,6 @@ export class SidebarHeader extends HeaderBase {
|
|||
`;
|
||||
}
|
||||
|
||||
private renderExitedToggleButton(exitedSessions: Session[], compact: boolean) {
|
||||
if (exitedSessions.length === 0) return '';
|
||||
|
||||
const buttonClass = compact
|
||||
? 'relative font-mono text-xs px-3 py-1.5 w-full rounded-lg border transition-all duration-200'
|
||||
: 'relative font-mono text-xs px-4 py-2 rounded-lg border transition-all duration-200';
|
||||
|
||||
const stateClass = this.hideExited
|
||||
? 'border-dark-border bg-dark-bg-tertiary text-dark-text hover:border-accent-green-darker'
|
||||
: 'border-accent-green bg-accent-green text-dark-bg hover:bg-accent-green-darker';
|
||||
|
||||
return html`
|
||||
<button
|
||||
class="${buttonClass} ${stateClass}"
|
||||
@click=${this.handleHideExitedToggle}
|
||||
title="${
|
||||
this.hideExited
|
||||
? `Show ${exitedSessions.length} exited sessions`
|
||||
: `Hide ${exitedSessions.length} exited sessions`
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>${this.hideExited ? 'Show' : 'Hide'} Exited</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs opacity-75">(${exitedSessions.length})</span>
|
||||
<div
|
||||
class="w-8 h-4 rounded-full transition-colors duration-200 ${
|
||||
this.hideExited ? 'bg-dark-border' : 'bg-dark-bg'
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="w-3 h-3 rounded-full transition-transform duration-200 mt-0.5 ${
|
||||
this.hideExited
|
||||
? 'translate-x-0.5 bg-dark-text-muted'
|
||||
: 'translate-x-4 bg-dark-bg'
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderKillAllButton(runningSessions: Session[]) {
|
||||
// Only show Kill button if there are running sessions
|
||||
if (runningSessions.length === 0) return '';
|
||||
|
||||
// Matching the same style as Show Exited button for consistency
|
||||
const buttonClass =
|
||||
'relative font-mono text-xs px-3 py-1.5 w-full rounded-lg border transition-all duration-200';
|
||||
const stateClass = this.killingAll
|
||||
? 'border-status-error bg-status-error text-dark-bg cursor-not-allowed'
|
||||
: 'border-dark-border bg-dark-bg-tertiary text-status-error hover:border-status-error hover:bg-dark-bg-secondary';
|
||||
|
||||
return html`
|
||||
<button
|
||||
class="${buttonClass} ${stateClass}"
|
||||
@click=${this.handleKillAll}
|
||||
?disabled=${this.killingAll}
|
||||
>
|
||||
${
|
||||
this.killingAll
|
||||
? html`
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<div
|
||||
class="w-3 h-3 border-2 border-dark-bg border-t-transparent rounded-full animate-spin"
|
||||
></div>
|
||||
<span>Killing...</span>
|
||||
</div>
|
||||
`
|
||||
: `Kill All (${runningSessions.length})`
|
||||
}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderCompactUserMenu() {
|
||||
// When no user (no-auth mode), show just a settings icon
|
||||
if (!this.currentUser) {
|
||||
|
|
@ -179,7 +101,6 @@ export class SidebarHeader extends HeaderBase {
|
|||
class="w-full text-left px-3 py-1.5 text-xs font-mono text-dark-text hover:bg-dark-bg-secondary"
|
||||
@click=${(e: Event) => {
|
||||
e.stopPropagation();
|
||||
console.log('🔧 Settings button clicked in sidebar header');
|
||||
this.handleOpenSettings();
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
* using the node-pty library while maintaining compatibility with tty-fwd.
|
||||
*/
|
||||
|
||||
import type { IPty } from '@homebridge/node-pty-prebuilt-multiarch';
|
||||
import * as pty from '@homebridge/node-pty-prebuilt-multiarch';
|
||||
import chalk from 'chalk';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as fs from 'fs';
|
||||
import * as net from 'net';
|
||||
import type { IPty } from 'node-pty';
|
||||
import * as pty from 'node-pty';
|
||||
import * as path from 'path';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import type {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
* These types match the tty-fwd format to ensure compatibility
|
||||
*/
|
||||
|
||||
import type { IPty } from '@homebridge/node-pty-prebuilt-multiarch';
|
||||
import type * as fs from 'fs';
|
||||
import type * as net from 'net';
|
||||
import type { IPty } from 'node-pty';
|
||||
import type { SessionInfo } from '../../shared/types.js';
|
||||
import type { AsciinemaWriter } from './asciinema-writer.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ if (!globalThis.crypto) {
|
|||
}
|
||||
|
||||
// Mock the native pty module before any imports
|
||||
vi.mock('@homebridge/node-pty-prebuilt-multiarch', () => ({
|
||||
vi.mock('node-pty', () => ({
|
||||
spawn: vi.fn(() => ({
|
||||
pid: 12345,
|
||||
cols: 80,
|
||||
|
|
|
|||
Loading…
Reference in a new issue