lex-uais 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b6c9562fc2b37ffa26eec00a63d7c0bd0bccb34feb807ba5f7f9e5a1d36d253f
4
+ data.tar.gz: 4b7a16c4dc99c84dd2766b770ce6f569ab65f2c46a442ae8b18dc3407ef08d51
5
+ SHA512:
6
+ metadata.gz: 465dc286dcfcfa2471447df60517e57c727cc7e53a966b16ac69ed5c1766448cf1f5e871d72e9d9dace0c7818b721d4d4a8a1b87a0fa223e8d3347c3d632ed64
7
+ data.tar.gz: 3ad6cb3bf597404a8d5931e34b024661883236b1567c68605b0e708363dbd1ddbd0848c1125acb18e82a345c89a103b4718aa86c3b307289a12c449fc3eab565
@@ -0,0 +1,7 @@
1
+ # Auto-generated from team-config.yml
2
+ # Team: extensions
3
+ #
4
+ # To apply: scripts/apply-codeowners.sh lex-uais
5
+
6
+ * @LegionIO/maintainers
7
+ * @LegionIO/extensions
@@ -0,0 +1,34 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ schedule:
7
+ - cron: '0 9 * * 1'
8
+
9
+ jobs:
10
+ ci:
11
+ uses: LegionIO/.github/.github/workflows/ci.yml@main
12
+
13
+ lint:
14
+ uses: LegionIO/.github/.github/workflows/lint-patterns.yml@main
15
+
16
+ security:
17
+ uses: LegionIO/.github/.github/workflows/security-scan.yml@main
18
+
19
+ version-changelog:
20
+ uses: LegionIO/.github/.github/workflows/version-changelog.yml@main
21
+
22
+ dependency-review:
23
+ uses: LegionIO/.github/.github/workflows/dependency-review.yml@main
24
+
25
+ stale:
26
+ if: github.event_name == 'schedule'
27
+ uses: LegionIO/.github/.github/workflows/stale.yml@main
28
+
29
+ release:
30
+ needs: [ci, lint]
31
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
32
+ uses: LegionIO/.github/.github/workflows/release.yml@main
33
+ secrets:
34
+ rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,50 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.4
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 160
8
+
9
+ Layout/SpaceAroundEqualsInParameterDefault:
10
+ EnforcedStyle: space
11
+
12
+ Layout/HashAlignment:
13
+ EnforcedHashRocketStyle: table
14
+ EnforcedColonStyle: table
15
+
16
+ Metrics/MethodLength:
17
+ Max: 50
18
+
19
+ Metrics/ClassLength:
20
+ Max: 1500
21
+
22
+ Metrics/ModuleLength:
23
+ Max: 1500
24
+
25
+ Metrics/BlockLength:
26
+ Max: 40
27
+ Exclude:
28
+ - 'spec/**/*'
29
+
30
+ Metrics/AbcSize:
31
+ Max: 60
32
+
33
+ Metrics/CyclomaticComplexity:
34
+ Max: 15
35
+
36
+ Metrics/PerceivedComplexity:
37
+ Max: 17
38
+
39
+ Style/Documentation:
40
+ Enabled: false
41
+
42
+ Style/SymbolArray:
43
+ Enabled: true
44
+
45
+ Style/FrozenStringLiteralComment:
46
+ Enabled: true
47
+ EnforcedStyle: always
48
+
49
+ Naming/FileName:
50
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-03-27
4
+
5
+ ### Added
6
+ - Initial release
7
+ - UAIS agent registration client (register, deregister, check, AIRB status)
8
+ - Mock adapter for development/testing (default when no base_url configured)
9
+ - Soft-warn enforcement (registration failures log warnings, never block)
10
+ - Net::HTTP-based connection helper
11
+ - Standalone Client class for use outside Legion framework
data/CLAUDE.md ADDED
@@ -0,0 +1,75 @@
1
+ # lex-uais: UAIS Registration Extension for LegionIO
2
+
3
+ **Repository Level 3 Documentation**
4
+ - **Parent (Level 2)**: `/Users/miverso2/rubymine/legion/extensions/CLAUDE.md`
5
+ - **Parent (Level 1)**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
6
+
7
+ ## Purpose
8
+
9
+ Legion Extension that provides a client for United AI Studio (UAIS) agent registration. Registers, deregisters, and queries Digital Worker status with the UAIS platform. Defaults to mock mode when no `base_url` is configured, and uses soft-warn enforcement so registration failures never block operations.
10
+
11
+ **Version**: 0.1.0
12
+ **GitHub**: https://github.com/LegionIO/lex-uais
13
+ **License**: Apache-2.0
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ Legion::Extensions::Uais
19
+ ├── Runners/
20
+ │ └── Registration # register_worker, deregister_worker, check_registration, airb_status
21
+ ├── Helpers/
22
+ │ └── Client # Net::HTTP connection builder with build_request helper
23
+ └── Client # Standalone client class (includes all runners)
24
+ ```
25
+
26
+ ## Key Files
27
+
28
+ | Path | Purpose |
29
+ |------|---------|
30
+ | `lib/legion/extensions/uais.rb` | Entry point, extension registration, `default_settings` |
31
+ | `lib/legion/extensions/uais/runners/registration.rb` | Registration logic (all 4 operations) + mock adapter + soft-warn |
32
+ | `lib/legion/extensions/uais/helpers/client.rb` | Net::HTTP connection builder with timeout and auth |
33
+ | `lib/legion/extensions/uais/client.rb` | Standalone `Client` class for use outside Legion framework |
34
+
35
+ ## Runner Methods
36
+
37
+ All methods are in `Runners::Registration` and return hashes with status info. On failure, they log warnings via `soft_warn` and return error hashes instead of raising.
38
+
39
+ | Method | Args | Notes |
40
+ |--------|------|-------|
41
+ | `register_worker` | `worker_id:, name:, owner_msid:, extension_name:, **opts` | opts: `risk_tier:`, `team:`, `business_role:` |
42
+ | `deregister_worker` | `worker_id:, reason: nil` | |
43
+ | `check_registration` | `worker_id:` | |
44
+ | `airb_status` | `worker_id:` | AIRB approval status |
45
+
46
+ ## Mock Mode
47
+
48
+ Mock mode is active when `mock: true` (default) or `base_url` is nil. All operations return success with `mock: true` in the response hash.
49
+
50
+ ## Notes
51
+
52
+ - `default_settings` defines `mock: true`, `base_url: nil`, `timeout: 30`
53
+ - Uses Net::HTTP directly (no Faraday dependency)
54
+ - `soft_warn` logs via `Legion::Logging.warn` if available; never raises
55
+ - Standalone `Client` class auto-enables mock when `base_url` is nil
56
+
57
+ ## Dependencies
58
+
59
+ | Gem | Purpose |
60
+ |-----|---------|
61
+ | `legion-json` | JSON serialization |
62
+ | `legion-logging` | Logging (optional, for soft-warn) |
63
+ | `legion-settings` | Configuration management |
64
+
65
+ ## Testing
66
+
67
+ ```bash
68
+ bundle install
69
+ bundle exec rspec
70
+ bundle exec rubocop
71
+ ```
72
+
73
+ ---
74
+
75
+ **Maintained By**: Matthew Iverson (@Esity)
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop'
11
+ gem 'simplecov'
12
+
13
+ gem 'legion-cache'
14
+ gem 'legion-crypt'
15
+ gem 'legion-data'
16
+ gem 'legion-transport'
17
+ end
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # lex-uais
2
+
3
+ LegionIO extension for United AI Studio (UAIS) agent registration. Provides a client for registering, deregistering, and checking Digital Worker registration status with the UAIS platform.
4
+
5
+ ## Features
6
+
7
+ - Register/deregister Digital Workers with UAIS
8
+ - Check registration status and AIRB approval
9
+ - Mock adapter (default) for development and testing
10
+ - Soft-warn enforcement: failures log warnings but never block operations
11
+ - Standalone `Client` class for use outside the Legion framework
12
+
13
+ ## Installation
14
+
15
+ Add to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'lex-uais'
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Standalone Client
24
+
25
+ ```ruby
26
+ require 'legion/extensions/uais/client'
27
+
28
+ # Mock mode (default when no base_url)
29
+ client = Legion::Extensions::Uais::Client.new
30
+ result = client.register_worker(worker_id: 'w-123', name: 'InvoiceBot', owner_msid: 'ms123', extension_name: 'lex-invoice')
31
+
32
+ # Live mode
33
+ client = Legion::Extensions::Uais::Client.new(base_url: 'https://api.uais.uhg.com', api_key: 'key')
34
+ result = client.register_worker(worker_id: 'w-123', name: 'InvoiceBot', owner_msid: 'ms123', extension_name: 'lex-invoice')
35
+ ```
36
+
37
+ ### Within Legion Framework
38
+
39
+ The extension is auto-discovered via the `lex-*` naming convention. Configure via settings:
40
+
41
+ ```yaml
42
+ uais:
43
+ options:
44
+ base_url: https://api.uais.uhg.com
45
+ api_key: <vault-sourced>
46
+ mock: false
47
+ timeout: 30
48
+ ```
49
+
50
+ ## Runner Methods
51
+
52
+ | Method | Description |
53
+ |--------|-------------|
54
+ | `register_worker` | Register a Digital Worker with UAIS |
55
+ | `deregister_worker` | Deregister a worker |
56
+ | `check_registration` | Check if a worker is registered |
57
+ | `airb_status` | Check AIRB approval status |
58
+
59
+ ## Development
60
+
61
+ ```bash
62
+ bundle install
63
+ bundle exec rspec
64
+ bundle exec rubocop
65
+ ```
66
+
67
+ ## License
68
+
69
+ Apache-2.0
data/lex-uais.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/uais/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-uais'
7
+ spec.version = Legion::Extensions::Uais::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['legionio@esity.info']
10
+
11
+ spec.summary = 'LegionIO extension for United AI Studio (UAIS) registration'
12
+ spec.description = 'UAIS agent registration client with mock adapter and soft-warn enforcement'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-uais'
14
+ spec.license = 'Apache-2.0'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['rubygems_mfa_required'] = 'true'
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = spec.homepage
20
+ spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
21
+
22
+ spec.files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency 'legion-json', '>= 1.2'
26
+ spec.add_dependency 'legion-logging', '>= 0.3'
27
+ spec.add_dependency 'legion-settings', '>= 0.3'
28
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Uais
6
+ class Client
7
+ include Helpers::Client
8
+ include Runners::Registration
9
+
10
+ attr_reader :opts
11
+
12
+ def initialize(base_url: nil, api_key: nil, timeout: 30, mock: nil, **extra)
13
+ mock = base_url.nil? if mock.nil?
14
+ @opts = { base_url: base_url, api_key: api_key, timeout: timeout, mock: mock, **extra }
15
+ end
16
+
17
+ def settings
18
+ { options: @opts }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Uais
9
+ module Helpers
10
+ module Client
11
+ def connection(base_url:, api_key: nil, timeout: 30, **_opts)
12
+ uri = URI.parse(base_url)
13
+ http = Net::HTTP.new(uri.host, uri.port)
14
+ http.use_ssl = uri.scheme == 'https'
15
+ http.open_timeout = timeout
16
+ http.read_timeout = timeout
17
+ http.instance_variable_set(:@_api_key, api_key)
18
+
19
+ def http.api_key
20
+ @_api_key
21
+ end
22
+
23
+ http
24
+ end
25
+
26
+ def build_request(method, path, body: nil, api_key: nil)
27
+ klass = case method.to_s.downcase
28
+ when 'post' then Net::HTTP::Post
29
+ when 'put' then Net::HTTP::Put
30
+ when 'get' then Net::HTTP::Get
31
+ when 'delete' then Net::HTTP::Delete
32
+ else
33
+ raise ArgumentError, "unsupported HTTP method: #{method}"
34
+ end
35
+
36
+ req = klass.new(path)
37
+ req['Content-Type'] = 'application/json'
38
+ req['Authorization'] = "Bearer #{api_key}" if api_key
39
+ req.body = Legion::JSON.dump(body) if body
40
+ req
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Uais
6
+ module Runners
7
+ module Registration
8
+ def register_worker(worker_id:, name:, owner_msid:, extension_name:, **opts)
9
+ return mock_register(worker_id, name) if mock_mode?
10
+
11
+ payload = {
12
+ worker_id: worker_id,
13
+ name: name,
14
+ owner_msid: owner_msid,
15
+ extension_name: extension_name,
16
+ risk_tier: opts[:risk_tier],
17
+ team: opts[:team],
18
+ business_role: opts[:business_role]
19
+ }.compact
20
+
21
+ response = uais_post('/api/v1/agents', payload)
22
+ { registered: response[:success], uais_id: response[:data]&.dig(:id), worker_id: worker_id }
23
+ rescue StandardError => e
24
+ soft_warn("UAIS registration failed for #{worker_id}: #{e.message}")
25
+ { registered: false, worker_id: worker_id, error: e.message }
26
+ end
27
+
28
+ def deregister_worker(worker_id:, reason: nil)
29
+ return mock_deregister(worker_id) if mock_mode?
30
+
31
+ response = uais_post("/api/v1/agents/#{worker_id}/deregister", { reason: reason })
32
+ { deregistered: response[:success], worker_id: worker_id }
33
+ rescue StandardError => e
34
+ soft_warn("UAIS deregistration failed for #{worker_id}: #{e.message}")
35
+ { deregistered: false, worker_id: worker_id, error: e.message }
36
+ end
37
+
38
+ def check_registration(worker_id:)
39
+ return { registered: true, mock: true } if mock_mode?
40
+
41
+ response = uais_get("/api/v1/agents/#{worker_id}")
42
+ { registered: response[:success], data: response[:data] }
43
+ rescue StandardError => e
44
+ soft_warn("UAIS registration check failed for #{worker_id}: #{e.message}")
45
+ { registered: false, error: e.message }
46
+ end
47
+
48
+ def airb_status(worker_id:)
49
+ return { status: 'approved', mock: true } if mock_mode?
50
+
51
+ response = uais_get("/api/v1/agents/#{worker_id}/airb")
52
+ { status: response.dig(:data, :status), data: response[:data] }
53
+ rescue StandardError => e
54
+ soft_warn("UAIS AIRB check failed for #{worker_id}: #{e.message}")
55
+ { status: 'unknown', error: e.message }
56
+ end
57
+
58
+ private
59
+
60
+ def uais_settings
61
+ settings[:options] || {}
62
+ end
63
+
64
+ def uais_post(path, body)
65
+ conn = connection(**uais_connection_opts)
66
+ req = build_request(:post, path, body: body, api_key: uais_settings[:api_key])
67
+ resp = conn.request(req)
68
+ parse_response(resp)
69
+ end
70
+
71
+ def uais_get(path)
72
+ conn = connection(**uais_connection_opts)
73
+ req = build_request(:get, path, api_key: uais_settings[:api_key])
74
+ resp = conn.request(req)
75
+ parse_response(resp)
76
+ end
77
+
78
+ def uais_connection_opts
79
+ {
80
+ base_url: uais_settings[:base_url] || 'https://api.uais.uhg.com',
81
+ api_key: uais_settings[:api_key],
82
+ timeout: uais_settings[:timeout] || 30
83
+ }
84
+ end
85
+
86
+ def parse_response(resp)
87
+ body = begin
88
+ Legion::JSON.load(resp.body)
89
+ rescue StandardError
90
+ {}
91
+ end
92
+ { success: resp.is_a?(Net::HTTPSuccess), status: resp.code.to_i, data: body }
93
+ end
94
+
95
+ def mock_mode?
96
+ uais_settings[:mock] == true || uais_settings[:base_url].nil?
97
+ end
98
+
99
+ def mock_register(worker_id, name)
100
+ soft_warn("UAIS mock mode: register #{name} (#{worker_id})")
101
+ { registered: true, mock: true, worker_id: worker_id }
102
+ end
103
+
104
+ def mock_deregister(worker_id)
105
+ soft_warn("UAIS mock mode: deregister #{worker_id}")
106
+ { deregistered: true, mock: true, worker_id: worker_id }
107
+ end
108
+
109
+ def soft_warn(message)
110
+ Legion::Logging.warn("[lex-uais] #{message}") if defined?(Legion::Logging)
111
+ end
112
+
113
+ include Helpers::Client if defined?(Helpers::Client)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Uais
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/uais/version'
4
+ require 'legion/extensions/uais/helpers/client'
5
+ require 'legion/extensions/uais/runners/registration'
6
+
7
+ module Legion
8
+ module Extensions
9
+ module Uais
10
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
11
+
12
+ def self.default_settings
13
+ {
14
+ options: {
15
+ base_url: nil,
16
+ api_key: nil,
17
+ mock: true,
18
+ timeout: 30
19
+ }
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/uais/client'
4
+
5
+ RSpec.describe Legion::Extensions::Uais::Client do
6
+ describe '#initialize' do
7
+ it 'defaults to mock mode when no base_url' do
8
+ client = described_class.new
9
+ expect(client.opts[:mock]).to be true
10
+ end
11
+
12
+ it 'defaults to mock mode when base_url is nil' do
13
+ client = described_class.new(base_url: nil)
14
+ expect(client.opts[:mock]).to be true
15
+ end
16
+
17
+ it 'disables mock when base_url is provided and mock not specified' do
18
+ client = described_class.new(base_url: 'https://example.com')
19
+ expect(client.opts[:mock]).to be false
20
+ end
21
+
22
+ it 'respects explicit mock: true even with base_url' do
23
+ client = described_class.new(base_url: 'https://example.com', mock: true)
24
+ expect(client.opts[:mock]).to be true
25
+ end
26
+
27
+ it 'stores api_key' do
28
+ client = described_class.new(api_key: 'test-key')
29
+ expect(client.opts[:api_key]).to eq('test-key')
30
+ end
31
+
32
+ it 'stores timeout' do
33
+ client = described_class.new(timeout: 60)
34
+ expect(client.opts[:timeout]).to eq(60)
35
+ end
36
+ end
37
+
38
+ describe '#settings' do
39
+ it 'wraps opts in :options key' do
40
+ client = described_class.new
41
+ expect(client.settings).to have_key(:options)
42
+ expect(client.settings[:options]).to eq(client.opts)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/uais/helpers/client'
4
+
5
+ RSpec.describe Legion::Extensions::Uais::Helpers::Client do
6
+ let(:test_class) { Class.new { include Legion::Extensions::Uais::Helpers::Client } }
7
+ let(:instance) { test_class.new }
8
+
9
+ describe '#connection' do
10
+ it 'returns a Net::HTTP instance' do
11
+ conn = instance.connection(base_url: 'https://example.com')
12
+ expect(conn).to be_a(Net::HTTP)
13
+ end
14
+
15
+ it 'enables SSL for https' do
16
+ conn = instance.connection(base_url: 'https://example.com')
17
+ expect(conn.use_ssl?).to be true
18
+ end
19
+
20
+ it 'disables SSL for http' do
21
+ conn = instance.connection(base_url: 'http://example.com')
22
+ expect(conn.use_ssl?).to be false
23
+ end
24
+
25
+ it 'sets timeout' do
26
+ conn = instance.connection(base_url: 'https://example.com', timeout: 15)
27
+ expect(conn.open_timeout).to eq(15)
28
+ expect(conn.read_timeout).to eq(15)
29
+ end
30
+ end
31
+
32
+ describe '#build_request' do
33
+ it 'creates a POST request' do
34
+ req = instance.build_request(:post, '/api/v1/agents')
35
+ expect(req).to be_a(Net::HTTP::Post)
36
+ end
37
+
38
+ it 'creates a GET request' do
39
+ req = instance.build_request(:get, '/api/v1/agents/123')
40
+ expect(req).to be_a(Net::HTTP::Get)
41
+ end
42
+
43
+ it 'sets content type to JSON' do
44
+ req = instance.build_request(:post, '/test')
45
+ expect(req['Content-Type']).to eq('application/json')
46
+ end
47
+
48
+ it 'sets authorization header when api_key provided' do
49
+ req = instance.build_request(:get, '/test', api_key: 'my-key')
50
+ expect(req['Authorization']).to eq('Bearer my-key')
51
+ end
52
+
53
+ it 'does not set authorization without api_key' do
54
+ req = instance.build_request(:get, '/test')
55
+ expect(req['Authorization']).to be_nil
56
+ end
57
+
58
+ it 'serializes body as JSON' do
59
+ req = instance.build_request(:post, '/test', body: { name: 'TestBot' })
60
+ expect(req.body).to include('TestBot')
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/uais/client'
4
+
5
+ RSpec.describe Legion::Extensions::Uais::Runners::Registration do
6
+ let(:client) { Legion::Extensions::Uais::Client.new }
7
+ let(:live_client) { Legion::Extensions::Uais::Client.new(base_url: 'https://api.uais.uhg.com', api_key: 'test-key', mock: false) }
8
+
9
+ describe 'mock mode' do
10
+ describe '#register_worker' do
11
+ subject(:result) do
12
+ client.register_worker(worker_id: 'w-123', name: 'TestBot', owner_msid: 'ms456', extension_name: 'lex-test')
13
+ end
14
+
15
+ it 'returns registered: true' do
16
+ expect(result[:registered]).to be true
17
+ end
18
+
19
+ it 'includes mock: true' do
20
+ expect(result[:mock]).to be true
21
+ end
22
+
23
+ it 'includes the worker_id' do
24
+ expect(result[:worker_id]).to eq('w-123')
25
+ end
26
+ end
27
+
28
+ describe '#deregister_worker' do
29
+ subject(:result) { client.deregister_worker(worker_id: 'w-123') }
30
+
31
+ it 'returns deregistered: true' do
32
+ expect(result[:deregistered]).to be true
33
+ end
34
+
35
+ it 'includes mock: true' do
36
+ expect(result[:mock]).to be true
37
+ end
38
+ end
39
+
40
+ describe '#check_registration' do
41
+ subject(:result) { client.check_registration(worker_id: 'w-123') }
42
+
43
+ it 'returns registered: true' do
44
+ expect(result[:registered]).to be true
45
+ end
46
+
47
+ it 'includes mock: true' do
48
+ expect(result[:mock]).to be true
49
+ end
50
+ end
51
+
52
+ describe '#airb_status' do
53
+ subject(:result) { client.airb_status(worker_id: 'w-123') }
54
+
55
+ it 'returns status approved' do
56
+ expect(result[:status]).to eq('approved')
57
+ end
58
+
59
+ it 'includes mock: true' do
60
+ expect(result[:mock]).to be true
61
+ end
62
+ end
63
+ end
64
+
65
+ describe 'mock_mode?' do
66
+ it 'is true when base_url is nil' do
67
+ c = Legion::Extensions::Uais::Client.new(base_url: nil)
68
+ expect(c.send(:mock_mode?)).to be true
69
+ end
70
+
71
+ it 'is true when mock is explicitly true' do
72
+ c = Legion::Extensions::Uais::Client.new(base_url: 'https://example.com', mock: true)
73
+ expect(c.send(:mock_mode?)).to be true
74
+ end
75
+
76
+ it 'is false when base_url is set and mock is false' do
77
+ c = Legion::Extensions::Uais::Client.new(base_url: 'https://example.com', mock: false)
78
+ expect(c.send(:mock_mode?)).to be false
79
+ end
80
+ end
81
+
82
+ describe 'soft-warn on failure' do
83
+ it 'returns error hash instead of raising on register failure' do
84
+ allow(live_client).to receive(:connection).and_raise(Errno::ECONNREFUSED)
85
+ result = live_client.register_worker(worker_id: 'w-fail', name: 'FailBot', owner_msid: 'ms789', extension_name: 'lex-fail')
86
+ expect(result[:registered]).to be false
87
+ expect(result[:error]).to include('Connection refused')
88
+ end
89
+
90
+ it 'returns error hash instead of raising on deregister failure' do
91
+ allow(live_client).to receive(:connection).and_raise(Errno::ECONNREFUSED)
92
+ result = live_client.deregister_worker(worker_id: 'w-fail')
93
+ expect(result[:deregistered]).to be false
94
+ expect(result[:error]).to include('Connection refused')
95
+ end
96
+
97
+ it 'returns error hash instead of raising on check_registration failure' do
98
+ allow(live_client).to receive(:connection).and_raise(Errno::ECONNREFUSED)
99
+ result = live_client.check_registration(worker_id: 'w-fail')
100
+ expect(result[:registered]).to be false
101
+ expect(result[:error]).to include('Connection refused')
102
+ end
103
+
104
+ it 'returns error hash instead of raising on airb_status failure' do
105
+ allow(live_client).to receive(:connection).and_raise(Errno::ECONNREFUSED)
106
+ result = live_client.airb_status(worker_id: 'w-fail')
107
+ expect(result[:status]).to eq('unknown')
108
+ expect(result[:error]).to include('Connection refused')
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Uais do
4
+ it 'has a version number' do
5
+ expect(Legion::Extensions::Uais::VERSION).not_to be_nil
6
+ end
7
+
8
+ it 'version is a string' do
9
+ expect(Legion::Extensions::Uais::VERSION).to be_a(String)
10
+ end
11
+
12
+ describe '.default_settings' do
13
+ subject(:settings) { described_class.default_settings }
14
+
15
+ it 'returns a hash with options' do
16
+ expect(settings).to be_a(Hash)
17
+ expect(settings).to have_key(:options)
18
+ end
19
+
20
+ it 'defaults mock to true' do
21
+ expect(settings[:options][:mock]).to be true
22
+ end
23
+
24
+ it 'defaults base_url to nil' do
25
+ expect(settings[:options][:base_url]).to be_nil
26
+ end
27
+
28
+ it 'defaults timeout to 30' do
29
+ expect(settings[:options][:timeout]).to eq(30)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ # Minimal stubs for Legion framework modules used by lex-uais
6
+ module Legion
7
+ module Extensions
8
+ module Helpers
9
+ module Lex; end
10
+ end
11
+ end
12
+
13
+ module Logging
14
+ def self.warn(msg); end
15
+ def self.info(msg); end
16
+ def self.debug(msg); end
17
+ end
18
+
19
+ module Settings
20
+ def self.[](_key)
21
+ nil
22
+ end
23
+
24
+ def self.dig(*_keys)
25
+ nil
26
+ end
27
+
28
+ def self.merge_settings(key, defaults); end
29
+ end
30
+
31
+ module JSON
32
+ def self.dump(obj)
33
+ require 'json'
34
+ ::JSON.generate(obj)
35
+ end
36
+
37
+ def self.load(str)
38
+ require 'json'
39
+ ::JSON.parse(str, symbolize_names: true)
40
+ end
41
+ end
42
+ end
43
+
44
+ require 'legion/extensions/uais'
45
+
46
+ RSpec.configure do |config|
47
+ config.example_status_persistence_file_path = '.rspec_status'
48
+ config.disable_monkey_patching!
49
+ config.expect_with(:rspec) { |c| c.syntax = :expect }
50
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-uais
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-json
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '1.2'
26
+ - !ruby/object:Gem::Dependency
27
+ name: legion-logging
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.3'
40
+ - !ruby/object:Gem::Dependency
41
+ name: legion-settings
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0.3'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0.3'
54
+ description: UAIS agent registration client with mock adapter and soft-warn enforcement
55
+ email:
56
+ - legionio@esity.info
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".github/CODEOWNERS"
62
+ - ".github/workflows/ci.yml"
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - ".rubocop.yml"
66
+ - CHANGELOG.md
67
+ - CLAUDE.md
68
+ - Gemfile
69
+ - README.md
70
+ - lex-uais.gemspec
71
+ - lib/legion/extensions/uais.rb
72
+ - lib/legion/extensions/uais/client.rb
73
+ - lib/legion/extensions/uais/helpers/client.rb
74
+ - lib/legion/extensions/uais/runners/registration.rb
75
+ - lib/legion/extensions/uais/version.rb
76
+ - spec/legion/extensions/uais/client_spec.rb
77
+ - spec/legion/extensions/uais/helpers/client_spec.rb
78
+ - spec/legion/extensions/uais/runners/registration_spec.rb
79
+ - spec/legion/extensions/uais_spec.rb
80
+ - spec/spec_helper.rb
81
+ homepage: https://github.com/LegionIO/lex-uais
82
+ licenses:
83
+ - Apache-2.0
84
+ metadata:
85
+ rubygems_mfa_required: 'true'
86
+ homepage_uri: https://github.com/LegionIO/lex-uais
87
+ source_code_uri: https://github.com/LegionIO/lex-uais
88
+ changelog_uri: https://github.com/LegionIO/lex-uais/blob/main/CHANGELOG.md
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '3.4'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.6.9
104
+ specification_version: 4
105
+ summary: LegionIO extension for United AI Studio (UAIS) registration
106
+ test_files: []