lex-affordance 0.1.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 +7 -0
- data/README.md +65 -0
- data/lib/legion/extensions/affordance/actors/scan.rb +17 -0
- data/lib/legion/extensions/affordance/client.rb +24 -0
- data/lib/legion/extensions/affordance/helpers/affordance_field.rb +119 -0
- data/lib/legion/extensions/affordance/helpers/affordance_item.rb +71 -0
- data/lib/legion/extensions/affordance/helpers/constants.rb +38 -0
- data/lib/legion/extensions/affordance/runners/affordance.rb +86 -0
- data/lib/legion/extensions/affordance/version.rb +9 -0
- data/lib/legion/extensions/affordance.rb +16 -0
- metadata +70 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c87927c90cc5dfc5bd73e728ad8952b88ac73eaf98d287f1c50f250694765e5b
|
|
4
|
+
data.tar.gz: 334f20991fedba8202b3d846780d50b69874bbb452af0d529763b080a1845867
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 03e2342c50eab44e9773bbcc9d7e7d8f12418807d06e5dc4a9665ccbb6a093239b851e2a2e44bc881260a73bc80968c324e92730ae25dc262e54d415142d65b2
|
|
7
|
+
data.tar.gz: e677b9ed5bc2cc6c2d645c07c9514cc41969ad723ffda66bf8276daa1b73c9d9972d2c3b45fbba7f0db66bbd911ae532ad472f9e623f0e218431fe0ce33e1f39
|
data/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# lex-affordance
|
|
2
|
+
|
|
3
|
+
Gibson's ecological affordance perception for LegionIO — detecting action possibilities in the environment.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Models J.J. Gibson's ecological theory of affordances: the agent perceives its environment in terms of what actions are possible, blocked, risky, or threatening given its current capabilities. The extension maintains an affordance field that tracks opportunities, threats, and action feasibility. Affordances decay over time as the environment changes.
|
|
8
|
+
|
|
9
|
+
## Core Concept: The Affordance Field
|
|
10
|
+
|
|
11
|
+
An affordance describes an action-environment relationship from the agent's perspective:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Register what the agent can do
|
|
15
|
+
client.register_capability(name: :make_api_call, domain: :http, level: 0.9)
|
|
16
|
+
|
|
17
|
+
# Detect what the environment affords
|
|
18
|
+
client.detect_affordance(
|
|
19
|
+
action: :deploy_service,
|
|
20
|
+
domain: :infrastructure,
|
|
21
|
+
affordance_type: :action_possible,
|
|
22
|
+
requires: [:make_api_call],
|
|
23
|
+
relevance: 0.8
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Check if an action is feasible
|
|
27
|
+
result = client.evaluate_action(action: :deploy_service, domain: :infrastructure)
|
|
28
|
+
# => { feasible: true, reason: :capable, risks: [], relevance: 0.8 }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
client = Legion::Extensions::Affordance::Client.new
|
|
35
|
+
|
|
36
|
+
# Set environmental conditions
|
|
37
|
+
client.set_environment(property: :network_available, value: true, domain: :infrastructure)
|
|
38
|
+
|
|
39
|
+
# List what's immediately actionable (relevance >= 0.5)
|
|
40
|
+
client.actionable_affordances
|
|
41
|
+
# => { affordances: [...], count: 2 }
|
|
42
|
+
|
|
43
|
+
# Check for threats
|
|
44
|
+
client.current_threats
|
|
45
|
+
# => { threats: [...], count: 1 }
|
|
46
|
+
|
|
47
|
+
# Maintenance (also runs automatically every 30s via Scan actor)
|
|
48
|
+
client.update_affordances
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Integration
|
|
52
|
+
|
|
53
|
+
The `Scan` actor decays affordances every 30 seconds automatically. Wire into lex-tick's `action_selection` phase to filter candidate actions by feasibility before execution.
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bundle install
|
|
59
|
+
bundle exec rspec
|
|
60
|
+
bundle exec rubocop
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Affordance
|
|
6
|
+
module Actors
|
|
7
|
+
class Scan < Legion::Extensions::Actors::Every
|
|
8
|
+
INTERVAL = 30
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
Runners::Affordance.instance_method(:update_affordances).bind_call(runner_instance)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/affordance/helpers/constants'
|
|
4
|
+
require 'legion/extensions/affordance/helpers/affordance_item'
|
|
5
|
+
require 'legion/extensions/affordance/helpers/affordance_field'
|
|
6
|
+
require 'legion/extensions/affordance/runners/affordance'
|
|
7
|
+
|
|
8
|
+
module Legion
|
|
9
|
+
module Extensions
|
|
10
|
+
module Affordance
|
|
11
|
+
class Client
|
|
12
|
+
include Runners::Affordance
|
|
13
|
+
|
|
14
|
+
def initialize(field: nil, **)
|
|
15
|
+
@field = field || Helpers::AffordanceField.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :field
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Affordance
|
|
6
|
+
module Helpers
|
|
7
|
+
class AffordanceField
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
attr_reader :affordances, :capabilities, :environment
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@affordances = {}
|
|
14
|
+
@capabilities = {}
|
|
15
|
+
@environment = {}
|
|
16
|
+
@counter = 0
|
|
17
|
+
@history = []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def register_capability(name:, domain: :general, level: 1.0)
|
|
21
|
+
return nil if @capabilities.size >= MAX_CAPABILITIES
|
|
22
|
+
|
|
23
|
+
@capabilities[name] = { domain: domain, level: level.to_f.clamp(0.0, 1.0) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def set_environment(property:, value:, domain: :general)
|
|
27
|
+
return nil if @environment.size >= MAX_ENVIRONMENT_PROPS && !@environment.key?(property)
|
|
28
|
+
|
|
29
|
+
@environment[property] = { value: value, domain: domain, updated_at: Time.now.utc }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def detect_affordance(action:, domain:, affordance_type:, requires: [], relevance: DEFAULT_RELEVANCE)
|
|
33
|
+
return nil unless AFFORDANCE_TYPES.include?(affordance_type)
|
|
34
|
+
return nil if @affordances.size >= MAX_AFFORDANCES
|
|
35
|
+
|
|
36
|
+
@counter += 1
|
|
37
|
+
aff_id = :"aff_#{@counter}"
|
|
38
|
+
aff = AffordanceItem.new(
|
|
39
|
+
id: aff_id, action: action, domain: domain,
|
|
40
|
+
affordance_type: affordance_type, requires: requires, relevance: relevance
|
|
41
|
+
)
|
|
42
|
+
@affordances[aff_id] = aff
|
|
43
|
+
record_detection(aff)
|
|
44
|
+
aff
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def evaluate_action(action:, domain:)
|
|
48
|
+
matching = @affordances.values.select { |a| a.action == action && a.domain == domain }
|
|
49
|
+
return { feasible: false, reason: :no_affordance } if matching.empty?
|
|
50
|
+
|
|
51
|
+
check_blockers(matching) || build_evaluation(matching)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def actionable_affordances
|
|
55
|
+
@affordances.values.select(&:actionable?).sort_by { |a| -a.relevance }.map(&:to_h)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def threats
|
|
59
|
+
@affordances.values.select(&:threatening?).map(&:to_h)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def affordances_in(domain:)
|
|
63
|
+
@affordances.values.select { |a| a.domain == domain }.map(&:to_h)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def decay_all
|
|
67
|
+
@affordances.each_value(&:decay)
|
|
68
|
+
@affordances.reject! { |_, a| a.faded? }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def to_h
|
|
72
|
+
{
|
|
73
|
+
affordance_count: @affordances.size,
|
|
74
|
+
capability_count: @capabilities.size,
|
|
75
|
+
environment_props: @environment.size,
|
|
76
|
+
actionable_count: @affordances.values.count(&:actionable?),
|
|
77
|
+
blocked_count: @affordances.values.count(&:blocked?),
|
|
78
|
+
threat_count: @affordances.values.count(&:threatening?),
|
|
79
|
+
history_size: @history.size
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def check_blockers(matching)
|
|
86
|
+
blockers = matching.select(&:blocked?)
|
|
87
|
+
return nil if blockers.empty?
|
|
88
|
+
|
|
89
|
+
{ feasible: false, reason: :blocked, blockers: blockers.map(&:to_h) }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def build_evaluation(matching)
|
|
93
|
+
capabilities_met = check_requirements(matching)
|
|
94
|
+
{
|
|
95
|
+
feasible: capabilities_met,
|
|
96
|
+
reason: capabilities_met ? :capable : :missing_capabilities,
|
|
97
|
+
risks: matching.select(&:risky?).map(&:to_h),
|
|
98
|
+
relevance: matching.map(&:relevance).max,
|
|
99
|
+
affordance_count: matching.size
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def check_requirements(affordances)
|
|
104
|
+
required = affordances.flat_map(&:requires).uniq
|
|
105
|
+
return true if required.empty?
|
|
106
|
+
|
|
107
|
+
required.all? { |r| @capabilities.key?(r) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def record_detection(affordance)
|
|
111
|
+
@history << { id: affordance.id, action: affordance.action, type: affordance.affordance_type,
|
|
112
|
+
at: Time.now.utc }
|
|
113
|
+
@history.shift while @history.size > MAX_HISTORY
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Affordance
|
|
6
|
+
module Helpers
|
|
7
|
+
class AffordanceItem
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
attr_reader :id, :action, :domain, :affordance_type, :requires, :detected_at
|
|
11
|
+
attr_accessor :relevance
|
|
12
|
+
|
|
13
|
+
def initialize(id:, action:, domain:, affordance_type:, requires: [], relevance: DEFAULT_RELEVANCE)
|
|
14
|
+
@id = id
|
|
15
|
+
@action = action
|
|
16
|
+
@domain = domain
|
|
17
|
+
@affordance_type = affordance_type
|
|
18
|
+
@requires = Array(requires)
|
|
19
|
+
@relevance = relevance.to_f.clamp(0.0, 1.0)
|
|
20
|
+
@detected_at = Time.now.utc
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def actionable?
|
|
24
|
+
%i[action_possible resource_available opportunity].include?(@affordance_type) &&
|
|
25
|
+
@relevance >= ACTIONABLE_THRESHOLD
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def blocked?
|
|
29
|
+
@affordance_type == :action_blocked
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def risky?
|
|
33
|
+
@affordance_type == :action_risky
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def threatening?
|
|
37
|
+
@affordance_type == :threat
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def decay
|
|
41
|
+
@relevance = [@relevance - RELEVANCE_DECAY, 0.0].max
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def faded?
|
|
45
|
+
@relevance <= RELEVANCE_FLOOR
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def relevance_label
|
|
49
|
+
RELEVANCE_LABELS.each { |range, lbl| return lbl if range.cover?(@relevance) }
|
|
50
|
+
:negligible
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_h
|
|
54
|
+
{
|
|
55
|
+
id: @id,
|
|
56
|
+
action: @action,
|
|
57
|
+
domain: @domain,
|
|
58
|
+
affordance_type: @affordance_type,
|
|
59
|
+
requires: @requires,
|
|
60
|
+
relevance: @relevance.round(4),
|
|
61
|
+
relevance_label: relevance_label,
|
|
62
|
+
actionable: actionable?,
|
|
63
|
+
blocked: blocked?,
|
|
64
|
+
risky: risky?
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Affordance
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_AFFORDANCES = 200
|
|
9
|
+
MAX_CAPABILITIES = 50
|
|
10
|
+
MAX_ENVIRONMENT_PROPS = 100
|
|
11
|
+
MAX_HISTORY = 200
|
|
12
|
+
|
|
13
|
+
RELEVANCE_FLOOR = 0.05
|
|
14
|
+
RELEVANCE_DECAY = 0.01
|
|
15
|
+
DEFAULT_RELEVANCE = 0.5
|
|
16
|
+
URGENCY_BOOST = 0.2
|
|
17
|
+
|
|
18
|
+
CAPABILITY_MATCH_THRESHOLD = 0.3
|
|
19
|
+
ACTIONABLE_THRESHOLD = 0.5
|
|
20
|
+
|
|
21
|
+
AFFORDANCE_TYPES = %i[
|
|
22
|
+
action_possible action_blocked action_risky
|
|
23
|
+
resource_available resource_depleted
|
|
24
|
+
opportunity threat neutral
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
RELEVANCE_LABELS = {
|
|
28
|
+
(0.8..) => :critical,
|
|
29
|
+
(0.6...0.8) => :important,
|
|
30
|
+
(0.4...0.6) => :moderate,
|
|
31
|
+
(0.2...0.4) => :minor,
|
|
32
|
+
(..0.2) => :negligible
|
|
33
|
+
}.freeze
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Affordance
|
|
6
|
+
module Runners
|
|
7
|
+
module Affordance
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def register_capability(name:, domain: :general, level: 1.0, **)
|
|
12
|
+
Legion::Logging.debug "[affordance] capability: #{name} domain=#{domain}"
|
|
13
|
+
cap = field.register_capability(name: name, domain: domain, level: level)
|
|
14
|
+
if cap
|
|
15
|
+
{ success: true, capability: name, capabilities: field.capabilities.size }
|
|
16
|
+
else
|
|
17
|
+
{ success: false, reason: :limit_reached }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def set_environment(property:, value:, domain: :general, **)
|
|
22
|
+
Legion::Logging.debug "[affordance] env: #{property}=#{value}"
|
|
23
|
+
result = field.set_environment(property: property, value: value, domain: domain)
|
|
24
|
+
if result
|
|
25
|
+
{ success: true, property: property }
|
|
26
|
+
else
|
|
27
|
+
{ success: false, reason: :limit_reached }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def detect_affordance(action:, domain:, affordance_type:, requires: [], relevance: nil, **)
|
|
32
|
+
rel = relevance || Helpers::Constants::DEFAULT_RELEVANCE
|
|
33
|
+
Legion::Logging.debug "[affordance] detect: #{action} type=#{affordance_type}"
|
|
34
|
+
aff = field.detect_affordance(
|
|
35
|
+
action: action, domain: domain, affordance_type: affordance_type.to_sym,
|
|
36
|
+
requires: requires, relevance: rel
|
|
37
|
+
)
|
|
38
|
+
if aff
|
|
39
|
+
{ success: true, affordance: aff.to_h }
|
|
40
|
+
else
|
|
41
|
+
{ success: false, reason: :invalid_or_full }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def evaluate_action(action:, domain:, **)
|
|
46
|
+
result = field.evaluate_action(action: action, domain: domain)
|
|
47
|
+
Legion::Logging.debug "[affordance] evaluate: #{action} feasible=#{result[:feasible]}"
|
|
48
|
+
{ success: true, **result }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def actionable_affordances(**)
|
|
52
|
+
items = field.actionable_affordances
|
|
53
|
+
{ success: true, affordances: items, count: items.size }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def current_threats(**)
|
|
57
|
+
items = field.threats
|
|
58
|
+
{ success: true, threats: items, count: items.size }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def affordances_in_domain(domain:, **)
|
|
62
|
+
items = field.affordances_in(domain: domain)
|
|
63
|
+
{ success: true, affordances: items, count: items.size }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def update_affordances(**)
|
|
67
|
+
Legion::Logging.debug '[affordance] tick'
|
|
68
|
+
field.decay_all
|
|
69
|
+
{ success: true, remaining: field.affordances.size }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def affordance_stats(**)
|
|
73
|
+
Legion::Logging.debug '[affordance] stats'
|
|
74
|
+
{ success: true, stats: field.to_h }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def field
|
|
80
|
+
@field ||= Helpers::AffordanceField.new
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/affordance/version'
|
|
4
|
+
require 'legion/extensions/affordance/helpers/constants'
|
|
5
|
+
require 'legion/extensions/affordance/helpers/affordance_item'
|
|
6
|
+
require 'legion/extensions/affordance/helpers/affordance_field'
|
|
7
|
+
require 'legion/extensions/affordance/runners/affordance'
|
|
8
|
+
require 'legion/extensions/affordance/client'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module Affordance
|
|
13
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined?(:Core)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-affordance
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: Gibson's ecological affordance perception for LegionIO — detecting action
|
|
27
|
+
possibilities in the environment
|
|
28
|
+
email:
|
|
29
|
+
- matthewdiverson@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- README.md
|
|
35
|
+
- lib/legion/extensions/affordance.rb
|
|
36
|
+
- lib/legion/extensions/affordance/actors/scan.rb
|
|
37
|
+
- lib/legion/extensions/affordance/client.rb
|
|
38
|
+
- lib/legion/extensions/affordance/helpers/affordance_field.rb
|
|
39
|
+
- lib/legion/extensions/affordance/helpers/affordance_item.rb
|
|
40
|
+
- lib/legion/extensions/affordance/helpers/constants.rb
|
|
41
|
+
- lib/legion/extensions/affordance/runners/affordance.rb
|
|
42
|
+
- lib/legion/extensions/affordance/version.rb
|
|
43
|
+
homepage: https://github.com/LegionIO/lex-affordance
|
|
44
|
+
licenses:
|
|
45
|
+
- MIT
|
|
46
|
+
metadata:
|
|
47
|
+
homepage_uri: https://github.com/LegionIO/lex-affordance
|
|
48
|
+
source_code_uri: https://github.com/LegionIO/lex-affordance
|
|
49
|
+
documentation_uri: https://github.com/LegionIO/lex-affordance/blob/master/README.md
|
|
50
|
+
changelog_uri: https://github.com/LegionIO/lex-affordance/blob/master/CHANGELOG.md
|
|
51
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-affordance/issues
|
|
52
|
+
rubygems_mfa_required: 'true'
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
require_paths:
|
|
55
|
+
- lib
|
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '3.4'
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '0'
|
|
66
|
+
requirements: []
|
|
67
|
+
rubygems_version: 3.6.9
|
|
68
|
+
specification_version: 4
|
|
69
|
+
summary: LegionIO affordance perception extension
|
|
70
|
+
test_files: []
|