mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-26 09:35:52 +00:00
Work on new session logic
This commit is contained in:
parent
1519fbca16
commit
22eb5a2e3f
7 changed files with 128 additions and 40 deletions
|
|
@ -504,8 +504,22 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Attach to the session
|
||||
if err := sess.Attach(); err != nil {
|
||||
// For spawned sessions, we need to execute the command and connect I/O
|
||||
// The session was already created by the server, we just need to run the command
|
||||
info := sess.GetInfo()
|
||||
if info == nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: Failed to get session info\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Execute the command that was stored in the session
|
||||
if len(info.Args) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "Error: No command specified in session\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create a new PTY and attach it to the existing session
|
||||
if err := sess.AttachSpawnedSession(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -380,11 +380,11 @@ func (s *Server) handleCreateSession(w http.ResponseWriter, r *http.Request) {
|
|||
// Generate a session ID
|
||||
sessionID := session.GenerateID()
|
||||
|
||||
// Get vt binary path (not vibetunnel)
|
||||
// Get vibetunnel binary path
|
||||
vtPath := findVTBinary()
|
||||
if vtPath == "" {
|
||||
log.Printf("[ERROR] vt binary not found")
|
||||
http.Error(w, "vt binary not found", http.StatusInternalServerError)
|
||||
log.Printf("[ERROR] vibetunnel binary not found")
|
||||
http.Error(w, "vibetunnel binary not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -466,18 +466,38 @@ func (s *Server) handleCreateSession(w http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to create session: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
||||
// Return structured error response for frontends to parse
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
errorResponse := map[string]interface{}{
|
||||
"success": false,
|
||||
"error": err.Error(),
|
||||
"details": fmt.Sprintf("Failed to create session with command '%s'", strings.Join(cmdline, " ")),
|
||||
}
|
||||
|
||||
// Extract more specific error information if available
|
||||
if sessionErr, ok := err.(*session.SessionError); ok {
|
||||
errorResponse["code"] = string(sessionErr.Code)
|
||||
if sessionErr.Code == session.ErrPTYCreationFailed {
|
||||
errorResponse["details"] = sessionErr.Message
|
||||
}
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(errorResponse); err != nil {
|
||||
log.Printf("Failed to encode error response: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get vt binary path (not vibetunnel)
|
||||
// Get vibetunnel binary path
|
||||
vtPath := findVTBinary()
|
||||
if vtPath == "" {
|
||||
log.Printf("[ERROR] vt binary not found for native terminal spawn")
|
||||
log.Printf("[ERROR] vibetunnel binary not found for native terminal spawn")
|
||||
if err := s.manager.RemoveSession(sess.ID); err != nil {
|
||||
log.Printf("Failed to remove session: %v", err)
|
||||
}
|
||||
http.Error(w, "vt binary not found", http.StatusInternalServerError)
|
||||
http.Error(w, "vibetunnel binary not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -518,7 +538,28 @@ func (s *Server) handleCreateSession(w http.ResponseWriter, r *http.Request) {
|
|||
IsSpawned: false, // This is not a spawned session (detached)
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Printf("[ERROR] Failed to create session: %v", err)
|
||||
|
||||
// Return structured error response for frontends to parse
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
errorResponse := map[string]interface{}{
|
||||
"success": false,
|
||||
"error": err.Error(),
|
||||
"details": fmt.Sprintf("Failed to create session with command '%s'", strings.Join(cmdline, " ")),
|
||||
}
|
||||
|
||||
// Extract more specific error information if available
|
||||
if sessionErr, ok := err.(*session.SessionError); ok {
|
||||
errorResponse["code"] = string(sessionErr.Code)
|
||||
if sessionErr.Code == session.ErrPTYCreationFailed {
|
||||
errorResponse["details"] = sessionErr.Message
|
||||
}
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(errorResponse); err != nil {
|
||||
log.Printf("Failed to encode error response: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1028,29 +1069,27 @@ func (s *Server) GetNgrokStatus() ngrok.StatusResponse {
|
|||
return s.ngrokService.GetStatus()
|
||||
}
|
||||
|
||||
// findVTBinary locates the vt binary in common locations
|
||||
// findVTBinary locates the vibetunnel Go binary in common locations
|
||||
func findVTBinary() string {
|
||||
// Get the directory of the current executable (vibetunnel)
|
||||
execPath, err := os.Executable()
|
||||
if err == nil {
|
||||
execDir := filepath.Dir(execPath)
|
||||
// Check if vt is in the same directory as vibetunnel
|
||||
vtPath := filepath.Join(execDir, "vt")
|
||||
if _, err := os.Stat(vtPath); err == nil {
|
||||
return vtPath
|
||||
}
|
||||
// Return the current executable path since we want to use vibetunnel itself
|
||||
return execPath
|
||||
}
|
||||
|
||||
// Check common locations
|
||||
paths := []string{
|
||||
// App bundle location
|
||||
"/Applications/VibeTunnel.app/Contents/Resources/vt",
|
||||
"/Applications/VibeTunnel.app/Contents/Resources/vibetunnel",
|
||||
// Development locations
|
||||
"./vt",
|
||||
"../vt",
|
||||
"../../linux/vt",
|
||||
"./linux/cmd/vibetunnel/vibetunnel",
|
||||
"../linux/cmd/vibetunnel/vibetunnel",
|
||||
"../../linux/cmd/vibetunnel/vibetunnel",
|
||||
"./vibetunnel",
|
||||
"../vibetunnel",
|
||||
// Installed location
|
||||
"/usr/local/bin/vt",
|
||||
"/usr/local/bin/vibetunnel",
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
|
|
@ -1061,10 +1100,11 @@ func findVTBinary() string {
|
|||
}
|
||||
|
||||
// Try to find in PATH
|
||||
if path, err := exec.LookPath("vt"); err == nil {
|
||||
if path, err := exec.LookPath("vibetunnel"); err == nil {
|
||||
return path
|
||||
}
|
||||
|
||||
// No binary found
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,12 +53,20 @@ func NewPTY(session *Session) (*PTY, error) {
|
|||
// If just launching the shell itself, don't use -c
|
||||
cmd = exec.Command(shell)
|
||||
} else {
|
||||
// Execute command through interactive login shell for proper environment handling
|
||||
// Execute command through login shell for proper environment handling
|
||||
// This ensures aliases and functions from .zshrc/.bashrc are loaded
|
||||
shellCmd := strings.Join(cmdline, " ")
|
||||
// Use interactive login shell to load user's configuration
|
||||
cmd = exec.Command(shell, "-i", "-l", "-c", shellCmd)
|
||||
debugLog("[DEBUG] NewPTY: Executing through interactive login shell: %s -i -l -c %q", shell, shellCmd)
|
||||
|
||||
// Try different approaches based on the shell
|
||||
if strings.Contains(shell, "zsh") {
|
||||
// For zsh, use login shell without -i flag as it can cause issues with PTY allocation
|
||||
cmd = exec.Command(shell, "-l", "-c", shellCmd)
|
||||
debugLog("[DEBUG] NewPTY: Executing through zsh login shell: %s -l -c %q", shell, shellCmd)
|
||||
} else {
|
||||
// For other shells (bash, sh), use interactive login
|
||||
cmd = exec.Command(shell, "-i", "-l", "-c", shellCmd)
|
||||
debugLog("[DEBUG] NewPTY: Executing through interactive login shell: %s -i -l -c %q", shell, shellCmd)
|
||||
}
|
||||
|
||||
// Add some debugging to understand what's happening
|
||||
debugLog("[DEBUG] NewPTY: Shell: %s", shell)
|
||||
|
|
@ -85,6 +93,14 @@ func NewPTY(session *Session) (*PTY, error) {
|
|||
// Pass all environment variables like Node.js implementation does
|
||||
// This ensures terminal features, locale settings, and shell prompts work correctly
|
||||
env := os.Environ()
|
||||
|
||||
// Log PATH for debugging
|
||||
for _, e := range env {
|
||||
if strings.HasPrefix(e, "PATH=") {
|
||||
debugLog("[DEBUG] NewPTY: PATH=%s", e[5:])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Override TERM if specified in session info
|
||||
termSet := false
|
||||
|
|
|
|||
|
|
@ -265,6 +265,25 @@ func (s *Session) Attach() error {
|
|||
return s.pty.Attach()
|
||||
}
|
||||
|
||||
// AttachSpawnedSession is used when a terminal is spawned with TTY_SESSION_ID
|
||||
// It creates a new PTY for the spawned terminal and runs the command
|
||||
func (s *Session) AttachSpawnedSession() error {
|
||||
// Create a new PTY for this spawned session
|
||||
pty, err := NewPTY(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create PTY: %w", err)
|
||||
}
|
||||
s.pty = pty
|
||||
|
||||
// Start the PTY with the command from session info
|
||||
if err := s.pty.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start PTY: %w", err)
|
||||
}
|
||||
|
||||
// Attach to the PTY to connect stdin/stdout
|
||||
return s.pty.Attach()
|
||||
}
|
||||
|
||||
func (s *Session) SendKey(key string) error {
|
||||
return s.sendInput([]byte(key))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import (
|
|||
// This is used as a fallback when the Mac app's terminal service is not available
|
||||
func SpawnInTerminal(sessionID, vtBinaryPath string, cmdline []string, workingDir string) error {
|
||||
// Format the command to run in the terminal
|
||||
// This matches the format used by the Rust implementation
|
||||
vtCommand := fmt.Sprintf("TTY_SESSION_ID=\"%s\" \"%s\" -- %s",
|
||||
// Using the Go vibetunnel binary with TTY_SESSION_ID environment variable
|
||||
vtCommand := fmt.Sprintf("TTY_SESSION_ID=\"%s\" \"%s\" %s",
|
||||
sessionID, vtBinaryPath, shellQuoteArgs(cmdline))
|
||||
|
||||
switch runtime.GOOS {
|
||||
|
|
|
|||
|
|
@ -100,15 +100,14 @@ remove_item "build/SourcePackages" "Source packages"
|
|||
remove_item "build/dmg-temp" "DMG temporary files"
|
||||
remove_item "DerivedData" "DerivedData"
|
||||
|
||||
# Clean tty-fwd Rust target (but keep the built binaries)
|
||||
# Clean Go build artifacts
|
||||
if [[ "$CLEAN_ALL" == "true" ]]; then
|
||||
remove_item "tty-fwd/target" "Rust build artifacts"
|
||||
remove_item "linux/build" "Go build artifacts"
|
||||
else
|
||||
# Keep the release binaries
|
||||
find tty-fwd/target -type f -name "*.d" -delete 2>/dev/null || true
|
||||
find tty-fwd/target -type f -name "*.rmeta" -delete 2>/dev/null || true
|
||||
find tty-fwd/target -type d -name "incremental" -exec rm -rf {} + 2>/dev/null || true
|
||||
[[ "$DRY_RUN" == "false" ]] && print_success "Cleaned Rust intermediate files"
|
||||
# Keep the release binaries but clean intermediate files
|
||||
find linux/build -type f -name "*.a" -delete 2>/dev/null || true
|
||||
find linux/build -type f -name "*.o" -delete 2>/dev/null || true
|
||||
[[ "$DRY_RUN" == "false" ]] && print_success "Cleaned Go intermediate files"
|
||||
fi
|
||||
|
||||
# Clean SPM build artifacts
|
||||
|
|
|
|||
|
|
@ -93,10 +93,10 @@ if [ -d "$APP_BUNDLE/Contents/Frameworks" ]; then
|
|||
done
|
||||
fi
|
||||
|
||||
# Sign embedded binaries (like tty-fwd)
|
||||
if [ -f "$APP_BUNDLE/Contents/Resources/tty-fwd" ]; then
|
||||
log "Signing tty-fwd binary..."
|
||||
codesign --force --options runtime --timestamp --sign "$SIGN_IDENTITY" $KEYCHAIN_OPTS "$APP_BUNDLE/Contents/Resources/tty-fwd" || log "Warning: Failed to sign tty-fwd"
|
||||
# Sign embedded binaries (like vibetunnel)
|
||||
if [ -f "$APP_BUNDLE/Contents/Resources/vibetunnel" ]; then
|
||||
log "Signing vibetunnel binary..."
|
||||
codesign --force --options runtime --timestamp --sign "$SIGN_IDENTITY" $KEYCHAIN_OPTS "$APP_BUNDLE/Contents/Resources/vibetunnel" || log "Warning: Failed to sign vibetunnel"
|
||||
fi
|
||||
|
||||
# Sign the main executable
|
||||
|
|
|
|||
Loading…
Reference in a new issue