klue-langcraft 0.2.0 → 0.4.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 +4 -4
- data/.rubocop.yml +18 -3
- data/CHANGELOG.md +26 -0
- data/bin/langcraft.rb +177 -0
- data/lib/klue/langcraft/dsl/interpreter.rb +18 -18
- data/lib/klue/langcraft/dsl/klue_runner.rb +68 -0
- data/lib/klue/langcraft/dsl/watcher.rb +88 -0
- data/lib/klue/langcraft/dsl/webhook.rb +57 -0
- data/lib/klue/langcraft/version.rb +1 -1
- data/lib/klue/langcraft.rb +3 -6
- data/package-lock.json +2 -2
- data/package.json +1 -1
- metadata +6 -8
- data/bin/dsl_watcher.rb +0 -10
- data/lib/base_process.rb +0 -41
- data/lib/dsl_folder_watcher.rb +0 -50
- data/lib/dsl_interpreter.rb +0 -112
- data/lib/dsl_process_data.rb +0 -31
- data/lib/process_file_collector.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 991b394224dd15a2eccfe3ce1db7b6bca00304d8c6cf75ac8763a34beb6b9d06
|
4
|
+
data.tar.gz: 9a814b4cb28a1eefe7763c7705f652f754c567404748e326c60049e7d9f7f7c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e34c11144fefacb28af86e336e48f5bc905fb2a4db2ea75079689fcd21a1ecbc2f5ea8a6ffac8fbc16713a32323007279c3b208655c3864d29861848d84b38d2
|
7
|
+
data.tar.gz: 990a9b599923fc5a7117f0fafe0a20057f63798ebbef06a02de8b9debc3b900b6bc1797a9fcd472301b74d10944077077e25aa333fd001a81b88619fdecd668e
|
data/.rubocop.yml
CHANGED
@@ -40,8 +40,23 @@ Metrics/BlockLength:
|
|
40
40
|
- shared_examples_for
|
41
41
|
- transaction
|
42
42
|
|
43
|
+
Metrics/AbcSize:
|
44
|
+
Exclude:
|
45
|
+
- bin/*
|
46
|
+
|
47
|
+
|
43
48
|
Metrics/MethodLength:
|
44
49
|
Max: 25
|
50
|
+
Exclude:
|
51
|
+
- bin/*
|
52
|
+
|
53
|
+
Metrics/PerceivedComplexity:
|
54
|
+
Exclude:
|
55
|
+
- bin/*
|
56
|
+
|
57
|
+
Metrics/CyclomaticComplexity:
|
58
|
+
Exclude:
|
59
|
+
- bin/*
|
45
60
|
|
46
61
|
Layout/LineLength:
|
47
62
|
Max: 200
|
@@ -55,7 +70,7 @@ Lint/UnusedMethodArgument:
|
|
55
70
|
Style/BlockComments:
|
56
71
|
Enabled: false
|
57
72
|
Include:
|
58
|
-
- "**/spec
|
73
|
+
- "**/spec/**/*"
|
59
74
|
|
60
75
|
# My Preferences - Start
|
61
76
|
Metrics/ClassLength:
|
@@ -75,8 +90,8 @@ Style/EmptyMethod:
|
|
75
90
|
Exclude:
|
76
91
|
- "**/spec/**/*"
|
77
92
|
Metrics/ParameterLists:
|
78
|
-
|
79
|
-
|
93
|
+
Enabled: false
|
94
|
+
|
80
95
|
Layout/EmptyLineBetweenDefs:
|
81
96
|
Exclude:
|
82
97
|
- "**/spec/**/*"
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
# [0.3.0](https://github.com/appydave/klue-langcraft/compare/v0.2.0...v0.3.0) (2024-10-26)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* fix cop ([d77187a](https://github.com/appydave/klue-langcraft/commit/d77187ac59088b7ce6ca23630a06fbb007f05fb4))
|
7
|
+
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* update cli for watch and process ([9f0eeb1](https://github.com/appydave/klue-langcraft/commit/9f0eeb1a9409f6734d4ab930ca5c3164b71446eb))
|
12
|
+
|
13
|
+
# [0.2.0](https://github.com/appydave/klue-langcraft/compare/v0.1.1...v0.2.0) (2024-10-25)
|
14
|
+
|
15
|
+
|
16
|
+
### Bug Fixes
|
17
|
+
|
18
|
+
* fix ci issue ([a3bfd35](https://github.com/appydave/klue-langcraft/commit/a3bfd35371b7c0ca70b0b57d70d4541c7a4ec54b))
|
19
|
+
* fix cop ([123070b](https://github.com/appydave/klue-langcraft/commit/123070bd1f636a81d01da469c2e5072c57384bae))
|
20
|
+
* update dsl_interpreter ([2507045](https://github.com/appydave/klue-langcraft/commit/2507045c3f7f205603a3f899785e9c5445c4c0bb))
|
21
|
+
|
22
|
+
|
23
|
+
### Features
|
24
|
+
|
25
|
+
* refactor dsl processor ([5875dc9](https://github.com/appydave/klue-langcraft/commit/5875dc957789f113da762a6dcb55cda66bfb8a3d))
|
26
|
+
|
1
27
|
## [0.1.1](https://github.com/appydave/klue-langcraft/compare/v0.1.0...v0.1.1) (2024-09-22)
|
2
28
|
|
3
29
|
|
data/bin/langcraft.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
5
|
+
|
6
|
+
require 'klue/langcraft'
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
# CLI class for the Klue Langcraft application.
|
10
|
+
# This class handles command-line interactions, parsing options,
|
11
|
+
# and executing the appropriate commands for processing files
|
12
|
+
# or watching directories.
|
13
|
+
class CLI
|
14
|
+
def initialize
|
15
|
+
@commands = {
|
16
|
+
'process' => method(:process_files),
|
17
|
+
'watch' => method(:watch_files)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
if ARGV.empty?
|
23
|
+
print_help
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
command, *args = ARGV
|
28
|
+
if @commands.key?(command)
|
29
|
+
@commands[command].call(args)
|
30
|
+
else
|
31
|
+
puts "Unknown command: #{command}"
|
32
|
+
print_help
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def process_files(args)
|
39
|
+
options = {}
|
40
|
+
parser = OptionParser.new do |opts|
|
41
|
+
opts.banner = 'Usage: langcraft process [options]'
|
42
|
+
|
43
|
+
opts.on('-i', '--input FILE', 'Input file (required)') do |file|
|
44
|
+
options[:input] = file
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-b', '--basic-output FILE', 'Output file') do |file|
|
48
|
+
options[:basic_output] = file
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-e', '--enhanced-output FILE', 'Output file') do |file|
|
52
|
+
options[:enhanced_output] = file
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-u', '--webhook-url URL', 'Webhook URL') do |url|
|
56
|
+
options[:webhook_url] = url
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('-d', '--debug LEVEL', %i[none info detailed], 'Debug level (none, info, detailed)') do |level|
|
60
|
+
options[:debug] = level
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('-h', '--help', 'Show this message') do
|
64
|
+
puts opts
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
parser.parse!(args)
|
70
|
+
|
71
|
+
if validate_options(options)
|
72
|
+
run_klue_runner(options)
|
73
|
+
else
|
74
|
+
print_usage(parser)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def validate_options(options)
|
79
|
+
if options[:input].nil?
|
80
|
+
puts 'Error: Input file is required.'
|
81
|
+
false
|
82
|
+
else
|
83
|
+
true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def run_klue_runner(options)
|
88
|
+
klue_runner = Klue::Langcraft::DSL::KlueRunner.new
|
89
|
+
klue_runner.run(
|
90
|
+
input_file: options[:input],
|
91
|
+
basic_output_file: options[:basic_output],
|
92
|
+
enhanced_output_file: options[:enhanced_output],
|
93
|
+
webhook_url: options[:webhook_url],
|
94
|
+
debug: options[:debug] || :none
|
95
|
+
)
|
96
|
+
rescue StandardError => e
|
97
|
+
puts "Error: #{e.message}"
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
|
101
|
+
def print_usage(parser)
|
102
|
+
puts 'Error: Invalid or missing options.'
|
103
|
+
puts
|
104
|
+
puts parser
|
105
|
+
exit 1
|
106
|
+
end
|
107
|
+
|
108
|
+
def watch_files(args)
|
109
|
+
options = {
|
110
|
+
directories: [],
|
111
|
+
create_basic_json: false,
|
112
|
+
create_enhanced_json: false,
|
113
|
+
log_level: :info,
|
114
|
+
webhook_url: nil,
|
115
|
+
extensions: ['.klue'] # Default extension
|
116
|
+
}
|
117
|
+
OptionParser.new do |opts|
|
118
|
+
opts.banner = 'Usage: server watch [options]'
|
119
|
+
|
120
|
+
opts.on('-w', '--watch-directory DIRECTORY', 'Directory to watch (can be specified multiple times)') do |dir|
|
121
|
+
options[:directories] << File.expand_path(dir) # Expand the path here
|
122
|
+
end
|
123
|
+
|
124
|
+
opts.on('-f', '--flags TYPE', 'Set processing flags (none, basic, enhanced, all)') do |type|
|
125
|
+
types = type.split(',').map(&:strip).map(&:downcase)
|
126
|
+
options[:create_basic_json] = types.include?('basic') || types.include?('all')
|
127
|
+
options[:create_enhanced_json] = types.include?('enhanced') || types.include?('all')
|
128
|
+
end
|
129
|
+
|
130
|
+
opts.on('-l', '--log-level LEVEL', %i[none info detailed], 'Log level (none, info, detailed)') do |level|
|
131
|
+
options[:log_level] = level
|
132
|
+
end
|
133
|
+
|
134
|
+
opts.on('-u', '--webhook-url URL', 'Webhook URL') do |url|
|
135
|
+
options[:webhook_url] = url
|
136
|
+
end
|
137
|
+
|
138
|
+
opts.on('-e', '--extensions EXT1,EXT2,...', Array, 'File extensions to watch (default: .klue)') do |exts|
|
139
|
+
options[:extensions] = exts.map { |ext| ext.start_with?('.') ? ext : ".#{ext}" }
|
140
|
+
end
|
141
|
+
|
142
|
+
opts.on('-h', '--help', 'Show this message') do
|
143
|
+
puts opts
|
144
|
+
exit
|
145
|
+
end
|
146
|
+
end.parse!(args)
|
147
|
+
|
148
|
+
# If no directories were specified, use the current directory
|
149
|
+
options[:directories] = [Dir.pwd] if options[:directories].empty?
|
150
|
+
|
151
|
+
# Ensure all directories are expanded
|
152
|
+
options[:directories].map! { |dir| File.expand_path(dir) }
|
153
|
+
|
154
|
+
Klue::Langcraft::DSL::Watcher.new(
|
155
|
+
options[:directories],
|
156
|
+
create_basic_json: options[:create_basic_json],
|
157
|
+
create_enhanced_json: options[:create_enhanced_json],
|
158
|
+
log_level: options[:log_level],
|
159
|
+
webhook_url: options[:webhook_url],
|
160
|
+
extensions: options[:extensions]
|
161
|
+
).start
|
162
|
+
end
|
163
|
+
|
164
|
+
def print_help
|
165
|
+
puts 'Klue Langcraft Server'
|
166
|
+
puts 'Usage: server [command] [options]'
|
167
|
+
puts ''
|
168
|
+
puts 'Commands:'
|
169
|
+
puts ' process Process a single file'
|
170
|
+
puts ' watch Watch directories for changes'
|
171
|
+
puts ''
|
172
|
+
puts "Run 'server [command] --help' for more information on a command."
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Run the CLI
|
177
|
+
CLI.new.run
|
@@ -17,18 +17,18 @@ module Klue
|
|
17
17
|
attr_accessor :processed
|
18
18
|
|
19
19
|
def initialize
|
20
|
-
|
21
|
-
@processed = false
|
20
|
+
klue_reset
|
22
21
|
end
|
23
22
|
|
24
23
|
def process(input: nil, input_file: nil, output_file: nil)
|
25
|
-
|
26
|
-
|
24
|
+
klue_reset
|
25
|
+
klue_validate_input_arguments(input, input_file)
|
26
|
+
klue_input_content = klue_input_content(input, input_file)
|
27
27
|
|
28
28
|
@processed = true
|
29
|
-
instance_eval(
|
29
|
+
instance_eval(klue_input_content)
|
30
30
|
|
31
|
-
|
31
|
+
klue_write_output(output_file) if output_file
|
32
32
|
data
|
33
33
|
end
|
34
34
|
|
@@ -36,7 +36,7 @@ module Klue
|
|
36
36
|
raise "You must call 'process' before using other methods" unless @processed
|
37
37
|
|
38
38
|
key = method_name
|
39
|
-
value =
|
39
|
+
value = klue_process_args(args, block)
|
40
40
|
|
41
41
|
if @data[key]
|
42
42
|
@data[key] = [@data[key]] unless @data[key].is_a?(Array)
|
@@ -50,7 +50,7 @@ module Klue
|
|
50
50
|
@processed || super
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
53
|
+
def klue_process_args(args, block)
|
54
54
|
positional_args = []
|
55
55
|
named_args = {}
|
56
56
|
|
@@ -82,32 +82,32 @@ module Klue
|
|
82
82
|
|
83
83
|
private
|
84
84
|
|
85
|
-
def
|
85
|
+
def klue_reset
|
86
|
+
@data = {}
|
87
|
+
@processed = false
|
88
|
+
end
|
89
|
+
|
90
|
+
def klue_validate_input_arguments(input, input_file)
|
86
91
|
raise ArgumentError, 'Either input or input_file must be provided' unless input || input_file
|
87
92
|
raise ArgumentError, 'Both input and input_file cannot be provided' if input && input_file
|
88
93
|
end
|
89
94
|
|
90
|
-
def
|
95
|
+
def klue_input_content(input, input_file)
|
91
96
|
input_file ? File.read(input_file) : input
|
92
97
|
end
|
93
98
|
|
94
|
-
def
|
95
|
-
output_path =
|
99
|
+
def klue_write_output(output_file)
|
100
|
+
output_path = klue_output_path(output_file)
|
96
101
|
File.write(output_path, JSON.pretty_generate(data))
|
97
102
|
end
|
98
103
|
|
99
|
-
def
|
104
|
+
def klue_output_path(output_file)
|
100
105
|
if Pathname.new(output_file).absolute?
|
101
106
|
output_file
|
102
107
|
else
|
103
108
|
File.join(File.dirname(output_file), File.basename(output_file))
|
104
109
|
end
|
105
110
|
end
|
106
|
-
|
107
|
-
# Convert to JSON
|
108
|
-
def to_json(*_args)
|
109
|
-
@data.to_json
|
110
|
-
end
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Klue
|
4
|
+
module Langcraft
|
5
|
+
module DSL
|
6
|
+
# KlueRunner handles the processing of DSL input data
|
7
|
+
# It manages the execution of various processors and the output of processed data
|
8
|
+
class KlueRunner
|
9
|
+
attr_reader :interpreter, :pipeline, :webhook
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@interpreter = Klue::Langcraft::DSL::Interpreter.new
|
13
|
+
@pipeline = Klue::Langcraft::DSL::ProcessDataPipeline.new(Klue::Langcraft::DSL::ProcessMatcher.new)
|
14
|
+
@webhook = Klue::Langcraft::DSL::Webhook.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Run the KlueRunner with the given input data
|
18
|
+
# @param input [String] The input data to process
|
19
|
+
# @param input_file [String] The input file to process (input data and file are mutually exclusive)
|
20
|
+
# @param basic_output_file [String] The output file to write the processed data, this file is before any processing
|
21
|
+
# @param enhanced_output_file [String] The output file to write the processed data, this file is after processing
|
22
|
+
def run(
|
23
|
+
input: nil,
|
24
|
+
input_file: nil,
|
25
|
+
basic_output_file: nil,
|
26
|
+
enhanced_output_file: nil,
|
27
|
+
webhook_url: nil,
|
28
|
+
log_level: :none
|
29
|
+
)
|
30
|
+
@log_level = log_level
|
31
|
+
|
32
|
+
log_info('Processing input')
|
33
|
+
data = interpreter.process(input: input, input_file: input_file, output_file: basic_output_file)
|
34
|
+
log_detailed('Interpreter output:', data)
|
35
|
+
|
36
|
+
log_info('Executing pipeline - enhance')
|
37
|
+
enhanced_data = pipeline.execute(data)
|
38
|
+
log_detailed('Enhanced output:', enhanced_data)
|
39
|
+
|
40
|
+
if enhanced_output_file
|
41
|
+
log_info("Writing enhanced output to file: #{enhanced_output_file}")
|
42
|
+
@pipeline.write_output(enhanced_data, enhanced_output_file)
|
43
|
+
end
|
44
|
+
|
45
|
+
if webhook_url
|
46
|
+
log_info("Delivering data to webhook: #{webhook_url}")
|
47
|
+
@webhook.deliver(webhook_url, enhanced_data)
|
48
|
+
end
|
49
|
+
|
50
|
+
log_info('Processing complete')
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def log_info(message)
|
56
|
+
puts message if %i[info detailed].include?(@log_level)
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_detailed(message, data)
|
60
|
+
return unless @log_level == :detailed
|
61
|
+
|
62
|
+
puts message
|
63
|
+
pp data
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'listen'
|
4
|
+
|
5
|
+
module Klue
|
6
|
+
module Langcraft
|
7
|
+
module DSL
|
8
|
+
# Watcher class for monitoring file changes in specified directories.
|
9
|
+
# This class sets up a file system listener to watch for changes to files
|
10
|
+
# with specified extensions, and processes those files using a KlueRunner.
|
11
|
+
class Watcher
|
12
|
+
def initialize(directories, **options)
|
13
|
+
@directories = directories.map { |dir| File.expand_path(dir) }
|
14
|
+
@options = options
|
15
|
+
@klue_runner = Klue::Langcraft::DSL::KlueRunner.new
|
16
|
+
@extensions = options[:extensions] || ['.klue']
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
listener = create_listener
|
21
|
+
log_watcher_info
|
22
|
+
listener.start
|
23
|
+
sleep
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def create_listener
|
29
|
+
extension_regex = create_extension_regex
|
30
|
+
Listen.to(*@directories, only: extension_regex) do |modified, added, _removed|
|
31
|
+
process_changed_files(modified + added)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_extension_regex
|
36
|
+
Regexp.union(@extensions.map { |ext| /#{Regexp.escape(ext)}$/ })
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_changed_files(files)
|
40
|
+
files.each { |file| process_file(file) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_file(file)
|
44
|
+
log_info('Processing file', file)
|
45
|
+
options = create_file_options(file)
|
46
|
+
run_klue_runner(options)
|
47
|
+
rescue StandardError => e
|
48
|
+
log_error('Error processing file', "#{file}: #{e.message}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_file_options(file)
|
52
|
+
options = @options.dup
|
53
|
+
options[:input_file] = file
|
54
|
+
options[:basic_output_file] = file.sub(/#{File.extname(file)}$/, '.json') if @options[:create_basic_json]
|
55
|
+
options[:enhanced_output_file] = file.sub(/#{File.extname(file)}$/, '.enhanced.json') if @options[:create_enhanced_json]
|
56
|
+
options
|
57
|
+
end
|
58
|
+
|
59
|
+
def run_klue_runner(options)
|
60
|
+
@klue_runner.run(
|
61
|
+
input_file: options[:input_file],
|
62
|
+
basic_output_file: options[:basic_output_file],
|
63
|
+
enhanced_output_file: options[:enhanced_output_file],
|
64
|
+
webhook_url: options[:webhook_url],
|
65
|
+
log_level: options[:log_level]
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_watcher_info
|
70
|
+
log_info('Watching directories', @directories.join(', '))
|
71
|
+
log_info('Watching file extensions', @extensions.join(', '))
|
72
|
+
log_info('Create basic JSON', @options[:create_basic_json])
|
73
|
+
log_info('Create enhanced JSON', @options[:create_enhanced_json])
|
74
|
+
log_info('Webhook URL', @options[:webhook_url])
|
75
|
+
log_info('Log level', @options[:log_level])
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_info(label, value)
|
79
|
+
puts "#{label.ljust(30)}: #{value}" if %i[info detailed].include?(@options[:log_level])
|
80
|
+
end
|
81
|
+
|
82
|
+
def log_error(label, value)
|
83
|
+
puts "#{label.ljust(30)}: #{value}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module Klue
|
8
|
+
module Langcraft
|
9
|
+
module DSL
|
10
|
+
# Webhook class for handling HTTP POST requests to specified URLs.
|
11
|
+
# This class is responsible for sending processed data to external services
|
12
|
+
# via webhooks, handling the delivery process, and logging the results.
|
13
|
+
class Webhook
|
14
|
+
def deliver(webhook_url, data)
|
15
|
+
klue_type = extract_klue_type(data)
|
16
|
+
uri = build_uri(webhook_url, klue_type)
|
17
|
+
response = send_request(uri, build_payload(klue_type, data))
|
18
|
+
log_response(response, uri)
|
19
|
+
response
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def extract_klue_type(data)
|
25
|
+
data.keys.first.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_uri(webhook_url, klue_type)
|
29
|
+
URI.parse("#{webhook_url}?klue-type=#{klue_type}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_payload(klue_type, data)
|
33
|
+
{ klue_type: klue_type, data: data }
|
34
|
+
end
|
35
|
+
|
36
|
+
def send_request(uri, payload)
|
37
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
38
|
+
request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' })
|
39
|
+
request.body = payload.to_json
|
40
|
+
http.request(request)
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_response(response, uri)
|
44
|
+
puts "Response: #{response.code} - #{response.message}"
|
45
|
+
puts "Endpoint: #{uri}"
|
46
|
+
log_response_body(response)
|
47
|
+
end
|
48
|
+
|
49
|
+
def log_response_body(response)
|
50
|
+
body = JSON.parse(response.body)
|
51
|
+
puts "DSL Type: #{body['type']}"
|
52
|
+
# puts JSON.pretty_generate(body['data'])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/klue/langcraft.rb
CHANGED
@@ -19,12 +19,9 @@ require 'klue/langcraft/dsl/processors/full_name_processor'
|
|
19
19
|
require 'klue/langcraft/dsl/interpreter'
|
20
20
|
require 'klue/langcraft/dsl/process_matcher'
|
21
21
|
require 'klue/langcraft/dsl/process_data_pipeline'
|
22
|
-
|
23
|
-
require '
|
24
|
-
require '
|
25
|
-
require 'dsl_interpreter'
|
26
|
-
require 'dsl_folder_watcher'
|
27
|
-
require 'dsl_process_data'
|
22
|
+
require 'klue/langcraft/dsl/webhook'
|
23
|
+
require 'klue/langcraft/dsl/watcher'
|
24
|
+
require 'klue/langcraft/dsl/klue_runner'
|
28
25
|
|
29
26
|
module Klue
|
30
27
|
module Langcraft
|
data/package-lock.json
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "klue-langcraft",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.4.0",
|
4
4
|
"lockfileVersion": 3,
|
5
5
|
"requires": true,
|
6
6
|
"packages": {
|
7
7
|
"": {
|
8
8
|
"name": "klue-langcraft",
|
9
|
-
"version": "0.
|
9
|
+
"version": "0.4.0",
|
10
10
|
"devDependencies": {
|
11
11
|
"@klueless-js/semantic-release-rubygem": "github:klueless-js/semantic-release-rubygem",
|
12
12
|
"@semantic-release/changelog": "^6.0.3",
|
data/package.json
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: klue-langcraft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Cruwys
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: appydave-tools
|
@@ -61,7 +61,7 @@ files:
|
|
61
61
|
- README.md
|
62
62
|
- Rakefile
|
63
63
|
- bin/console
|
64
|
-
- bin/
|
64
|
+
- bin/langcraft.rb
|
65
65
|
- bin/setup
|
66
66
|
- docs/dsl-class-diagram.md
|
67
67
|
- docs/dsl-examples.md
|
@@ -78,24 +78,22 @@ files:
|
|
78
78
|
- docs/project-plan/project_done.svg
|
79
79
|
- docs/project-plan/project_in_progress.svg
|
80
80
|
- docs/project-plan/project_todo.svg
|
81
|
-
- lib/base_process.rb
|
82
|
-
- lib/dsl_folder_watcher.rb
|
83
|
-
- lib/dsl_interpreter.rb
|
84
|
-
- lib/dsl_process_data.rb
|
85
81
|
- lib/klue/langcraft.rb
|
86
82
|
- lib/klue/langcraft/dsl/interpreter.rb
|
83
|
+
- lib/klue/langcraft/dsl/klue_runner.rb
|
87
84
|
- lib/klue/langcraft/dsl/process_data_pipeline.rb
|
88
85
|
- lib/klue/langcraft/dsl/process_matcher.rb
|
89
86
|
- lib/klue/langcraft/dsl/processor_config.rb
|
90
87
|
- lib/klue/langcraft/dsl/processors/file_collector_processor.rb
|
91
88
|
- lib/klue/langcraft/dsl/processors/full_name_processor.rb
|
92
89
|
- lib/klue/langcraft/dsl/processors/processor.rb
|
90
|
+
- lib/klue/langcraft/dsl/watcher.rb
|
91
|
+
- lib/klue/langcraft/dsl/webhook.rb
|
93
92
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/-brief.md
|
94
93
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/parser.rb
|
95
94
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/sample_usage.rb
|
96
95
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/tokenizer.rb
|
97
96
|
- lib/klue/langcraft/version.rb
|
98
|
-
- lib/process_file_collector.rb
|
99
97
|
- package-lock.json
|
100
98
|
- package.json
|
101
99
|
- sig/klue/langcraft.rbs
|
data/bin/dsl_watcher.rb
DELETED
data/lib/base_process.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# file: lib/base_process.rb
|
4
|
-
|
5
|
-
class BaseProcess
|
6
|
-
attr_reader :key
|
7
|
-
|
8
|
-
def initialize(key)
|
9
|
-
@key = key
|
10
|
-
end
|
11
|
-
|
12
|
-
def deep_match(input, predicate)
|
13
|
-
matches = []
|
14
|
-
|
15
|
-
# If the current input is a Hash, iterate over each key-value pair
|
16
|
-
if input.is_a?(Hash)
|
17
|
-
|
18
|
-
input.each do |key, value|
|
19
|
-
|
20
|
-
# If the value matches the predicate, add it to matches
|
21
|
-
if predicate.call(key, value)
|
22
|
-
matches << value
|
23
|
-
end
|
24
|
-
|
25
|
-
# Continue searching deeper within the value
|
26
|
-
matches.concat(deep_match(value, predicate))
|
27
|
-
end
|
28
|
-
|
29
|
-
# If the input is an Array, iterate over each item
|
30
|
-
elsif input.is_a?(Array)
|
31
|
-
|
32
|
-
input.each do |item|
|
33
|
-
|
34
|
-
# Continue searching within each item of the array
|
35
|
-
matches.concat(deep_match(item, predicate))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
matches
|
40
|
-
end
|
41
|
-
end
|
data/lib/dsl_folder_watcher.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class DSLFolderWatcher
|
4
|
-
def self.watch(folder_path)
|
5
|
-
puts "Watching: #{folder_path}"
|
6
|
-
listener = Listen.to(folder_path) do |modified, added, _removed|
|
7
|
-
changes = (modified + added).uniq
|
8
|
-
|
9
|
-
# DEBOUNCE CURRENTLY NOT WORKING
|
10
|
-
# debounce_map = {}
|
11
|
-
# debounce_interval = 1 # seconds
|
12
|
-
|
13
|
-
changes.each do |file_path|
|
14
|
-
next unless File.extname(file_path) == '.klue'
|
15
|
-
|
16
|
-
puts file_path
|
17
|
-
|
18
|
-
# debounce_map[file_path] ||= Time.now
|
19
|
-
# next unless Time.now - debounce_map[file_path] >= debounce_interval
|
20
|
-
|
21
|
-
# debounce_map[file_path] = Time.now
|
22
|
-
|
23
|
-
base_name = file_path.gsub(/\.klue$/, '')
|
24
|
-
input_file = "#{base_name}.klue"
|
25
|
-
output_file = "#{base_name}.json"
|
26
|
-
|
27
|
-
interpreter = DSLInterpreter.new
|
28
|
-
if interpreter.process('', input_file, output_file)
|
29
|
-
# Process the JSON data to add 'process-data' details
|
30
|
-
dsl_processor = DSLProcessData.new
|
31
|
-
dsl_processor.process('', output_file, output_file)
|
32
|
-
# SKIP EXTEND FILE FOR NOW AND REWRITE THE OUTPUTFILE
|
33
|
-
# dsl_processor.process('', output_file, extended_output_file)
|
34
|
-
|
35
|
-
# interpreter.send_to_endpoint
|
36
|
-
else
|
37
|
-
puts 'Skipping further processing due to errors in DSL interpretation.'
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Remove old entries from debounce_map to prevent memory bloat
|
42
|
-
# debounce_map.each_key do |key|
|
43
|
-
# debounce_map.delete(key) if Time.now - debounce_map[key] > debounce_interval * 2
|
44
|
-
# end
|
45
|
-
end
|
46
|
-
listener.start
|
47
|
-
puts "Wait for changes: #{folder_path}"
|
48
|
-
sleep
|
49
|
-
end
|
50
|
-
end
|
data/lib/dsl_interpreter.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# ChatReferences:
|
4
|
-
# - https://chatgpt.com/c/67064770-d524-8002-8344-3091e895d150
|
5
|
-
# - https://chatgpt.com/c/6706289c-9b9c-8002-86e3-f9198c1c608a
|
6
|
-
# - https://chatgpt.com/c/670dcd34-5dbc-8002-ad7a-d4df54a6a2e0
|
7
|
-
#
|
8
|
-
class DSLInterpreter
|
9
|
-
def initialize
|
10
|
-
@data = {}
|
11
|
-
end
|
12
|
-
|
13
|
-
# Capturing top-level DSL methods
|
14
|
-
def method_missing(method_name, *args, &block)
|
15
|
-
key = method_name
|
16
|
-
value = process_args(args, block)
|
17
|
-
|
18
|
-
# Append key-value to the current context of @data
|
19
|
-
if @data[key]
|
20
|
-
@data[key] = [@data[key]] unless @data[key].is_a?(Array)
|
21
|
-
@data[key] << value
|
22
|
-
else
|
23
|
-
@data[key] = value
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# A method to handle parameters and nested blocks
|
28
|
-
def process_args(args, block)
|
29
|
-
data = {}
|
30
|
-
|
31
|
-
# Handling positional and named parameters separately
|
32
|
-
positional_args = []
|
33
|
-
named_args = {}
|
34
|
-
|
35
|
-
args.each do |arg|
|
36
|
-
if arg.is_a?(Hash)
|
37
|
-
named_args.merge!(arg)
|
38
|
-
else
|
39
|
-
positional_args << arg
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Assign positional parameters generically
|
44
|
-
positional_args.each_with_index do |arg, index|
|
45
|
-
data[:"param#{index + 1}"] = arg
|
46
|
-
end
|
47
|
-
|
48
|
-
# Merge named parameters directly
|
49
|
-
data.merge!(named_args)
|
50
|
-
|
51
|
-
# Handling a nested block
|
52
|
-
if block
|
53
|
-
interpreter = DSLInterpreter.new
|
54
|
-
interpreter.instance_eval(&block)
|
55
|
-
data.merge!(interpreter.data)
|
56
|
-
end
|
57
|
-
|
58
|
-
data.empty? ? nil : data
|
59
|
-
end
|
60
|
-
|
61
|
-
# To access data after interpreting
|
62
|
-
attr_reader :data
|
63
|
-
|
64
|
-
# Reading file and evaluating as Ruby
|
65
|
-
def process(base_path, input_file, output_file)
|
66
|
-
file_path = File.join(base_path, input_file)
|
67
|
-
content = File.read(file_path)
|
68
|
-
|
69
|
-
# begin
|
70
|
-
instance_eval(content)
|
71
|
-
# rescue SyntaxError => e
|
72
|
-
# puts "Syntax error in DSL file: #{input_file}"
|
73
|
-
# puts "Error message: #{e.message}"
|
74
|
-
# puts "Error occurred at line: #{e.backtrace.first}"
|
75
|
-
# return false # Indicate that processing failed
|
76
|
-
# rescue StandardError => e
|
77
|
-
# puts "Error processing DSL file: #{input_file}"
|
78
|
-
# puts "Error message: #{e.message}"
|
79
|
-
# puts "Error occurred at: #{e.backtrace.first}"
|
80
|
-
# return false # Indicate that processing failed
|
81
|
-
# end
|
82
|
-
|
83
|
-
output_path = File.join(base_path, output_file)
|
84
|
-
File.write(output_path, JSON.pretty_generate(to_hash))
|
85
|
-
true # Indicate that processing succeeded
|
86
|
-
end
|
87
|
-
|
88
|
-
# Convert to hash or JSON as required
|
89
|
-
def to_hash
|
90
|
-
@data
|
91
|
-
end
|
92
|
-
|
93
|
-
def to_json(*_args)
|
94
|
-
@data.to_json
|
95
|
-
end
|
96
|
-
|
97
|
-
# Method to send data to an endpoint
|
98
|
-
def send_to_endpoint
|
99
|
-
root_key = @data.keys.first
|
100
|
-
action_type = root_key.to_s
|
101
|
-
|
102
|
-
uri = URI.parse("http://localhost:4567/dsl/#{action_type}")
|
103
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
104
|
-
request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' })
|
105
|
-
payload = { action_type: action_type, data: @data }
|
106
|
-
request.body = payload.to_json
|
107
|
-
|
108
|
-
response = http.request(request)
|
109
|
-
puts "Response: #{response.code} - #{response.message}"
|
110
|
-
puts "Endpoint: #{uri}"
|
111
|
-
end
|
112
|
-
end
|
data/lib/dsl_process_data.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class DSLProcessData
|
4
|
-
PROCESSORS = [{ file_collector: ProcessFileCollector }].freeze
|
5
|
-
|
6
|
-
# Method to process the JSON file after initial evaluation
|
7
|
-
def process(base_path, input_file, output_file)
|
8
|
-
json_file_path = File.join(base_path, input_file)
|
9
|
-
data = JSON.parse(File.read(json_file_path))
|
10
|
-
|
11
|
-
# Loop through the processors and execute matching ones
|
12
|
-
PROCESSORS.each do |processor_entry|
|
13
|
-
key, processor_class = processor_entry.first
|
14
|
-
processor = processor_class.new(key)
|
15
|
-
|
16
|
-
next unless processor.match?(data)
|
17
|
-
|
18
|
-
result = processor.execute(data)
|
19
|
-
|
20
|
-
data['process-data'] ||= {}
|
21
|
-
|
22
|
-
result.each do |key, result|
|
23
|
-
data['process-data'][key.to_s] = result unless result.empty?
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Write the updated JSON data to an extended file
|
28
|
-
extended_output_file = File.join(base_path, output_file)
|
29
|
-
File.write(extended_output_file, JSON.pretty_generate(data))
|
30
|
-
end
|
31
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# file: lib/process_file_collector.rb
|
4
|
-
|
5
|
-
class ProcessFileCollector < BaseProcess
|
6
|
-
def initialize(key)
|
7
|
-
super
|
8
|
-
@found = nil
|
9
|
-
# @matcher = ->(key, value) { key.to_s == 'file_collector' && value.is_a?(Hash) }
|
10
|
-
@matcher = lambda do |key, value|
|
11
|
-
if key.to_s == 'file_collector'
|
12
|
-
if value.is_a?(Array)
|
13
|
-
value.any? { |v| v.is_a?(Hash) }
|
14
|
-
else
|
15
|
-
value.is_a?(Hash)
|
16
|
-
end
|
17
|
-
else
|
18
|
-
false
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def match?(input)
|
24
|
-
@found = deep_match(input, @matcher)
|
25
|
-
!@found.empty?
|
26
|
-
end
|
27
|
-
|
28
|
-
def execute(_input)
|
29
|
-
# Iterate over each `file_collector` found and process individually
|
30
|
-
results = {}
|
31
|
-
|
32
|
-
@found.each do |data|
|
33
|
-
next unless data.is_a?(Hash)
|
34
|
-
|
35
|
-
# Extract the `as` key if present
|
36
|
-
as_key = data['as']
|
37
|
-
|
38
|
-
working_directory = File.expand_path(data['root'])
|
39
|
-
|
40
|
-
options = Appydave::Tools::GptContext::Options.new(
|
41
|
-
working_directory: working_directory,
|
42
|
-
include_patterns: extract_patterns(data.dig('files', 'include')),
|
43
|
-
exclude_patterns: extract_patterns(data.dig('files', 'exclude')),
|
44
|
-
format: 'json',
|
45
|
-
line_limit: data['line_length']
|
46
|
-
)
|
47
|
-
|
48
|
-
collector = Appydave::Tools::GptContext::FileCollector.new(options)
|
49
|
-
json = collector.build
|
50
|
-
|
51
|
-
# Structuring the result under `process-data` with `as` as key
|
52
|
-
result_data = {
|
53
|
-
type: 'file_collector',
|
54
|
-
data: {
|
55
|
-
working_directory: working_directory,
|
56
|
-
files: JSON.parse(json)
|
57
|
-
}
|
58
|
-
}
|
59
|
-
|
60
|
-
# If `as` key exists, use it to store under process-data with that identifier
|
61
|
-
if as_key
|
62
|
-
results[as_key] = result_data
|
63
|
-
else
|
64
|
-
# Generate a unique key if no `as` key is defined
|
65
|
-
unique_key = "file_collector_#{results.size + 1}"
|
66
|
-
results[unique_key] = result_data
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
results
|
71
|
-
rescue SyntaxError, NameError, NoMethodError => e
|
72
|
-
puts "Ruby evaluation error in ProcessFileCollector: #{e.message}"
|
73
|
-
puts "Error occurred at: #{e.backtrace.first}"
|
74
|
-
{}
|
75
|
-
rescue StandardError => e
|
76
|
-
puts "Unexpected error in ProcessFileCollector: #{e.message}"
|
77
|
-
puts e.backtrace.join("\n")
|
78
|
-
{}
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def extract_patterns(files_data)
|
84
|
-
if files_data.is_a?(Hash)
|
85
|
-
[files_data['param1']]
|
86
|
-
elsif files_data.is_a?(Array)
|
87
|
-
files_data.map { |entry| entry['param1'] }
|
88
|
-
else
|
89
|
-
[]
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|