meta_workflows 0.7.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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +36 -0
  3. data/README.md +498 -0
  4. data/Rakefile +10 -0
  5. data/app/assets/javascripts/meta_workflows/controllers/loading_phrases_controller.js +31 -0
  6. data/app/assets/javascripts/meta_workflows/controllers/redirect_controller.js +15 -0
  7. data/app/assets/javascripts/meta_workflows/controllers/response_scroll_controller.js +67 -0
  8. data/app/assets/javascripts/meta_workflows_manifest.js +13 -0
  9. data/app/assets/stylesheets/meta_workflows/application.css +161 -0
  10. data/app/controllers/meta_workflows/application_controller.rb +10 -0
  11. data/app/controllers/meta_workflows/debug_controller.rb +101 -0
  12. data/app/controllers/meta_workflows/humans_controller.rb +96 -0
  13. data/app/controllers/meta_workflows/meta_controller.rb +21 -0
  14. data/app/helpers/meta_workflows/application_helper.rb +7 -0
  15. data/app/helpers/meta_workflows/debug_helper.rb +54 -0
  16. data/app/helpers/meta_workflows/execution_helper.rb +77 -0
  17. data/app/helpers/meta_workflows/formatting_helper.rb +30 -0
  18. data/app/helpers/meta_workflows/meta_workflows_helper.rb +41 -0
  19. data/app/helpers/meta_workflows/status_badge_helper.rb +66 -0
  20. data/app/jobs/meta_workflows/application_job.rb +14 -0
  21. data/app/jobs/meta_workflows/human_input_job.rb +35 -0
  22. data/app/jobs/meta_workflows/meta_job.rb +121 -0
  23. data/app/jobs/meta_workflows/meta_workflow_job.rb +20 -0
  24. data/app/jobs/meta_workflows/record_redirect_job.rb +36 -0
  25. data/app/mailers/meta_workflows/application_mailer.rb +8 -0
  26. data/app/models/meta_workflows/application_record.rb +7 -0
  27. data/app/models/meta_workflows/chat.rb +20 -0
  28. data/app/models/meta_workflows/message.rb +11 -0
  29. data/app/models/meta_workflows/tool_call.rb +7 -0
  30. data/app/models/meta_workflows/workflow.rb +32 -0
  31. data/app/models/meta_workflows/workflow_execution.rb +23 -0
  32. data/app/models/meta_workflows/workflow_step.rb +49 -0
  33. data/app/models/meta_workflows.rb +7 -0
  34. data/app/services/meta_workflows/execution_filter_service.rb +80 -0
  35. data/app/sidekiq/meta_workflows/tools/meta_workflow_tool.rb +86 -0
  36. data/app/views/layouts/meta_workflows/application.html.erb +17 -0
  37. data/app/views/layouts/meta_workflows/debug.html.erb +47 -0
  38. data/app/views/meta_workflows/_loader.html.erb +22 -0
  39. data/app/views/meta_workflows/_redirect.html.erb +1 -0
  40. data/app/views/meta_workflows/_response.html.erb +5 -0
  41. data/app/views/meta_workflows/_response_form.html.erb +36 -0
  42. data/app/views/meta_workflows/debug/executions.html.erb +187 -0
  43. data/app/views/meta_workflows/debug/show_execution.html.erb +283 -0
  44. data/app/views/meta_workflows/debug/show_workflow.html.erb +148 -0
  45. data/app/views/meta_workflows/debug/workflows.html.erb +110 -0
  46. data/config/routes.rb +13 -0
  47. data/db/migrate/20250530220618_create_meta_workflows_workflows.rb +16 -0
  48. data/db/migrate/20250530220634_create_meta_workflows_workflow_executions.rb +18 -0
  49. data/db/migrate/20250530220704_create_meta_workflows_chats.rb +16 -0
  50. data/db/migrate/20250530220722_create_meta_workflows_messages.rb +19 -0
  51. data/db/migrate/20250530220737_create_meta_workflows_tool_calls.rb +18 -0
  52. data/db/migrate/20250530220750_create_meta_workflows_workflow_steps.rb +17 -0
  53. data/db/migrate/20250613213159_add_error_fields_to_workflow_steps.rb +8 -0
  54. data/lib/meta_workflows/asset_installer.rb +509 -0
  55. data/lib/meta_workflows/configuration.rb +39 -0
  56. data/lib/meta_workflows/engine.rb +47 -0
  57. data/lib/meta_workflows/export_execution_service.rb +56 -0
  58. data/lib/meta_workflows/version.rb +5 -0
  59. data/lib/meta_workflows.rb +9 -0
  60. data/lib/services/meta_workflows/application_service.rb +11 -0
  61. data/lib/services/meta_workflows/meta_workflow_service.rb +277 -0
  62. data/lib/services/meta_workflows/updaters/meta_service.rb +39 -0
  63. data/lib/tasks/meta_workflows_tasks.rake +153 -0
  64. metadata +219 -0
@@ -0,0 +1,509 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'fileutils'
5
+
6
+ module MetaWorkflows
7
+ # Asset installer class to handle file discovery and path resolution
8
+ class AssetInstaller
9
+ attr_reader :gem_root, :host_app_root
10
+
11
+ # Custom exception for installation errors
12
+ class InstallationError < StandardError; end
13
+ class ValidationError < InstallationError; end
14
+ class PermissionError < InstallationError; end
15
+ class SourceError < InstallationError; end
16
+
17
+ def initialize
18
+ @gem_root = Gem::Specification.find_by_name('meta_workflows').gem_dir
19
+ @host_app_root = Rails.root
20
+ @rollback_files = []
21
+ rescue Gem::LoadError
22
+ raise InstallationError, 'meta_workflows gem not found. Please ensure it is properly installed.'
23
+ end
24
+
25
+ # Validate environment before installation
26
+ def valid_environment?
27
+ puts 'Validating installation environment...'
28
+
29
+ errors = validation_errors
30
+
31
+ if errors.any?
32
+ puts 'Validation failed:'
33
+ errors.each { |error| puts " ✗ #{error}" }
34
+ return false
35
+ end
36
+
37
+ puts '✓ Environment validation passed'
38
+ true
39
+ end
40
+
41
+ # Discover all assets that can be installed
42
+ def discover_assets
43
+ puts 'Discovering available assets...'
44
+
45
+ assets = []
46
+
47
+ begin
48
+ # Discover JavaScript controllers
49
+ js_assets = discover_javascript_assets
50
+ assets.concat(js_assets)
51
+ puts " ✓ Found #{js_assets.count} JavaScript asset(s)" if js_assets.any?
52
+
53
+ # Discover stylesheets
54
+ css_assets = discover_stylesheet_assets
55
+ assets.concat(css_assets)
56
+ puts " ✓ Found #{css_assets.count} stylesheet asset(s)" if css_assets.any?
57
+
58
+ # Validate discovered assets
59
+ validate_assets(assets)
60
+
61
+ assets
62
+ rescue StandardError => e
63
+ raise SourceError, "Failed to discover assets: #{e.message}"
64
+ end
65
+ end
66
+
67
+ # Install assets with comprehensive error handling and rollback capability
68
+ def install_assets(assets)
69
+ copied_count = 0
70
+ skipped_count = 0
71
+ failed_count = 0
72
+ success = true
73
+
74
+ puts 'Installing assets...'
75
+
76
+ # Clear rollback tracking
77
+ @rollback_files.clear
78
+
79
+ assets.each do |asset|
80
+ result = process_single_asset(asset)
81
+ case result
82
+ when :copied
83
+ copied_count += 1
84
+ puts "✓ Copied #{asset[:relative_path]}"
85
+ when :skipped
86
+ skipped_count += 1
87
+ puts "⚠ Skipped #{asset[:relative_path]} (already exists)"
88
+ when :failed
89
+ failed_count += 1
90
+ success = false
91
+ puts "✗ Failed to copy #{asset[:relative_path]}"
92
+ end
93
+ rescue StandardError => e
94
+ handle_installation_error(e, asset, failed_count)
95
+ failed_count += 1
96
+ success = false
97
+ end
98
+
99
+ {
100
+ success: success,
101
+ copied: copied_count,
102
+ skipped: skipped_count,
103
+ failed: failed_count
104
+ }
105
+ end
106
+
107
+ # Display comprehensive integration instructions after successful installation
108
+ def display_integration_instructions(assets, _result)
109
+ puts '=' * 80
110
+ puts '🎉 INTEGRATION INSTRUCTIONS'
111
+ puts '=' * 80
112
+ puts
113
+
114
+ # Analyze what was actually copied
115
+ copied_controllers = assets.select do |asset|
116
+ asset[:type] == :javascript && asset[:relative_path].include?('controllers/')
117
+ end
118
+ copied_stylesheets = assets.select { |asset| asset[:type] == :stylesheet }
119
+
120
+ display_controller_instructions(copied_controllers) if copied_controllers.any?
121
+ display_stylesheet_instructions(copied_stylesheets) if copied_stylesheets.any?
122
+ display_general_instructions
123
+ display_troubleshooting_guide
124
+
125
+ puts '=' * 80
126
+ puts '✨ Happy coding with meta_workflows!'
127
+ puts '=' * 80
128
+ end
129
+
130
+ private
131
+
132
+ def validation_errors
133
+ errors = []
134
+
135
+ # Check if we can find the gem
136
+ errors << "meta_workflows gem directory not found at: #{@gem_root}" unless File.exist?(@gem_root)
137
+
138
+ # Check if host application root exists
139
+ errors << "Host application root not found at: #{@host_app_root}" unless File.exist?(@host_app_root)
140
+
141
+ # Check basic write permissions to host app
142
+ unless writable_directory?(@host_app_root)
143
+ errors << "No write permission to host application directory: #{@host_app_root}"
144
+ end
145
+
146
+ # Check for required directories in host app
147
+ required_dirs = [
148
+ File.join(@host_app_root, 'app'),
149
+ File.join(@host_app_root, 'app', 'javascript'),
150
+ File.join(@host_app_root, 'app', 'assets')
151
+ ]
152
+
153
+ required_dirs.each do |dir|
154
+ errors << "Required directory missing: #{dir}" unless File.exist?(dir)
155
+ end
156
+
157
+ errors
158
+ end
159
+
160
+ def process_single_asset(asset)
161
+ install_single_asset(asset)
162
+ rescue StandardError
163
+ :failed
164
+ end
165
+
166
+ def handle_installation_error(error, asset, _failed_count)
167
+ puts "✗ Failed to copy #{asset[:relative_path]}: #{error.message}"
168
+
169
+ # If we have critical failures, consider rollback
170
+ return unless should_rollback?(error)
171
+
172
+ puts 'Critical error encountered. Rolling back changes...'
173
+ rollback_installation
174
+ raise InstallationError, "Installation failed and was rolled back: #{error.message}"
175
+ end
176
+
177
+ # Display instructions for Stimulus controllers
178
+ def display_controller_instructions(controllers)
179
+ puts '📋 STIMULUS CONTROLLERS'
180
+ puts '-' * 40
181
+ puts
182
+ puts 'The following Stimulus controllers have been installed:'
183
+ puts
184
+
185
+ controllers.each do |controller|
186
+ controller_name = File.basename(controller[:relative_path], '.js').gsub('_controller', '').tr('_', '-')
187
+ puts "• #{controller_name.upcase}_CONTROLLER"
188
+ puts " Location: #{controller[:relative_path]}"
189
+ puts " Usage: data-controller=\"meta-workflows--#{controller_name}\""
190
+ puts
191
+ end
192
+
193
+ puts '🔧 STIMULUS SETUP REQUIRED:'
194
+ puts
195
+ puts 'Add the following line to your app/javascript/controllers/index.js file:'
196
+ puts
197
+ puts ' import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"'
198
+ puts ' eagerLoadControllersFrom("controllers/meta_workflows", application)'
199
+ puts
200
+ puts 'This will automatically load and register all meta_workflows controllers with the'
201
+ puts '"meta-workflows--" prefix (e.g., "meta-workflows--redirect").'
202
+ puts
203
+
204
+ display_controller_usage_examples
205
+ end
206
+
207
+ # Display usage examples for each controller
208
+ def display_controller_usage_examples
209
+ puts '📖 CONTROLLER USAGE EXAMPLES:'
210
+ puts
211
+ puts '• REDIRECT CONTROLLER:'
212
+ puts ' <div data-controller="meta-workflows--redirect"'
213
+ puts ' data-meta-workflows--redirect-url-value="/success"'
214
+ puts ' data-meta-workflows--redirect-delay-value="2000">'
215
+ puts ' Redirecting...'
216
+ puts ' </div>'
217
+ puts
218
+ puts '• RESPONSE SCROLL CONTROLLER:'
219
+ puts ' <div data-controller="meta-workflows--response-scroll"'
220
+ puts ' id="response-content-container">'
221
+ puts ' <!-- Auto-scrolls to bottom as content changes -->'
222
+ puts ' </div>'
223
+ puts
224
+ puts '• LOADING PHRASES CONTROLLER:'
225
+ puts ' <div data-controller="meta-workflows--loading-phrases"'
226
+ loading_phrases_example_line
227
+ puts ' <span data-meta-workflows--loading-phrases-target="message">Loading...</span>'
228
+ puts ' </div>'
229
+ puts
230
+ end
231
+
232
+ def loading_phrases_example_line
233
+ puts ' data-meta-workflows--loading-phrases-phrases-value=' \
234
+ '\'["Processing...", "Almost done...", "Finishing up..."]\'>'
235
+ end
236
+
237
+ # Display instructions for stylesheets
238
+ def display_stylesheet_instructions(stylesheets)
239
+ puts '🎨 STYLESHEETS'
240
+ puts '-' * 40
241
+ puts
242
+ puts 'The following stylesheets have been installed:'
243
+ puts
244
+
245
+ stylesheets.each do |stylesheet|
246
+ puts "• #{stylesheet[:relative_path]}"
247
+ end
248
+ puts
249
+
250
+ puts '🔧 STYLESHEET SETUP REQUIRED:'
251
+ puts
252
+ puts '1. Add to your application.css file:'
253
+ puts
254
+ puts ' /*'
255
+ puts ' *= require meta_workflows/application'
256
+ puts ' */'
257
+ puts
258
+ puts '2. Or with Sass/SCSS, add to application.scss:'
259
+ puts
260
+ puts ' @import "meta_workflows/application";'
261
+ puts
262
+ puts '3. With CSS imports, add to your main CSS file:'
263
+ puts
264
+ puts ' @import url("./meta_workflows/application.css");'
265
+ puts
266
+ end
267
+
268
+ # Display general integration instructions
269
+ def display_general_instructions
270
+ puts '⚙️ GENERAL INTEGRATION NOTES'
271
+ puts '-' * 40
272
+ puts
273
+ puts '• Ensure your Rails application has Hotwire/Stimulus properly configured'
274
+ puts '• Controllers use the "meta-workflows--" prefix to avoid naming conflicts'
275
+ puts '• All assets maintain the meta_workflows namespace structure'
276
+ puts '• Assets are copied to standard Rails asset directories for easy management'
277
+ puts '• You can customize the copied files as needed for your application'
278
+ puts
279
+ puts '📁 ASSET LOCATIONS:'
280
+ puts ' • JavaScript Controllers: app/javascript/controllers/meta_workflows/'
281
+ puts ' • Stylesheets: app/assets/stylesheets/meta_workflows/'
282
+ puts
283
+ end
284
+
285
+ # Display troubleshooting guide
286
+ def display_troubleshooting_guide
287
+ puts '🔍 TROUBLESHOOTING'
288
+ puts '-' * 40
289
+ puts
290
+ puts 'If controllers are not working:'
291
+ puts ' 1. Check that Stimulus is properly installed and configured'
292
+ puts ' 2. Verify controllers are registered in your application.js'
293
+ puts ' 3. Check browser console for JavaScript errors'
294
+ puts ' 4. Ensure data-controller attributes use correct naming: "meta-workflows--controller-name"'
295
+ puts
296
+ puts 'If stylesheets are not loading:'
297
+ puts ' 1. Verify CSS imports in your application.css or application.scss'
298
+ puts ' 2. Check that Rails asset pipeline is properly configured'
299
+ puts ' 3. Run "rails assets:precompile" in production'
300
+ puts
301
+ puts 'For additional help:'
302
+ puts ' • Check the meta_workflows gem documentation'
303
+ puts ' • Review Rails asset pipeline documentation'
304
+ puts ' • Verify Hotwire/Stimulus setup in your application'
305
+ puts
306
+ end
307
+
308
+ # Install a single asset with comprehensive error handling
309
+ def install_single_asset(asset)
310
+ validate_source_file(asset)
311
+ destination_dir = File.dirname(asset[:destination_path])
312
+ create_destination_directory(destination_dir)
313
+ check_destination_permissions(destination_dir)
314
+
315
+ # Check if file already exists
316
+ return :skipped if File.exist?(asset[:destination_path]) && !should_overwrite?(asset[:relative_path])
317
+
318
+ copy_asset_file(asset)
319
+ end
320
+
321
+ def validate_source_file(asset)
322
+ # Validate source file exists
323
+ raise SourceError, "Source file not found: #{asset[:source_path]}" unless File.exist?(asset[:source_path])
324
+
325
+ # Validate source file is readable
326
+ return if File.readable?(asset[:source_path])
327
+
328
+ raise PermissionError, "Cannot read source file: #{asset[:source_path]}"
329
+ end
330
+
331
+ def create_destination_directory(destination_dir)
332
+ FileUtils.mkdir_p(destination_dir)
333
+ rescue Errno::EACCES
334
+ raise PermissionError, "Cannot create destination directory: #{destination_dir}"
335
+ rescue Errno::ENOSPC
336
+ raise InstallationError, "No space left on device when creating: #{destination_dir}"
337
+ end
338
+
339
+ def check_destination_permissions(destination_dir)
340
+ return if writable_directory?(destination_dir)
341
+
342
+ raise PermissionError, "No write permission to destination directory: #{destination_dir}"
343
+ end
344
+
345
+ def copy_asset_file(asset)
346
+ FileUtils.cp(asset[:source_path], asset[:destination_path])
347
+ verify_file_copy(asset)
348
+ @rollback_files << asset[:destination_path]
349
+ :copied
350
+ rescue Errno::EACCES
351
+ raise PermissionError, "Permission denied when copying to: #{asset[:destination_path]}"
352
+ rescue Errno::ENOSPC
353
+ raise InstallationError, "No space left on device when copying to: #{asset[:destination_path]}"
354
+ rescue Errno::EIO
355
+ raise InstallationError, "I/O error when copying to: #{asset[:destination_path]}"
356
+ end
357
+
358
+ def verify_file_copy(asset)
359
+ # Verify the copy was successful
360
+ unless File.exist?(asset[:destination_path])
361
+ raise InstallationError, 'File copy appeared successful but destination file not found'
362
+ end
363
+
364
+ # Verify file sizes match
365
+ return if File.size(asset[:source_path]) == File.size(asset[:destination_path])
366
+
367
+ raise InstallationError, 'File copy incomplete - size mismatch'
368
+ end
369
+
370
+ # Check if directory is writable
371
+ def writable_directory?(path)
372
+ File.writable?(path) && File.executable?(path)
373
+ end
374
+
375
+ # Validate discovered assets
376
+ def validate_assets(assets)
377
+ errors = []
378
+
379
+ assets.each do |asset|
380
+ errors << "Source file missing: #{asset[:source_path]}" unless File.exist?(asset[:source_path])
381
+
382
+ errors << "Source file not readable: #{asset[:source_path]}" unless File.readable?(asset[:source_path])
383
+
384
+ destination_dir = File.dirname(asset[:destination_path])
385
+ if File.exist?(destination_dir) && !writable_directory?(destination_dir)
386
+ errors << "Cannot write to destination directory: #{destination_dir}"
387
+ end
388
+ end
389
+
390
+ return unless errors.any?
391
+
392
+ error_message = "Asset validation failed:\n#{errors.map { |e| " - #{e}" }.join("\n")}"
393
+ raise ValidationError, error_message
394
+ end
395
+
396
+ # Prompt user for overwrite confirmation
397
+ def should_overwrite?(relative_path)
398
+ print "File #{relative_path} already exists. Overwrite? (y/N): "
399
+ response = $stdin.gets.chomp
400
+ response.downcase.start_with?('y')
401
+ end
402
+
403
+ # Determine if we should rollback based on the error type
404
+ def should_rollback?(error)
405
+ # Rollback on critical errors that could leave system in inconsistent state
406
+ error.is_a?(PermissionError) ||
407
+ (error.is_a?(InstallationError) && error.message.include?('No space left'))
408
+ end
409
+
410
+ # Rollback installation by removing copied files
411
+ def rollback_installation
412
+ return if @rollback_files.empty?
413
+
414
+ puts "Rolling back #{@rollback_files.count} file(s)..."
415
+
416
+ @rollback_files.reverse_each do |file_path|
417
+ if File.exist?(file_path)
418
+ File.delete(file_path)
419
+ puts " ✓ Removed #{file_path}"
420
+ end
421
+ rescue StandardError => e
422
+ puts " ✗ Could not remove #{file_path}: #{e.message}"
423
+ end
424
+
425
+ @rollback_files.clear
426
+ end
427
+
428
+ # Discover JavaScript controllers in the meta_workflows namespace
429
+ def discover_javascript_assets
430
+ assets = []
431
+ js_source_dir = File.join(gem_root, 'app', 'assets', 'javascripts', 'meta_workflows')
432
+
433
+ return assets unless Dir.exist?(js_source_dir)
434
+
435
+ begin
436
+ Dir.glob(File.join(js_source_dir, '**', '*.js')).each do |source_path|
437
+ assets << build_javascript_asset(source_path, js_source_dir)
438
+ end
439
+ rescue StandardError => e
440
+ raise SourceError, "Error discovering JavaScript assets: #{e.message}"
441
+ end
442
+
443
+ assets
444
+ end
445
+
446
+ def build_javascript_asset(source_path, js_source_dir)
447
+ relative_path = source_path.sub("#{js_source_dir}/", '')
448
+
449
+ # Determine destination based on file type
450
+ if relative_path.start_with?('controllers/')
451
+ build_controller_asset(source_path, relative_path)
452
+ else
453
+ build_general_js_asset(source_path, relative_path)
454
+ end
455
+ end
456
+
457
+ def build_controller_asset(source_path, relative_path)
458
+ controller_name = File.basename(relative_path)
459
+ destination_path = File.join(host_app_root, 'app', 'javascript', 'controllers', 'meta_workflows',
460
+ controller_name)
461
+ display_path = "app/javascript/controllers/meta_workflows/#{controller_name}"
462
+
463
+ {
464
+ type: :javascript,
465
+ source_path: source_path,
466
+ destination_path: destination_path,
467
+ relative_path: display_path
468
+ }
469
+ end
470
+
471
+ def build_general_js_asset(source_path, relative_path)
472
+ destination_path = File.join(host_app_root, 'app', 'javascript', 'meta_workflows', relative_path)
473
+ display_path = "app/javascript/meta_workflows/#{relative_path}"
474
+
475
+ {
476
+ type: :javascript,
477
+ source_path: source_path,
478
+ destination_path: destination_path,
479
+ relative_path: display_path
480
+ }
481
+ end
482
+
483
+ # Discover stylesheets in the meta_workflows namespace
484
+ def discover_stylesheet_assets
485
+ assets = []
486
+ css_source_dir = File.join(gem_root, 'app', 'assets', 'stylesheets', 'meta_workflows')
487
+
488
+ return assets unless Dir.exist?(css_source_dir)
489
+
490
+ begin
491
+ Dir.glob(File.join(css_source_dir, '**', '*.css')).each do |source_path|
492
+ relative_path = source_path.sub("#{css_source_dir}/", '')
493
+ destination_path = File.join(host_app_root, 'app', 'assets', 'stylesheets', 'meta_workflows', relative_path)
494
+
495
+ assets << {
496
+ type: :stylesheet,
497
+ source_path: source_path,
498
+ destination_path: destination_path,
499
+ relative_path: "app/assets/stylesheets/meta_workflows/#{relative_path}"
500
+ }
501
+ end
502
+ rescue StandardError => e
503
+ raise SourceError, "Error discovering stylesheet assets: #{e.message}"
504
+ end
505
+
506
+ assets
507
+ end
508
+ end
509
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class Configuration
5
+ attr_accessor :default_workflow_timeout,
6
+ :max_retry_attempts,
7
+ :workflow_definitions_path,
8
+ :enable_ui_components,
9
+ :sidekiq_queue_name
10
+
11
+ def initialize
12
+ @default_workflow_timeout = 1800 # 30 minutes in seconds
13
+ @max_retry_attempts = 3
14
+ @workflow_definitions_path = if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
15
+ Rails.root.join(
16
+ 'config/workflows'
17
+ )
18
+ end
19
+ @enable_ui_components = true
20
+ @sidekiq_queue_name = 'meta_workflows'
21
+ end
22
+ end
23
+
24
+ class << self
25
+ attr_writer :configuration
26
+
27
+ def configuration
28
+ @configuration ||= Configuration.new
29
+ end
30
+
31
+ def configure
32
+ yield(configuration)
33
+ end
34
+
35
+ def reset_configuration!
36
+ @configuration = Configuration.new
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace MetaWorkflows
6
+
7
+ # Add autoload paths for engine components
8
+ config.autoload_paths += %W[
9
+ #{root}/app/controllers/concerns
10
+ #{root}/app/models/concerns
11
+ #{root}/app/services
12
+ #{root}/app/components
13
+ #{root}/app/jobs
14
+ #{root}/app/sidekiq
15
+ #{root}/lib
16
+ ]
17
+
18
+ # Configure generators
19
+ config.generators do |g|
20
+ g.test_framework :rspec
21
+ g.fixture_replacement :factory_bot
22
+ g.factory_bot dir: 'spec/factories'
23
+ end
24
+
25
+ # Initialize engine configuration
26
+ initializer 'meta_workflows.configure' do |app|
27
+ # Ensure Sidekiq is available for background jobs
28
+ if defined?(Sidekiq)
29
+ # Configure Sidekiq for MetaWorkflows jobs
30
+ app.config.active_job.queue_adapter = :sidekiq
31
+ end
32
+
33
+ # Configure ViewComponent if available
34
+ if defined?(ViewComponent)
35
+ existing_paths = [*ViewComponent::Base.view_component_path]
36
+ engine_component_path = root.join('app', 'components').to_s
37
+
38
+ app.config.view_component.view_component_path = (existing_paths + [engine_component_path]).uniq
39
+ end
40
+ end
41
+
42
+ # Configure routes
43
+ initializer 'meta_workflows.routes' do |app|
44
+ # Routes will be mounted by the host application
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class ExportExecutionService
5
+ def initialize(execution)
6
+ @execution = execution
7
+ end
8
+
9
+ def export_data
10
+ {
11
+ execution: execution_data,
12
+ workflow: workflow_data,
13
+ workflow_steps: workflow_steps_data
14
+ }
15
+ end
16
+
17
+ private
18
+
19
+ def execution_data
20
+ {
21
+ id: @execution.id,
22
+ workflow_id: @execution.workflow_id,
23
+ workflow_name: @execution.workflow.name,
24
+ record_type: @execution.record_type,
25
+ record_id: @execution.record_id,
26
+ current_step: @execution.current_step,
27
+ completed: @execution.completed,
28
+ workflow_params: @execution.workflow_params,
29
+ created_at: @execution.created_at,
30
+ updated_at: @execution.updated_at
31
+ }
32
+ end
33
+
34
+ def workflow_data
35
+ {
36
+ id: @execution.workflow.id,
37
+ name: @execution.workflow.name,
38
+ recipe: @execution.workflow.recipe
39
+ }
40
+ end
41
+
42
+ def workflow_steps_data
43
+ @execution.workflow_steps.order(:step).map do |step|
44
+ {
45
+ id: step.id,
46
+ step: step.step,
47
+ workflow_execution_id: step.workflow_execution_id,
48
+ chat_id: step.chat_id,
49
+ created_at: step.created_at,
50
+ updated_at: step.updated_at,
51
+ chat: step.chat&.as_json(include: { messages: { include: :tool_calls } })
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ VERSION = '0.7.0'
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'meta_workflows/version'
4
+ require 'meta_workflows/configuration'
5
+ require 'meta_workflows/engine'
6
+
7
+ module MetaWorkflows
8
+ # Engine provides the main interface for configuration and setup
9
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Services
4
+ module MetaWorkflows
5
+ class ApplicationService
6
+ def self.call(*, &)
7
+ new(*, &).call
8
+ end
9
+ end
10
+ end
11
+ end