Fix npm package issues and update dependencies for beta 15.2 (#494)

This commit is contained in:
Peter Steinberger 2025-08-02 17:48:31 +02:00 committed by GitHub
parent 1ee6d50bab
commit dd4db5ab07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 288 additions and 101 deletions

3
.grok/settings.json Normal file
View file

@ -0,0 +1,3 @@
{
"model": "grok-4-latest"
}

191
docs/npm-release.md Normal file
View file

@ -0,0 +1,191 @@
# NPM Release Checklist
This checklist ensures a smooth and error-free npm release process for VibeTunnel.
## Pre-Release Checklist
### 1. Code Quality
- [ ] Run all tests: `pnpm test`
- [ ] Run linting: `pnpm run lint`
- [ ] Run type checking: `pnpm run typecheck`
- [ ] Run format check: `pnpm run format:check`
- [ ] Fix any issues found: `pnpm run check:fix`
### 2. Dependency Updates
- [ ] Update all dependencies to latest versions
- [ ] Run `pnpm update --interactive --latest`
- [ ] Test thoroughly after updates
- [ ] Check for security vulnerabilities: `pnpm audit`
### 3. Version Updates (CRITICAL - Must be synchronized!)
- [ ] Update version in `web/package.json`
- [ ] Update version in `web/package.npm.json` (must match!)
- [ ] Update version in `mac/VibeTunnel/version.xcconfig` (MARKETING_VERSION)
- [ ] Update version in `ios/VibeTunnel/version.xcconfig` (if applicable)
- [ ] Ensure all versions match exactly
### 4. Changelog
- [ ] Update CHANGELOG.md with new features, fixes, and breaking changes
- [ ] Include migration guide for breaking changes
- [ ] Credit contributors
## Build Process
### 5. Clean Build
- [ ] Clean previous builds: `rm -rf dist-npm/ vibetunnel-*.tgz`
- [ ] Run build: `pnpm run build:npm`
- [ ] Verify build output shows all platforms built successfully
- [ ] Check for "✅ authenticate-pam listed as optional dependency" in output
### 6. Package Verification (CRITICAL)
- [ ] Verify tarball exists: `ls -la vibetunnel-*.tgz`
- [ ] Extract package.json: `tar -xf vibetunnel-*.tgz package/package.json`
- [ ] Verify authenticate-pam is OPTIONAL:
```bash
grep -A5 -B5 authenticate-pam package/package.json
# Must show under "optionalDependencies", NOT "dependencies"
```
- [ ] Clean up: `rm -rf package/`
- [ ] Check package size is reasonable (~8-15 MB)
### 7. Package Contents Verification
- [ ] List package contents: `tar -tzf vibetunnel-*.tgz | head -50`
- [ ] Verify critical files are included:
- [ ] `package/lib/vibetunnel-cli`
- [ ] `package/lib/cli.js`
- [ ] `package/bin/vibetunnel`
- [ ] `package/bin/vt`
- [ ] `package/scripts/postinstall.js`
- [ ] `package/scripts/install-vt-command.js`
- [ ] `package/node-pty/` directory
- [ ] `package/prebuilds/` directory with .tar.gz files
- [ ] `package/public/` directory
## Testing
### 8. Local Installation Test
- [ ] Test installation: `npm install -g ./vibetunnel-*.tgz`
- [ ] Verify version: `vibetunnel --version`
- [ ] Start server: `vibetunnel`
- [ ] Access web UI: http://localhost:4020
- [ ] Test vt command: `vt echo "test"`
- [ ] Uninstall: `npm uninstall -g vibetunnel`
### 9. Docker Test (Linux Compatibility)
- [ ] Create test Dockerfile:
```dockerfile
FROM node:20-slim
COPY vibetunnel-*.tgz /tmp/
RUN npm install -g /tmp/vibetunnel-*.tgz
CMD ["vibetunnel", "--version"]
```
- [ ] Build: `docker build -t vt-test .`
- [ ] Run: `docker run --rm vt-test`
- [ ] Test without PAM headers (should succeed)
- [ ] Test with PAM: Add `RUN apt-get update && apt-get install -y libpam0g-dev` before install
### 10. Cross-Platform Testing
- [ ] Test on macOS (if available)
- [ ] Test on Linux x64
- [ ] Test on Linux ARM64 (if available)
- [ ] Verify prebuilds are used (no compilation during install)
## Publishing
### 11. Pre-Publish Checks
- [ ] Ensure you're logged in to npm: `npm whoami`
- [ ] Check current tags: `npm dist-tag ls vibetunnel`
- [ ] Verify no uncommitted changes: `git status`
- [ ] Create git tag: `git tag v1.0.0-beta.X`
### 12. Publish (CRITICAL - Use tarball filename!)
- [ ] Publish beta: `npm publish vibetunnel-*.tgz --tag beta`
- [ ] Verify on npm: https://www.npmjs.com/package/vibetunnel
- [ ] Test installation from npm: `npm install -g vibetunnel@beta`
### 13. Post-Publish Verification
- [ ] Check package page shows correct version
- [ ] Verify optional dependencies are displayed correctly
- [ ] Test installation on clean system
- [ ] Monitor npm downloads and issues
### 14. Promotion to Latest (if stable)
- [ ] Wait for user feedback (at least 24 hours)
- [ ] If stable, promote: `npm dist-tag add vibetunnel@VERSION latest`
- [ ] Update documentation to reference new version
## Post-Release
### 15. Documentation Updates
- [ ] Update README.md with new version info
- [ ] Update installation instructions if needed
- [ ] Update web/docs/npm.md release history
- [ ] Create GitHub release with changelog
### 16. Communication
- [ ] Announce release on relevant channels
- [ ] Notify users of breaking changes
- [ ] Thank contributors
## Emergency Procedures
### If Wrong package.json Was Used
1. **DO NOT PANIC**
2. Check if authenticate-pam is a regular dependency (bad) or optional (good)
3. If bad, deprecate immediately:
```bash
npm deprecate vibetunnel@VERSION "Installation issues on Linux. Use next version."
```
4. Increment version and republish following this checklist
### If Build Failed to Include Files
1. Check build logs for errors
2. Verify all copy operations in build-npm.js succeeded
3. Ensure no .gitignore or .npmignore is excluding files
4. Rebuild with verbose logging if needed
## Common Issues to Avoid
1. **NEVER use `npm publish` without tarball filename** - it may rebuild with wrong config
2. **ALWAYS verify authenticate-pam is optional** before publishing
3. **ALWAYS sync versions** across all config files
4. **NEVER skip the Docker test** - it catches Linux issues
5. **ALWAYS use beta tag first** - easier to fix issues before promoting to latest
## Version Numbering
- Beta releases: `1.0.0-beta.X` where X increments
- Patch releases: `1.0.0-beta.X.Y` where Y is patch number
- Stable releases: `1.0.0` (no beta suffix)
## Quick Commands Reference
```bash
# Update dependencies
pnpm update --interactive --latest
# Build
pnpm run build:npm
# Verify
tar -xf vibetunnel-*.tgz package/package.json && \
grep -A5 optionalDependencies package/package.json && \
rm -rf package/
# Publish
npm publish vibetunnel-*.tgz --tag beta
# Promote to latest
npm dist-tag add vibetunnel@VERSION latest
# Check tags
npm dist-tag ls vibetunnel
```
## Release Frequency
- Beta releases: As needed for testing new features
- Stable releases: After thorough testing and user feedback
- Security patches: ASAP after discovery
Remember: It's better to delay a release than to publish a broken package!

View file

@ -1,7 +1,7 @@
// VibeTunnel Version Configuration
// This file contains the version and build number for the app
MARKETING_VERSION = 1.0.0-beta.7
MARKETING_VERSION = 1.0.0-beta.15.2
CURRENT_PROJECT_VERSION = 160
// Domain and GitHub configuration

View file

@ -1,7 +1,7 @@
// VibeTunnel Version Configuration
// This file contains the version and build number for the app
MARKETING_VERSION = 1.0.0-beta.16
MARKETING_VERSION = 1.0.0-beta.15.2
CURRENT_PROJECT_VERSION = 206
// Domain and GitHub configuration

View file

@ -1,6 +1,6 @@
{
"name": "vibetunnel",
"version": "1.0.0-beta.15.1",
"version": "1.0.0-beta.15.2",
"description": "Terminal sharing server with web interface - supports macOS, Linux, and headless environments",
"main": "dist/server/server.js",
"bin": {
@ -96,7 +96,7 @@
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-markdown": "^6.3.3",
"@codemirror/lang-markdown": "^6.3.4",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/state": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.3",
@ -124,7 +124,7 @@
"devDependencies": {
"@biomejs/biome": "^2.1.3",
"@open-wc/testing": "^4.0.0",
"@playwright/test": "^1.54.1",
"@playwright/test": "^1.54.2",
"@prettier/plugin-oxc": "^0.0.4",
"@tailwindcss/postcss": "^4.1.11",
"@testing-library/dom": "^10.4.1",
@ -156,7 +156,7 @@
"supertest": "^7.1.4",
"tailwindcss": "^4.1.11",
"tsx": "^4.20.3",
"typescript": "^5.8.3",
"typescript": "^5.9.2",
"uuid": "^11.1.0",
"vitest": "^3.2.4",
"ws-mock": "^0.1.0"

View file

@ -1,6 +1,6 @@
{
"name": "vibetunnel",
"version": "1.0.0-beta.15.1",
"version": "1.0.0-beta.15.2",
"description": "Terminal sharing server with web interface - supports macOS, Linux, and headless environments",
"main": "lib/cli.js",
"bin": {
@ -12,7 +12,6 @@
"bin/",
"scripts/",
"node-pty/",
"node_modules/authenticate-pam/",
"prebuilds/",
"README.md"
],
@ -41,11 +40,11 @@
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-markdown": "^6.3.3",
"@codemirror/lang-markdown": "^6.3.4",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/state": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.38.0",
"@codemirror/view": "^6.38.1",
"@xterm/headless": "^5.5.0",
"bonjour-service": "^1.3.0",
"chalk": "^5.4.1",
@ -61,7 +60,8 @@
"postject": "1.0.0-alpha.6",
"signal-exit": "^4.1.0",
"web-push": "^3.6.7",
"ws": "^8.18.3"
"ws": "^8.18.3",
"node-addon-api": "^7.1.0"
},
"optionalDependencies": {
"authenticate-pam": "^1.0.5"

View file

@ -24,8 +24,8 @@ importers:
specifier: ^6.0.2
version: 6.0.2
'@codemirror/lang-markdown':
specifier: ^6.3.3
version: 6.3.3
specifier: ^6.3.4
version: 6.3.4
'@codemirror/lang-python':
specifier: ^6.2.1
version: 6.2.1
@ -103,8 +103,8 @@ importers:
specifier: ^4.0.0
version: 4.0.0
'@playwright/test':
specifier: ^1.54.1
version: 1.54.1
specifier: ^1.54.2
version: 1.54.2
'@prettier/plugin-oxc':
specifier: ^0.0.4
version: 0.0.4
@ -188,7 +188,7 @@ importers:
version: 3.6.2
puppeteer:
specifier: ^24.15.0
version: 24.15.0(typescript@5.8.3)
version: 24.15.0(typescript@5.9.2)
supertest:
specifier: ^7.1.4
version: 7.1.4
@ -199,8 +199,8 @@ importers:
specifier: ^4.20.3
version: 4.20.3
typescript:
specifier: ^5.8.3
version: 5.8.3
specifier: ^5.9.2
version: 5.9.2
uuid:
specifier: ^11.1.0
version: 11.1.0
@ -321,8 +321,8 @@ packages:
'@codemirror/lang-json@6.0.2':
resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==}
'@codemirror/lang-markdown@6.3.3':
resolution: {integrity: sha512-1fn1hQAPWlSSMCvnF810AkhWpNLkJpl66CRfIy3vVl20Sl4NwChkorCHqpMtNbXr1EuMJsrDnhEpjZxKZ2UX3A==}
'@codemirror/lang-markdown@6.3.4':
resolution: {integrity: sha512-fBm0BO03azXnTAsxhONDYHi/qWSI+uSEIpzKM7h/bkIc9fHnFp9y7KTMXKON0teNT97pFhc1a9DQTtWBYEZ7ug==}
'@codemirror/lang-python@6.2.1':
resolution: {integrity: sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==}
@ -710,8 +710,8 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@playwright/test@1.54.1':
resolution: {integrity: sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==}
'@playwright/test@1.54.2':
resolution: {integrity: sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==}
engines: {node: '>=18'}
hasBin: true
@ -1653,8 +1653,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.192:
resolution: {integrity: sha512-rP8Ez0w7UNw/9j5eSXCe10o1g/8B1P5SM90PCCMVkIRQn2R0LEHWz4Eh9RnxkniuDe1W0cTSOB3MLlkTGDcuCg==}
electron-to-chromium@1.5.194:
resolution: {integrity: sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==}
emoji-regex@10.4.0:
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
@ -1825,8 +1825,8 @@ packages:
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
follow-redirects@1.15.11:
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
@ -2682,13 +2682,13 @@ packages:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
playwright-core@1.54.1:
resolution: {integrity: sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==}
playwright-core@1.54.2:
resolution: {integrity: sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==}
engines: {node: '>=18'}
hasBin: true
playwright@1.54.1:
resolution: {integrity: sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==}
playwright@1.54.2:
resolution: {integrity: sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==}
engines: {node: '>=18'}
hasBin: true
@ -3165,8 +3165,8 @@ packages:
typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
typescript@5.9.2:
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
engines: {node: '>=14.17'}
hasBin: true
@ -3516,7 +3516,7 @@ snapshots:
'@codemirror/language': 6.11.2
'@lezer/json': 1.0.3
'@codemirror/lang-markdown@6.3.3':
'@codemirror/lang-markdown@6.3.4':
dependencies:
'@codemirror/autocomplete': 6.18.6
'@codemirror/lang-html': 6.4.9
@ -3863,9 +3863,9 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@playwright/test@1.54.1':
'@playwright/test@1.54.2':
dependencies:
playwright: 1.54.1
playwright: 1.54.2
'@polka/url@1.0.0-next.29': {}
@ -4537,7 +4537,7 @@ snapshots:
browserslist@4.25.1:
dependencies:
caniuse-lite: 1.0.30001731
electron-to-chromium: 1.5.192
electron-to-chromium: 1.5.194
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.25.1)
@ -4745,14 +4745,14 @@ snapshots:
depd: 2.0.0
keygrip: 1.1.0
cosmiconfig@9.0.0(typescript@5.8.3):
cosmiconfig@9.0.0(typescript@5.9.2):
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.1
js-yaml: 4.1.0
parse-json: 5.2.0
optionalDependencies:
typescript: 5.8.3
typescript: 5.9.2
crelt@1.0.6: {}
@ -4847,7 +4847,7 @@ snapshots:
ee-first@1.1.1: {}
electron-to-chromium@1.5.192: {}
electron-to-chromium@1.5.194: {}
emoji-regex@10.4.0: {}
@ -5064,7 +5064,7 @@ snapshots:
flatted@3.3.3: {}
follow-redirects@1.15.9(debug@4.4.1):
follow-redirects@1.15.11(debug@4.4.1):
optionalDependencies:
debug: 4.4.1
@ -5250,7 +5250,7 @@ snapshots:
http-proxy@1.18.1(debug@4.4.1):
dependencies:
eventemitter3: 4.0.7
follow-redirects: 1.15.9(debug@4.4.1)
follow-redirects: 1.15.11(debug@4.4.1)
requires-port: 1.0.0
transitivePeerDependencies:
- debug
@ -5916,11 +5916,11 @@ snapshots:
pify@2.3.0: {}
playwright-core@1.54.1: {}
playwright-core@1.54.2: {}
playwright@1.54.1:
playwright@1.54.2:
dependencies:
playwright-core: 1.54.1
playwright-core: 1.54.2
optionalDependencies:
fsevents: 2.3.2
@ -6020,11 +6020,11 @@ snapshots:
- supports-color
- utf-8-validate
puppeteer@24.15.0(typescript@5.8.3):
puppeteer@24.15.0(typescript@5.9.2):
dependencies:
'@puppeteer/browsers': 2.10.6
chromium-bidi: 7.2.0(devtools-protocol@0.0.1464554)
cosmiconfig: 9.0.0(typescript@5.8.3)
cosmiconfig: 9.0.0(typescript@5.9.2)
devtools-protocol: 0.0.1464554
puppeteer-core: 24.15.0
typed-query-selector: 2.12.0
@ -6477,7 +6477,7 @@ snapshots:
typedarray@0.0.6: {}
typescript@5.8.3: {}
typescript@5.9.2: {}
undici-types@6.21.0: {}

View file

@ -525,19 +525,18 @@ function validatePackageHybrid() {
console.log(' ⚠️ Prebuilds skipped in current-only mode');
}
// Check authenticate-pam (Linux support)
const authPamDir = path.join(DIST_DIR, 'node_modules', 'authenticate-pam');
if (!fs.existsSync(authPamDir)) {
warnings.push('authenticate-pam module not included (Linux PAM auth will not work)');
} else {
console.log(' ✅ authenticate-pam module included for Linux support');
}
// Validate package.json
const packageJsonPath = path.join(DIST_DIR, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
// Check authenticate-pam is listed as optionalDependency
if (packageJson.optionalDependencies && packageJson.optionalDependencies['authenticate-pam']) {
console.log(' ✅ authenticate-pam listed as optional dependency');
} else {
warnings.push('authenticate-pam not listed as optional dependency (Linux PAM auth may not work)');
}
// Check postinstall script
if (!packageJson.scripts || !packageJson.scripts.postinstall) {
errors.push('Missing postinstall script in package.json');
@ -667,8 +666,8 @@ async function main() {
// Step 4: Bundle node-pty with dependencies
bundleNodePty();
// Step 5: Copy authenticate-pam module for Linux support (OUR ENHANCEMENT)
copyAuthenticatePam();
// Step 5: Don't copy authenticate-pam - it's an optionalDependency that will be installed by npm
// copyAuthenticatePam();
// Step 6: Use package.npm.json if available, otherwise create clean package.json
console.log('\n6⃣ Creating package.json for npm...\n');

View file

@ -880,12 +880,12 @@ describe('SessionCreateForm', () => {
// Set up working directory and command first
element.workingDir = '/home/user/project';
element.command = 'vim';
// Trigger Git repository check which will load currentBranch and selectedBaseBranch
// @ts-expect-error - accessing private method for testing
await element.checkGitRepository();
await element.updateComplete;
// The Git check should have loaded the repository info and set currentBranch to 'main'
// and selectedBaseBranch should also be 'main' (current branch is selected by default)
// This ensures the Git info will be included in the session creation request

View file

@ -315,7 +315,7 @@ export class PushNotificationService {
// Subscribe to push notifications
this.pushSubscription = await this.serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: vapidKey,
applicationServerKey: vapidKey as BufferSource,
});
// Convert to our interface format

View file

@ -300,42 +300,27 @@ branch refs/heads/feature
});
});
describe('POST /api/worktrees/switch', () => {
it('should switch branch and enable follow mode', async () => {
mockExecFile.mockResolvedValueOnce({ stdout: '', stderr: '' }); // status (no uncommitted changes)
mockExecFile.mockResolvedValueOnce({ stdout: '', stderr: '' }); // checkout
mockExecFile.mockResolvedValueOnce({ stdout: '', stderr: '' }); // config
const response = await request(app).post('/api/worktrees/switch').send({
repoPath: '/home/user/project',
branch: 'develop',
});
expect(response.status).toBe(200);
expect(response.body.currentBranch).toBe('develop');
expect(mockExecFile).toHaveBeenCalledWith(
'git',
['checkout', 'develop'],
expect.objectContaining({ cwd: '/home/user/project' })
);
expect(mockExecFile).toHaveBeenCalledWith(
'git',
['config', '--local', 'vibetunnel.followWorktree', '/path/to/worktree'],
expect.objectContaining({ cwd: '/home/user/project' })
);
});
it('should handle missing parameters', async () => {
const response = await request(app).post('/api/worktrees/switch').send({});
expect(response.status).toBe(400);
});
});
describe('POST /api/worktrees/follow', () => {
it('should enable follow mode', async () => {
// Mock worktree list to find the path for the branch
mockExecFile.mockResolvedValueOnce({
stdout: `worktree /home/user/project
HEAD abc123
branch refs/heads/main
`,
stderr: '',
});
// Mock setting git config for follow branch
mockExecFile.mockResolvedValueOnce({ stdout: '', stderr: '' }); // config set
// Mock branch list check
mockExecFile.mockResolvedValueOnce({ stdout: '* main\n', stderr: '' });
// Mock checkout
mockExecFile.mockResolvedValueOnce({ stdout: '', stderr: '' });
const response = await request(app).post('/api/worktrees/follow').send({
repoPath: '/home/user/project',
branch: 'main',

View file

@ -35,19 +35,27 @@ export interface ServerStatus {
*/
export class SocketApiClient {
private readonly controlSocketPath: string;
private readonly controlDir: string;
constructor() {
// Use control directory from environment or default
const controlDir = process.env.VIBETUNNEL_CONTROL_DIR || path.join(os.homedir(), '.vibetunnel');
this.controlDir = process.env.VIBETUNNEL_CONTROL_DIR || path.join(os.homedir(), '.vibetunnel');
// Use api.sock instead of control.sock to avoid conflicts with Mac app
this.controlSocketPath = path.join(controlDir, 'api.sock');
this.controlSocketPath = path.join(this.controlDir, 'api.sock');
logger.debug(`SocketApiClient initialized with control directory: ${this.controlDir}`);
logger.debug(`Socket path: ${this.controlSocketPath}`);
}
/**
* Check if the control socket exists
*/
private isSocketAvailable(): boolean {
return fs.existsSync(this.controlSocketPath);
const available = fs.existsSync(this.controlSocketPath);
logger.debug(
`Socket availability check: ${this.controlSocketPath} - ${available ? 'available' : 'not available'}`
);
return available;
}
/**

View file

@ -4,6 +4,7 @@ import { MultiplexerManager } from '../../server/services/multiplexer-manager.js
import { ScreenManager } from '../../server/services/screen-manager.js';
import { TmuxManager } from '../../server/services/tmux-manager.js';
import { ZellijManager } from '../../server/services/zellij-manager.js';
import type { MultiplexerType } from '../../shared/multiplexer-types.js';
import { TitleMode } from '../../shared/types.js';
// Mock the managers
@ -191,7 +192,7 @@ describe('MultiplexerManager', () => {
it('should throw error for unknown multiplexer type', async () => {
await expect(
multiplexerManager.createSession('unknown' as any, 'new-session')
multiplexerManager.createSession('unknown' as unknown as MultiplexerType, 'new-session')
).rejects.toThrow('Unknown multiplexer type: unknown');
});
});
@ -250,9 +251,9 @@ describe('MultiplexerManager', () => {
});
it('should throw error for unknown multiplexer type', async () => {
await expect(multiplexerManager.attachToSession('unknown' as any, 'main')).rejects.toThrow(
'Unknown multiplexer type: unknown'
);
await expect(
multiplexerManager.attachToSession('unknown' as unknown as MultiplexerType, 'main')
).rejects.toThrow('Unknown multiplexer type: unknown');
});
});
@ -280,9 +281,9 @@ describe('MultiplexerManager', () => {
});
it('should throw error for unknown multiplexer type', async () => {
await expect(multiplexerManager.killSession('unknown' as any, 'old-session')).rejects.toThrow(
'Unknown multiplexer type: unknown'
);
await expect(
multiplexerManager.killSession('unknown' as unknown as MultiplexerType, 'old-session')
).rejects.toThrow('Unknown multiplexer type: unknown');
});
});