lex-microsoft_teams 0.6.23 → 0.6.25
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/CHANGELOG.md +13 -0
- data/CLAUDE.md +4 -4
- data/lib/legion/extensions/microsoft_teams/actors/absorb_meeting.rb +60 -0
- data/lib/legion/extensions/microsoft_teams/helpers/browser_auth.rb +3 -3
- data/lib/legion/extensions/microsoft_teams/helpers/graph_client.rb +75 -0
- data/lib/legion/extensions/microsoft_teams/hooks/auth.rb +1 -5
- data/lib/legion/extensions/microsoft_teams/runners/auth.rb +1 -0
- data/lib/legion/extensions/microsoft_teams/version.rb +1 -1
- data/lib/legion/extensions/microsoft_teams.rb +1 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42353a2d92e77ba3659f2ab93c24339efca2e869a4ae18d30621b5fb3e3cd947
|
|
4
|
+
data.tar.gz: 4420f02e9d66c85c673e6411203b06fa49862d9a5a16ef78cf7b55a366da3a8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b165c26eca6e63b1d181a0aee9a2bfadd72039e2cda558bc33fc08e49a1ba1730af044012dcb6936848c03b02ca7d30c00be6678c5e4e9665070f2811b24ec4a
|
|
7
|
+
data.tar.gz: bdae821120daca1fcd888c57cc2a2996aced5b8270e88332235dc4c2ee71fe1a0f4533f6b5a28df6761f78f3bb233e7aafb4f92db4c33fec518cf1d39d78584e
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.6.25] - 2026-03-28
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- `Hooks::Auth` — migrated from v2.0 `Routes::Hooks` pattern to v3.0 `LexDispatch` pattern: replaced instance `route`/`runner_class` overrides with a class-level `self.runner_class`; hook now registers as `POST /api/extensions/microsoft_teams/hooks/auth/handle` (was `/api/hooks/lex/microsoft_teams/auth/callback`)
|
|
9
|
+
- `Runners::Auth` — added `handle` alias for `auth_callback` so LexDispatch's default `:handle` routing resolves correctly
|
|
10
|
+
- `Helpers::BrowserAuth` — updated all three references to the hook redirect URI and probe path from the stale v2.0 path to `/api/extensions/microsoft_teams/hooks/auth/handle`
|
|
11
|
+
|
|
12
|
+
## [0.6.24] - 2026-03-28
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `Actors::AbsorbMeeting` — Subscription actor that listens on `lex.microsoft_teams.absorbers.meeting.absorb` and delegates to `Absorbers::Meeting#absorb`
|
|
16
|
+
- `Helpers::GraphClient` — mixin module wrapping `Helpers::Client#graph_connection` with `graph_get`, `graph_post`, `graph_paginate`, and an inline `GraphError` class for responses other than 200, 201, 204, or 404; 401/403 raise with descriptive messages including the Graph error body when available
|
|
17
|
+
|
|
5
18
|
### Fixed
|
|
6
19
|
- `Absorbers::Meeting#graph_token` — rescue now captures the exception as `=> e` and logs a warning, satisfying the rescue-logging lint rule
|
|
7
20
|
|
data/CLAUDE.md
CHANGED
|
@@ -10,7 +10,7 @@ Legion Extension that connects LegionIO to Microsoft Teams via Graph API and Bot
|
|
|
10
10
|
|
|
11
11
|
**GitHub**: https://github.com/LegionIO/lex-microsoft_teams
|
|
12
12
|
**License**: MIT
|
|
13
|
-
**Version**: 0.6.
|
|
13
|
+
**Version**: 0.6.24
|
|
14
14
|
|
|
15
15
|
## Architecture
|
|
16
16
|
|
|
@@ -64,7 +64,7 @@ Legion::Extensions::MicrosoftTeams
|
|
|
64
64
|
│ ├── TraceRetriever # Retrieves memory traces from the shared store for bot context (2000-token budget, strength-ranked dedup)
|
|
65
65
|
│ └── TransformDefinitions # lex-transformer definitions for conversation extraction and person summary
|
|
66
66
|
├── Hooks/
|
|
67
|
-
│ └── Auth # OAuth callback hook (mount '/callback') → /api/
|
|
67
|
+
│ └── Auth # OAuth callback hook (mount '/callback') → /api/extensions/microsoft_teams/hooks/auth/handle
|
|
68
68
|
├── CLI/
|
|
69
69
|
│ └── Auth # CLI module for `legion lex exec teams auth login/status`
|
|
70
70
|
└── Client # Standalone client (includes all runners)
|
|
@@ -74,10 +74,10 @@ Legion::Extensions::MicrosoftTeams
|
|
|
74
74
|
|
|
75
75
|
Opt-in browser-based OAuth for delegated Microsoft Graph permissions. Two flows:
|
|
76
76
|
|
|
77
|
-
- **Authorization Code + PKCE** (primary): Opens browser for Entra ID login. When the Legion API is running, uses the hook URL (`/api/
|
|
77
|
+
- **Authorization Code + PKCE** (primary): Opens browser for Entra ID login. When the Legion API is running, uses the hook URL (`/api/extensions/microsoft_teams/hooks/auth/handle`) with `Legion::Events` for callback notification; otherwise falls back to an ephemeral local port via `CallbackServer`
|
|
78
78
|
- **Device Code** (fallback): Auto-selected in headless/SSH environments (no `DISPLAY`/`WAYLAND_DISPLAY`)
|
|
79
79
|
|
|
80
|
-
Tokens stored in Vault at a per-user path (`{USER}/microsoft_teams/delegated_token`, where `{USER}` is the system username) with configurable pre-expiry silent refresh. CLI command: `legion auth teams`. Hook route: `
|
|
80
|
+
Tokens stored in Vault at a per-user path (`{USER}/microsoft_teams/delegated_token`, where `{USER}` is the system username) with configurable pre-expiry silent refresh. CLI command: `legion auth teams`. Hook route: `POST /api/extensions/microsoft_teams/hooks/auth/handle` for daemon re-auth (routed through LexDispatch for RBAC/audit).
|
|
81
81
|
|
|
82
82
|
Key files: `Helpers::BrowserAuth` (orchestrator), `Helpers::CallbackServer` (ephemeral TCP), `Runners::Auth` (authorize_url, exchange_code, refresh_delegated_token, auth_callback), `Helpers::TokenCache` (delegated slot), `Hooks::Auth` (hook class with mount path).
|
|
83
83
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MicrosoftTeams
|
|
6
|
+
module Actor
|
|
7
|
+
class AbsorbMeeting < Legion::Extensions::Actors::Subscription
|
|
8
|
+
def runner_class = 'Legion::Extensions::MicrosoftTeams::Absorbers::Meeting'
|
|
9
|
+
def runner_function = 'absorb'
|
|
10
|
+
def check_subtask? = false
|
|
11
|
+
def generate_task? = false
|
|
12
|
+
|
|
13
|
+
def enabled?
|
|
14
|
+
defined?(Legion::Extensions::Absorbers::Base) &&
|
|
15
|
+
defined?(Legion::Extensions::MicrosoftTeams::Absorbers::Meeting)
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
log.debug("AbsorbMeeting#enabled?: #{e.message}")
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def work(payload)
|
|
22
|
+
parsed = parse_payload(payload)
|
|
23
|
+
absorber = Absorbers::Meeting.new
|
|
24
|
+
result = absorber.absorb(
|
|
25
|
+
url: parsed[:url],
|
|
26
|
+
metadata: parsed[:metadata] || {},
|
|
27
|
+
context: parsed[:context] || {}
|
|
28
|
+
)
|
|
29
|
+
if result.respond_to?(:[]) && result.key?(:success)
|
|
30
|
+
if result[:success]
|
|
31
|
+
ack!
|
|
32
|
+
else
|
|
33
|
+
log.error("AbsorbMeeting actor absorb failed: #{result.inspect}")
|
|
34
|
+
reject!(requeue: false)
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
ack!
|
|
38
|
+
end
|
|
39
|
+
result
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
log.error("AbsorbMeeting actor error: #{e.message}")
|
|
42
|
+
reject!(requeue: false)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def parse_payload(payload)
|
|
48
|
+
data = payload.is_a?(String) ? json_load(payload) : payload
|
|
49
|
+
return {} unless data.is_a?(Hash)
|
|
50
|
+
|
|
51
|
+
data.transform_keys(&:to_sym)
|
|
52
|
+
rescue StandardError => e
|
|
53
|
+
log.debug("AbsorbMeeting#parse_payload: #{e.message}")
|
|
54
|
+
{}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -72,7 +72,7 @@ module Legion
|
|
|
72
72
|
else
|
|
73
73
|
4567
|
|
74
74
|
end
|
|
75
|
-
"http://127.0.0.1:#{port}/api/
|
|
75
|
+
"http://127.0.0.1:#{port}/api/extensions/microsoft_teams/hooks/auth/handle"
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def generate_pkce
|
|
@@ -121,9 +121,9 @@ module Legion
|
|
|
121
121
|
def hook_route_registered?
|
|
122
122
|
return false unless defined?(Legion::API)
|
|
123
123
|
|
|
124
|
-
log.debug("Probing hook route at http://127.0.0.1:#{api_port}/api/
|
|
124
|
+
log.debug("Probing hook route at http://127.0.0.1:#{api_port}/api/extensions/microsoft_teams/hooks/auth/handle")
|
|
125
125
|
conn = Faraday.new(url: "http://127.0.0.1:#{api_port}")
|
|
126
|
-
resp = conn.head('/api/
|
|
126
|
+
resp = conn.head('/api/extensions/microsoft_teams/hooks/auth/handle')
|
|
127
127
|
registered = resp.status != 404
|
|
128
128
|
log.debug("Hook route probe returned #{resp.status} (registered=#{registered})")
|
|
129
129
|
registered
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MicrosoftTeams
|
|
6
|
+
module Helpers
|
|
7
|
+
module GraphClient
|
|
8
|
+
class GraphError < StandardError; end
|
|
9
|
+
|
|
10
|
+
def graph_get(path, token:, params: {})
|
|
11
|
+
connection = graph_connection(token: token)
|
|
12
|
+
response = connection.get(path, params)
|
|
13
|
+
handle_graph_response(response, path)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def graph_paginate(path, token:, params: {}, max_pages: 10)
|
|
17
|
+
results = []
|
|
18
|
+
next_link = nil
|
|
19
|
+
page = 0
|
|
20
|
+
|
|
21
|
+
loop do
|
|
22
|
+
current_path = next_link || path
|
|
23
|
+
data = graph_get(current_path, token: token, params: page.zero? ? params : {})
|
|
24
|
+
break if data.nil?
|
|
25
|
+
|
|
26
|
+
items = data['value'] || data[:value]
|
|
27
|
+
results.concat(Array(items)) if items
|
|
28
|
+
|
|
29
|
+
next_link = data['@odata.nextLink'] || data[:'@odata.nextLink']
|
|
30
|
+
page += 1
|
|
31
|
+
break if next_link.nil? || page >= max_pages
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
results
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def graph_post(path, token:, body: {})
|
|
38
|
+
connection = graph_connection(token: token)
|
|
39
|
+
response = connection.post(path) do |req|
|
|
40
|
+
req.body = body
|
|
41
|
+
end
|
|
42
|
+
handle_graph_response(response, path)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def handle_graph_response(response, path)
|
|
48
|
+
error_message =
|
|
49
|
+
if response.body.respond_to?(:dig)
|
|
50
|
+
response.body.dig('error', 'message') ||
|
|
51
|
+
response.body.dig(:error, :message)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
case response.status
|
|
55
|
+
when 200, 201
|
|
56
|
+
response.body
|
|
57
|
+
when 204, 404
|
|
58
|
+
nil
|
|
59
|
+
when 401
|
|
60
|
+
detail = error_message || 'Access token is missing, expired, or invalid.'
|
|
61
|
+
raise GraphError, "Graph API 401 Unauthorized on #{path}: #{detail}"
|
|
62
|
+
when 403
|
|
63
|
+
detail = error_message || 'Caller does not have sufficient permissions to perform this action.'
|
|
64
|
+
raise GraphError, "Graph API 403 Forbidden on #{path}: #{detail}"
|
|
65
|
+
else
|
|
66
|
+
base_message = "Graph API #{response.status} on #{path}"
|
|
67
|
+
base_message = "#{base_message}: #{error_message}" if error_message
|
|
68
|
+
raise GraphError, base_message
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -7,11 +7,7 @@ module Legion
|
|
|
7
7
|
class Auth < Legion::Extensions::Hooks::Base
|
|
8
8
|
mount '/callback'
|
|
9
9
|
|
|
10
|
-
def
|
|
11
|
-
:auth_callback
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def runner_class
|
|
10
|
+
def self.runner_class
|
|
15
11
|
'Legion::Extensions::MicrosoftTeams::Runners::Auth'
|
|
16
12
|
end
|
|
17
13
|
end
|
|
@@ -33,6 +33,7 @@ require 'legion/extensions/microsoft_teams/helpers/callback_server'
|
|
|
33
33
|
require 'legion/extensions/microsoft_teams/helpers/browser_auth'
|
|
34
34
|
require 'legion/extensions/microsoft_teams/helpers/permission_guard'
|
|
35
35
|
require 'legion/extensions/microsoft_teams/helpers/transform_definitions'
|
|
36
|
+
require 'legion/extensions/microsoft_teams/helpers/graph_client'
|
|
36
37
|
|
|
37
38
|
# Transport
|
|
38
39
|
if defined?(Legion::Transport)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-microsoft_teams
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -176,6 +176,7 @@ files:
|
|
|
176
176
|
- lex-microsoft_teams.gemspec
|
|
177
177
|
- lib/legion/extensions/microsoft_teams.rb
|
|
178
178
|
- lib/legion/extensions/microsoft_teams/absorbers/meeting.rb
|
|
179
|
+
- lib/legion/extensions/microsoft_teams/actors/absorb_meeting.rb
|
|
179
180
|
- lib/legion/extensions/microsoft_teams/actors/api_ingest.rb
|
|
180
181
|
- lib/legion/extensions/microsoft_teams/actors/auth_validator.rb
|
|
181
182
|
- lib/legion/extensions/microsoft_teams/actors/cache_bulk_ingest.rb
|
|
@@ -194,6 +195,7 @@ files:
|
|
|
194
195
|
- lib/legion/extensions/microsoft_teams/helpers/browser_auth.rb
|
|
195
196
|
- lib/legion/extensions/microsoft_teams/helpers/callback_server.rb
|
|
196
197
|
- lib/legion/extensions/microsoft_teams/helpers/client.rb
|
|
198
|
+
- lib/legion/extensions/microsoft_teams/helpers/graph_client.rb
|
|
197
199
|
- lib/legion/extensions/microsoft_teams/helpers/high_water_mark.rb
|
|
198
200
|
- lib/legion/extensions/microsoft_teams/helpers/permission_guard.rb
|
|
199
201
|
- lib/legion/extensions/microsoft_teams/helpers/prompt_resolver.rb
|