ace-demo 0.25.10 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2576c7e579d985978254d6ad23cfd014dbef779cb037b1a9ebee477f2397b469
4
- data.tar.gz: b150dface3379067b1ada246a291a51a63cf974259e4a900af90ea026955310e
3
+ metadata.gz: 58391984e07317e02abd29f0fe8153841f4d616aac0849ea916b020b2c8492c3
4
+ data.tar.gz: 48f1b66626923433238798ca500cd71143b5b05df3a6e6ae5163f6d83d707ce7
5
5
  SHA512:
6
- metadata.gz: 7c961bf483a063172ffd823e4a81a4d517c07ac537c275125133994283ebe8984c30d52dd82c9c75e9ce3c67ffbf38c257eba6adc3d94a1d528b0a7ad50996ac
7
- data.tar.gz: e2e23e7267a6e6a0d7c6f1d2c982319fcaf2d60f5e6afcd7732053cfc5b588ae93c4502f21a765d7b4cdcb3bdde743a2ae10cf84214c813404afc0bfaaf7f1d7
6
+ metadata.gz: 9cdcfb904e837ace1001c4960f80150ffb957a406428a7ee82f0f21a8851b7678e9322f8e23ea4f1ad56ff86b0aa492494978589f162e8f08026f40d00797ef5
7
+ data.tar.gz: d76d42482f636e3de108eb2f6f372f6c930730b4b9bf34d416580253813f0c8e649a26debb7508ced3f569c87b3c75cb2eb164137e905266fffd4e3225e92312
data/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.26.0] - 2026-10-04
11
+
12
+ ### Added
13
+ - Demo terminal directives run through the `ace-runtime` contract (`TmuxDirectiveExecutor` replaced by `RuntimeDirectiveExecutor`): the four lifecycle wait conditions and send directives work on both adapters with identical semantics, with per-directive `runtime:` selection (directive > `ACE_RUNTIME` > configured > detection). `attach`/`detach` stay tmux-local and raise an explicit error under any other runtime.
14
+ - New dependency: `ace-runtime` (~> 0.1); `ace-tmux` is kept for the tmux adapter and detach.
15
+
16
+ ## [0.25.11] - 2026-10-02
17
+
18
+ ### Technical
19
+ - Updated the ace-tmux dependency to the 0.18 adapter release line.
20
+
10
21
 
11
22
  ## [0.25.10] - 2026-09-02
12
23
 
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/runtime"
4
+ require "ace/tmux"
5
+ require "shellwords"
6
+
7
+ module Ace
8
+ module Demo
9
+ module Molecules
10
+ # Executes demo terminal directives through the runtime contract.
11
+ # wait/send resolve via the selected runtime adapter; attach/detach
12
+ # stay tmux-local human-facing operations (k86.0 validation default).
13
+ # The YAML envelope key remains "tmux" for tape compatibility of the
14
+ # directive shape itself; wait/send never touch tmux APIs directly.
15
+ class RuntimeDirectiveExecutor
16
+ LIFECYCLE_CONDITIONS = Ace::Runtime::Atoms::SendContract::LIFECYCLE_CONDITIONS
17
+ WINDOW_CONDITIONS = %w[window-exists window-active].freeze
18
+ DEFAULT_TIMEOUT = 10.0
19
+
20
+ def initialize(runtime: nil, env: ENV, tmux_executor: nil)
21
+ @runtime_override = runtime
22
+ @env = env || ENV
23
+ @tmux_executor = tmux_executor
24
+ end
25
+
26
+ def execute(command, env = nil)
27
+ directive_env = env || @env
28
+ directive = command.fetch("tmux")
29
+ action = directive.fetch("action")
30
+
31
+ case action
32
+ when "attach"
33
+ ensure_tmux_local!(directive, directive_env, action)
34
+ session = directive.fetch("session")
35
+ {shell_command: [tmux_binary, "attach-session", "-t", session].map { |part| Shellwords.escape(part) }.join(" ")}
36
+ when "detach"
37
+ ensure_tmux_local!(directive, directive_env, action)
38
+ session = directive.fetch("session")
39
+ tmux_executor.run([tmux_binary, "detach-client", "-s", session])
40
+ nil
41
+ when "wait"
42
+ wait_directive(directive, directive_env)
43
+ nil
44
+ when "send"
45
+ send_directive(directive, directive_env)
46
+ nil
47
+ else
48
+ raise ArgumentError, "Unsupported tmux action '#{action}'"
49
+ end
50
+ rescue KeyError => e
51
+ raise ArgumentError, "Invalid tmux directive: missing #{e.key}"
52
+ rescue Ace::Runtime::Error => e
53
+ raise Ace::Demo::Error, e.message
54
+ end
55
+
56
+ private
57
+
58
+ private
59
+
60
+ attr_reader :env, :runtime_override
61
+
62
+ # The adapter resolves per execute() call: the per-directive
63
+ # `runtime:` key and the caller environment are selection inputs,
64
+ # so a memoized adapter could route a later directive to the
65
+ # wrong runtime.
66
+ def adapter(directive, directive_env)
67
+ Ace::Runtime::Molecules::RuntimeSelector.new(
68
+ config: Ace::Runtime.config,
69
+ env: directive_env
70
+ ).resolve(explicit: directive_runtime(directive))
71
+ end
72
+
73
+ def wait_directive(directive, directive_env)
74
+ condition = directive.fetch("for")
75
+ unless LIFECYCLE_CONDITIONS.include?(condition)
76
+ raise ArgumentError, "wait condition must be one of: #{LIFECYCLE_CONDITIONS.join(', ')}"
77
+ end
78
+
79
+ target = if WINDOW_CONDITIONS.include?(condition)
80
+ required_target(directive, "window")
81
+ else
82
+ required_target(directive, "pane")
83
+ end
84
+ adapter(directive, directive_env).wait_lifecycle(
85
+ condition: condition,
86
+ target: target,
87
+ timeout: directive.fetch("timeout", DEFAULT_TIMEOUT)
88
+ )
89
+ end
90
+
91
+ def send_directive(directive, directive_env)
92
+ pane = required_target(directive, "pane")
93
+ runtime_adapter = adapter(directive, directive_env)
94
+ if directive["command"]
95
+ runtime_adapter.send_command(pane: pane, command: directive["command"])
96
+ else
97
+ runtime_adapter.send_keys(pane: pane, keys: [directive.fetch("key")])
98
+ end
99
+ end
100
+
101
+ def required_target(directive, kind)
102
+ target = directive[kind].to_s.strip
103
+ raise ArgumentError, "Invalid tmux directive: missing #{kind}" if target.empty?
104
+
105
+ target
106
+ end
107
+
108
+ # Selection precedence mirrors the contract selector: per-directive
109
+ # runtime > executor runtime > ACE_RUNTIME > configured runtime >
110
+ # detection. The name form is needed separately for the tmux-local
111
+ # guard, which must not resolve an adapter at all.
112
+ def selected_runtime_name(directive, directive_env)
113
+ directive_runtime(directive) ||
114
+ directive_env["ACE_RUNTIME"] ||
115
+ Ace::Runtime.config["runtime"] ||
116
+ Ace::Runtime.detect(env: directive_env)&.to_s ||
117
+ ""
118
+ end
119
+
120
+ def directive_runtime(directive)
121
+ explicit = (directive["runtime"] || runtime_override).to_s.strip
122
+ explicit.empty? ? nil : explicit
123
+ end
124
+
125
+ def ensure_tmux_local!(directive, directive_env, action)
126
+ selected = selected_runtime_name(directive, directive_env)
127
+ return if selected.empty? || selected == "tmux"
128
+
129
+ raise Ace::Demo::Error, "#{action} is tmux-local and unsupported under the '#{selected}' runtime"
130
+ end
131
+
132
+ def tmux_executor
133
+ @tmux_executor_instance ||= @tmux_executor || Ace::Tmux::Molecules::TmuxExecutor.new
134
+ end
135
+
136
+ def tmux_binary
137
+ "tmux"
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -19,7 +19,7 @@ module Ace
19
19
  yaml_compiler: Atoms::VhsTapeCompiler,
20
20
  asciinema_tape_compiler: Atoms::AsciinemaTapeCompiler,
21
21
  media_retimer: Molecules::MediaRetimer.new,
22
- tmux_directive_executor: Molecules::TmuxDirectiveExecutor.new,
22
+ directive_executor: Molecules::RuntimeDirectiveExecutor.new,
23
23
  sandbox_builder: Molecules::DemoSandboxBuilder.new,
24
24
  teardown_executor: Molecules::DemoTeardownExecutor.new,
25
25
  output_dir: Demo.config["output_dir"],
@@ -38,7 +38,7 @@ module Ace
38
38
  @yaml_compiler = yaml_compiler
39
39
  @asciinema_tape_compiler = asciinema_tape_compiler
40
40
  @media_retimer = media_retimer
41
- @tmux_directive_executor = tmux_directive_executor
41
+ @directive_executor = directive_executor
42
42
  @sandbox_builder = sandbox_builder
43
43
  @teardown_executor = teardown_executor
44
44
  @output_dir = output_dir || ".ace-local/demo"
@@ -268,7 +268,7 @@ module Ace
268
268
  write_io.flush
269
269
  nil
270
270
  when :tmux
271
- @tmux_directive_executor.execute({"tmux" => command.fetch(:directive)}, env)
271
+ @directive_executor.execute({"tmux" => command.fetch(:directive)}, env)
272
272
  end
273
273
  end
274
274
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Demo
5
- VERSION = "0.25.10"
5
+ VERSION = "0.26.0"
6
6
  end
7
7
  end
data/lib/ace/demo.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "fileutils"
4
4
  require "ace/support/config"
5
- require "ace/tmux"
5
+ require "ace/runtime"
6
6
 
7
7
  require_relative "demo/version"
8
8
  require_relative "demo/atoms/vhs_command_builder"
@@ -41,7 +41,7 @@ require_relative "demo/molecules/media_retimer"
41
41
  require_relative "demo/molecules/demo_comment_poster"
42
42
  require_relative "demo/molecules/verification_report_writer"
43
43
  require_relative "demo/molecules/recording_manifest_writer"
44
- require_relative "demo/molecules/tmux_directive_executor"
44
+ require_relative "demo/molecules/runtime_directive_executor"
45
45
  require_relative "demo/organisms/demo_recorder"
46
46
  require_relative "demo/organisms/demo_attacher"
47
47
  require_relative "demo/organisms/tape_creator"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-demo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.10
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-09-05 00:00:00.000000000 Z
10
+ date: 2026-10-04 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-support-cli
@@ -65,20 +65,34 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0.14'
68
+ - !ruby/object:Gem::Dependency
69
+ name: ace-runtime
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: ace-tmux
70
84
  requirement: !ruby/object:Gem::Requirement
71
85
  requirements:
72
86
  - - "~>"
73
87
  - !ruby/object:Gem::Version
74
- version: '0.17'
88
+ version: '0.18'
75
89
  type: :runtime
76
90
  prerelease: false
77
91
  version_requirements: !ruby/object:Gem::Requirement
78
92
  requirements:
79
93
  - - "~>"
80
94
  - !ruby/object:Gem::Version
81
- version: '0.17'
95
+ version: '0.18'
82
96
  - !ruby/object:Gem::Dependency
83
97
  name: ace-support-test-helpers
84
98
  requirement: !ruby/object:Gem::Requirement
@@ -200,10 +214,10 @@ files:
200
214
  - lib/ace/demo/molecules/inline_recorder.rb
201
215
  - lib/ace/demo/molecules/media_retimer.rb
202
216
  - lib/ace/demo/molecules/recording_manifest_writer.rb
217
+ - lib/ace/demo/molecules/runtime_directive_executor.rb
203
218
  - lib/ace/demo/molecules/tape_resolver.rb
204
219
  - lib/ace/demo/molecules/tape_scanner.rb
205
220
  - lib/ace/demo/molecules/tape_writer.rb
206
- - lib/ace/demo/molecules/tmux_directive_executor.rb
207
221
  - lib/ace/demo/molecules/verification_report_writer.rb
208
222
  - lib/ace/demo/molecules/vhs_executor.rb
209
223
  - lib/ace/demo/organisms/demo_attacher.rb
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "shellwords"
4
-
5
- module Ace
6
- module Demo
7
- module Molecules
8
- class TmuxDirectiveExecutor
9
- def initialize(control_surface: nil, executor: nil, tmux: "tmux")
10
- @control_surface = control_surface
11
- @executor = executor || Ace::Tmux::Molecules::TmuxExecutor.new
12
- @tmux = tmux
13
- end
14
-
15
- def execute(command, env = nil)
16
- directive = command.fetch("tmux")
17
- action = directive.fetch("action")
18
- surface = control_surface_for(env: env)
19
-
20
- case action
21
- when "attach"
22
- session = directive.fetch("session")
23
- {shell_command: [tmux, "attach-session", "-t", session].map { |part| Shellwords.escape(part) }.join(" ")}
24
- when "detach"
25
- surface.detach_session(session: directive["session"])
26
- nil
27
- when "wait"
28
- surface.wait_for_condition(
29
- condition: directive.fetch("for"),
30
- session: directive["session"],
31
- window: directive["window"],
32
- pane: directive["pane"],
33
- pattern: directive["pattern"],
34
- timeout: directive.fetch("timeout", Ace::Tmux::Organisms::ControlSurface::DEFAULT_TIMEOUT)
35
- )
36
- nil
37
- when "send"
38
- if directive["command"]
39
- surface.send_command(
40
- session: directive["session"],
41
- window: directive["window"],
42
- pane: directive["pane"],
43
- command: directive["command"]
44
- )
45
- else
46
- surface.send_key(
47
- session: directive["session"],
48
- window: directive["window"],
49
- pane: directive["pane"],
50
- key: directive.fetch("key")
51
- )
52
- end
53
- nil
54
- else
55
- raise ArgumentError, "Unsupported tmux action '#{action}'"
56
- end
57
- rescue KeyError => e
58
- raise ArgumentError, "Invalid tmux directive: missing #{e.key}"
59
- rescue Ace::Tmux::Error => e
60
- raise Ace::Demo::Error, e.message
61
- end
62
-
63
- private
64
-
65
- attr_reader :control_surface, :executor, :tmux
66
-
67
- def control_surface_for(env:)
68
- return control_surface if control_surface
69
-
70
- resolver = Ace::Tmux::Molecules::RuntimeTargetResolver.new(
71
- executor: executor,
72
- tmux: tmux,
73
- env: env || ENV
74
- )
75
- Ace::Tmux::Organisms::ControlSurface.new(executor: executor, resolver: resolver, tmux: tmux)
76
- end
77
- end
78
- end
79
- end
80
- end