Class: Ace::Assign::Molecules::TmuxForkRunner

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/assign/molecules/tmux_fork_runner.rb

Overview

Minimal tmux integration for forked subtree execution.

Defined Under Namespace

Classes: Result

Constant Summary collapse

DEFAULT_POLL_INTERVAL =
0.2

Instance Method Summary collapse

Constructor Details

#initialize(tmux_binary: "tmux") ⇒ TmuxForkRunner

Returns a new instance of TmuxForkRunner.



15
16
17
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 15

def initialize(tmux_binary: "tmux")
  @tmux_binary = tmux_binary
end

Instance Method Details

#current_sessionObject



23
24
25
26
27
28
29
30
31
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 23

def current_session
  explicit = ENV["ACE_TMUX_SESSION"].to_s.strip
  return explicit unless explicit.empty?
  return nil if ENV["TMUX"].to_s.strip.empty?

  capture([tmux_binary, "display-message", "-p", "#S"]).stdout
rescue
  nil
end

#current_windowObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 33

def current_window
  explicit = ENV["ACE_ASSIGN_FORK_WINDOW"].to_s.strip
  return explicit unless explicit.empty?
  session = ENV["ACE_TMUX_SESSION"].to_s.strip
  if !session.empty?
    window = capture([tmux_binary, "display-message", "-t", "#{session}:", "-p", "#W"]).stdout
    return window unless window.empty?

    return active_window_name(session)
  end
  return nil if ENV["TMUX"].to_s.strip.empty?

  capture([tmux_binary, "display-message", "-p", "#W"]).stdout
rescue
  nil
end

#ensure_window(session:, name:, root:) ⇒ Object

Raises:



57
58
59
60
61
62
63
64
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 57

def ensure_window(session:, name:, root:)
  return {created: false, target: "#{session}:#{name}"} if window_exists?(session: session, name: name)

  result = capture([tmux_binary, "new-window", "-t", "#{session}:", "-n", name, "-c", File.expand_path(root)])
  raise Error, "Failed to create tmux fork window #{name}: #{result.stderr}" unless result.success?

  {created: true, target: "#{session}:#{name}"}
end

#fork_window_name(base_window) ⇒ Object



50
51
52
53
54
55
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 50

def fork_window_name(base_window)
  base = base_window.to_s.strip
  return base if base.end_with?("-fs")

  "#{base}-fs"
end

#merge_tmux_metadata(session_meta_file:, session:, window:, pane:) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 109

def (session_meta_file:, session:, window:, pane:)
  data = if File.exist?(session_meta_file)
    YAML.safe_load_file(session_meta_file) || {}
  else
    {}
  end
  data["launch_mode"] = "tmux"
  data["tmux_session"] = session
  data["tmux_window"] = window
  data["tmux_pane_id"] = pane
  File.write(session_meta_file, data.to_yaml)
end

#prepare_pane(session:, window:, root:, keep_existing:) ⇒ Object

Raises:



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 66

def prepare_pane(session:, window:, root:, keep_existing:)
  target = "#{session}:#{window}"
  if keep_existing
    pane = first_pane(target)
    set_pane_remain_on_exit(pane)
    select_layout(target)
    return pane
  end

  result = capture([tmux_binary, "split-window", "-t", target, "-c", File.expand_path(root), "-P", "-F", '#{pane_id}'])
  raise Error, "Failed to create tmux fork pane in #{window}: #{result.stderr}" unless result.success?

  pane = result.stdout
  set_pane_remain_on_exit(pane)
  select_layout(target)
  pane
end

#run_script_in_pane(pane_target:, script_path:) ⇒ Object



88
89
90
91
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 88

def run_script_in_pane(pane_target:, script_path:)
  command = "bash #{Shellwords.escape(File.expand_path(script_path))}"
  run!([tmux_binary, "send-keys", "-t", pane_target, command, "Enter"], "send tmux fork command")
end

#select_window(session:, window:) ⇒ Object



84
85
86
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 84

def select_window(session:, window:)
  run!([tmux_binary, "select-window", "-t", "#{session}:#{window}"], "select tmux fork window #{window}")
end

#tmux_context?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 19

def tmux_context?
  !current_session.to_s.strip.empty?
end

#wait_for_completion(status_file:, timeout:) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ace/assign/molecules/tmux_fork_runner.rb', line 93

def wait_for_completion(status_file:, timeout:)
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout.to_i
  until File.exist?(status_file)
    if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
      raise Error, "Timed out waiting for tmux fork pane to finish (#{File.basename(status_file)})"
    end

    sleep(DEFAULT_POLL_INTERVAL)
  end

  status = File.read(status_file).strip
  Integer(status)
rescue ArgumentError
  raise Error, "Invalid tmux fork status file: #{status_file}"
end