From 1ed7b669ce85fdb5394ec798482f2e26f3ec3328 Mon Sep 17 00:00:00 2001 From: DanliaQwerty20 Date: Mon, 14 Sep 2026 01:44:45 +0300 Subject: [PATCH 1/2] feat: add Channel Gateway to local stack --- .env.example | 2 ++ .github/workflows/app-smoke.yml | 9 ++++++++ README.md | 15 +++++++------ compose/apps.local.yaml | 6 +++++ compose/compose.yaml | 26 ++++++++++++++++++++++ compose/keycloak/portable-agent-realm.json | 19 ++++++++++++++++ config/versions.env | 1 + docs/architecture.md | 10 ++++++--- docs/index.md | 8 +++---- docs/runbook.md | 6 ++--- scripts/check-apps.ps1 | 11 ++++----- scripts/check-compose.ps1 | 25 +++++++++++++++------ scripts/check-keycloak.ps1 | 1 + 13 files changed, 110 insertions(+), 29 deletions(-) diff --git a/.env.example b/.env.example index 3f9bb0f..990f39c 100644 --- a/.env.example +++ b/.env.example @@ -12,12 +12,14 @@ ACTION_SERVICE_PORT=18081 ACTION_SERVICE_TENANT_ID=11111111-1111-4111-8111-111111111111 AGENT_RUNTIME_PORT=18080 +CHANNEL_GATEWAY_PORT=18084 MCP_GATEWAY_PORT=18083 CALENDAR_MCP_PORT=18082 CALENDAR_TEST_API_KEY=local_calendar_test_change_me # Можно переопределить, если соседние репозитории лежат в других папках. AGENT_RUNTIME_CONTEXT=../../portable-agent-agent-runtime +CHANNEL_GATEWAY_CONTEXT=../../portable-agent-channel-gateway ACTION_SERVICE_CONTEXT=../../portable-agent-action-service MCP_GATEWAY_CONTEXT=../../portable-agent-mcp-gateway CALENDAR_MCP_CONTEXT=../../portable-agent-calendar-mcp diff --git a/.github/workflows/app-smoke.yml b/.github/workflows/app-smoke.yml index 661fb8c..80e4fc8 100644 --- a/.github/workflows/app-smoke.yml +++ b/.github/workflows/app-smoke.yml @@ -24,6 +24,7 @@ jobs: timeout-minutes: 30 env: COMPOSE_PROJECT_NAME: pa-app-${{ github.run_id }}-${{ github.run_attempt }} + CHANNEL_GATEWAY_CONTEXT: ${{ github.workspace }}/repos/channel-gateway AGENT_RUNTIME_CONTEXT: ${{ github.workspace }}/repos/agent-runtime ACTION_SERVICE_CONTEXT: ${{ github.workspace }}/repos/action-service MCP_GATEWAY_CONTEXT: ${{ github.workspace }}/repos/mcp-gateway @@ -37,11 +38,19 @@ jobs: shell: pwsh run: | $versions = Get-Content -Raw config/versions.env | ConvertFrom-StringData + "channel=$($versions.CHANNEL_GATEWAY_IMAGE.Split(':')[-1])" >> $env:GITHUB_OUTPUT "agent=$($versions.AGENT_RUNTIME_IMAGE.Split(':')[-1])" >> $env:GITHUB_OUTPUT "action=$($versions.ACTION_SERVICE_IMAGE.Split(':')[-1])" >> $env:GITHUB_OUTPUT "gateway=$($versions.MCP_GATEWAY_IMAGE.Split(':')[-1])" >> $env:GITHUB_OUTPUT "calendar=$($versions.CALENDAR_MCP_IMAGE.Split(':')[-1])" >> $env:GITHUB_OUTPUT + - name: Получить Channel Gateway + uses: actions/checkout@v7 + with: + repository: portable-agent/channel-gateway + ref: ${{ steps.versions.outputs.channel }} + path: repos/channel-gateway + - name: Получить Agent Runtime uses: actions/checkout@v7 with: diff --git a/README.md b/README.md index 72fbe0c..0998447 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Платформа разработки включает: - Compose-профиль `core`: PostgreSQL, Redpanda, Keycloak, Temporal и OPA; -- Compose-профиль `apps`: Agent Runtime, Action Service, MCP Gateway и Calendar MCP; +- Compose-профиль `apps`: Channel Gateway, Agent Runtime, Action Service, MCP Gateway и Calendar MCP; - Compose-профиль `observe`: OpenTelemetry, Prometheus, Grafana, Tempo и Loki; - безопасный общий chart `charts/service`; - Argo CD bootstrap `charts/gitops`; @@ -30,14 +30,14 @@ Copy-Item .env.example .env pwsh ./scripts/start-local.ps1 -Observe ``` -Чтобы поднять проверяемый путь `Agent → Action → Gateway → Calendar`, используй: +Чтобы поднять проверяемый путь `Channel → Agent → Action → Gateway → Calendar`, используй: ```powershell pwsh ./scripts/start-local.ps1 -Apps pwsh ./scripts/check-apps.ps1 ``` -`check-apps.ps1` отправляет demo-команду в Agent Runtime, проверяет предложение, создаёт и +`check-apps.ps1` отправляет demo-команду в Channel Gateway, проверяет предложение, создаёт и подтверждает действие, ждёт Temporal workflow и проверяет, что Calendar MCP сохранил ровно одно событие с тем же `eventId`. @@ -46,9 +46,10 @@ pwsh ./scripts/check-apps.ps1 Compose project и всегда удаляет только созданные им контейнеры и volumes. `-Apps` собирает образы из соседних локальных репозиториев через `compose/apps.local.yaml`. Пути можно -переопределить переменными `AGENT_RUNTIME_CONTEXT`, `ACTION_SERVICE_CONTEXT`, +переопределить переменными `CHANNEL_GATEWAY_CONTEXT`, `AGENT_RUNTIME_CONTEXT`, `ACTION_SERVICE_CONTEXT`, `MCP_GATEWAY_CONTEXT` и `CALENDAR_MCP_CONTEXT`; вход в GHCR для локального запуска не нужен. По -умолчанию Agent Runtime доступен на `http://localhost:18080`, Action API — на +умолчанию Channel Gateway доступен на `http://localhost:18084`, Agent Runtime — на +`http://localhost:18080`, Action API — на `http://localhost:18081`, MCP Gateway — на `http://localhost:18083`, а Calendar MCP — на `http://localhost:18082`. @@ -67,8 +68,8 @@ manager, а не из Git. очищает созданные тестовые встречи. После запуска скрипт получает настоящий JWT и сверяет пользователя, `tenant_id` и audience -`agent-runtime`, `action-service` и `calendar-mcp` с realm fixture. Один пользовательский токен -проходит независимую проверку в Agent Runtime и Action API. +`channel-gateway`, `agent-runtime`, `action-service` и `calendar-mcp` с realm fixture. Один +пользовательский токен проходит независимую проверку в Channel Gateway, Agent Runtime и Action API. Если Keycloak volume создан старой версией fixture, запуск остановится с командой для явного пересоздания локальных данных. diff --git a/compose/apps.local.yaml b/compose/apps.local.yaml index 4e1c7ea..1493756 100644 --- a/compose/apps.local.yaml +++ b/compose/apps.local.yaml @@ -1,4 +1,10 @@ services: + channel-gateway: + image: portable-agent/channel-gateway:local + build: + context: ${CHANNEL_GATEWAY_CONTEXT:-../../portable-agent-channel-gateway} + pull_policy: build + agent-runtime: image: portable-agent/agent-runtime:local build: diff --git a/compose/compose.yaml b/compose/compose.yaml index f6d05fd..9c86fa6 100644 --- a/compose/compose.yaml +++ b/compose/compose.yaml @@ -197,6 +197,32 @@ services: retries: 20 networks: [platform] + channel-gateway: + profiles: [apps] + image: ${CHANNEL_GATEWAY_IMAGE:?set CHANNEL_GATEWAY_IMAGE} + environment: + HOST: 0.0.0.0 + PORT: 8080 + AGENT_URL: http://agent-runtime:8080 + AGENT_TIMEOUT_MS: 5000 + AGENT_AVAILABLE_CONNECTORS: '["fake-calendar"]' + OIDC_ISSUER_URL: http://localhost:${KEYCLOAK_PORT:-8081}/realms/portable-agent + OIDC_JWKS_URL: http://keycloak:8080/realms/portable-agent/protocol/openid-connect/certs + OIDC_AUDIENCE: channel-gateway + depends_on: + keycloak: + condition: service_healthy + agent-runtime: + condition: service_healthy + ports: + - "127.0.0.1:${CHANNEL_GATEWAY_PORT:-18084}:8080" + healthcheck: + test: [CMD, node, -e, "fetch('http://localhost:8080/health/live').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] + interval: 5s + timeout: 3s + retries: 30 + networks: [platform] + agent-runtime: profiles: [apps] image: ${AGENT_RUNTIME_IMAGE:?set AGENT_RUNTIME_IMAGE} diff --git a/compose/keycloak/portable-agent-realm.json b/compose/keycloak/portable-agent-realm.json index 83eb057..471bc39 100644 --- a/compose/keycloak/portable-agent-realm.json +++ b/compose/keycloak/portable-agent-realm.json @@ -44,6 +44,13 @@ } ], "clients": [ + { + "clientId": "channel-gateway", + "name": "Channel Gateway Resource Server", + "enabled": true, + "bearerOnly": true, + "protocol": "openid-connect" + }, { "clientId": "agent-runtime", "name": "Agent Runtime Resource Server", @@ -142,6 +149,18 @@ "introspection.token.claim": "true" } }, + { + "name": "channel-gateway-audience", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "channel-gateway", + "id.token.claim": "false", + "access.token.claim": "true", + "introspection.token.claim": "true" + } + }, { "name": "agent-runtime-audience", "protocol": "openid-connect", diff --git a/config/versions.env b/config/versions.env index d3425bb..73ba8fc 100644 --- a/config/versions.env +++ b/config/versions.env @@ -11,6 +11,7 @@ GRAFANA_IMAGE=grafana/grafana:13.2.0 TEMPO_IMAGE=grafana/tempo:3.0.3 LOKI_IMAGE=grafana/loki:3.7.7 BUSYBOX_IMAGE=busybox:1.37.0 +CHANNEL_GATEWAY_IMAGE=ghcr.io/portable-agent/channel-gateway:3d6126408d4ac826b80a89a3f0b74b7912580887 AGENT_RUNTIME_IMAGE=ghcr.io/portable-agent/agent-runtime:4dd541e459d0bdbf889ddc1fbe68e1f7c3b47b89 ACTION_SERVICE_IMAGE=ghcr.io/portable-agent/action-service:8d449ab990001e31cd42a7655a16c5716c5d6f20 MCP_GATEWAY_IMAGE=ghcr.io/portable-agent/mcp-gateway:0a3884d52ad1156ed173a4ecf9182ea3a0d2e686 diff --git a/docs/architecture.md b/docs/architecture.md index 1fc4baf..adcae28 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -25,6 +25,7 @@ services/catalog.json -> environments//services//values.yaml ```mermaid sequenceDiagram participant User as Пользователь + participant Channel as Channel Gateway participant Agent as Agent Runtime participant Action as Action Service participant Keycloak @@ -32,8 +33,10 @@ sequenceDiagram participant Calendar as Calendar MCP User->>Keycloak: логин - Keycloak-->>User: tenant + audience agent-runtime и action-service - User->>Agent: текст + безопасный context + JWT + Keycloak-->>User: tenant + audience channel, agent и action + User->>Channel: текст + безопасный context + JWT + Channel->>Channel: JWT + server connector list + Channel->>Agent: нормализованный текст + тот же JWT Agent->>Agent: JWT + подготовка ActionPlan Agent-->>User: proposal требует подтверждения User->>Action: создать и подтвердить действие @@ -50,5 +53,6 @@ sequenceDiagram Canonical issuer локального realm доступен хосту через `localhost`. Контейнеры загружают JWKS по внутреннему имени `keycloak`, поэтому проверка токена не зависит от DNS хоста. `start-local -Apps` сначала поднимает зависимости и создаёт Temporal namespace, затем собирает и запускает приложения. -Agent Runtime не исполняет и не сохраняет действие. Он создаёт предложение по контракту `2.1.0`; +Channel Gateway не зависит от Telegram и принимает общий текстовый контракт `2.2.0`. Agent Runtime не +исполняет и не сохраняет действие. Он создаёт предложение по совместимому контракту `2.2.0`; после подтверждения Action Service становится источником состояния, аудита и Temporal workflow. diff --git a/docs/index.md b/docs/index.md index ea744f2..003507a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,15 +1,15 @@ # Deploy Репозиторий содержит инженерный полигон Portable Agent. Compose поднимает инфраструктуру и локальный -полный локальный slice `Agent Runtime → Action Service → MCP Gateway → Calendar MCP`. Минимальный +полный локальный slice `Channel Gateway → Agent Runtime → Action Service → MCP Gateway → Calendar MCP`. Минимальный Helm chart устанавливается и проверяется в одноразовом k3d-кластере. Текущий результат: 1. acceptance-тест полного backend-пути в `test-lab`; 2. единый локальный JWT с отдельными audience сервисов; -3. Agent Runtime с закрытым API и контрактом `2.1.0`; +3. общий Channel Gateway и Agent Runtime с закрытыми API и контрактом `2.2.0`; 4. воспроизводимый Compose с локальной сборкой всех приложений. -Следующий инфраструктурный пакет — preview namespace для pull request. Бизнес-этап после него — -Channel Gateway и первый переносимый виджет. +Следующий инфраструктурный пакет — preview namespace для pull request. Следующий продуктовый этап — +первый переносимый виджет подтверждения. diff --git a/docs/runbook.md b/docs/runbook.md index e0c8d4a..f741741 100644 --- a/docs/runbook.md +++ b/docs/runbook.md @@ -18,12 +18,12 @@ ## Приложения не запускаются 1. Выполни `pwsh ./scripts/check-compose.ps1`. -2. Проверь, что рядом лежат репозитории `portable-agent-agent-runtime`, +2. Проверь, что рядом лежат репозитории `portable-agent-channel-gateway`, `portable-agent-agent-runtime`, `portable-agent-action-service`, `portable-agent-mcp-gateway` и `portable-agent-calendar-mcp`, либо задай их `*_CONTEXT` в `.env`. 3. Если Keycloak сообщает о старом fixture, локально выполни `pwsh ./scripts/stop-local.ps1 -DeleteData`. Команда удаляет только volumes этого Compose project. -4. Проверь `/health/live` Agent Runtime, `/actuator/health/readiness` Action Service и `/health` +4. Проверь `/health/live` Channel Gateway и Agent Runtime, `/actuator/health/readiness` Action Service и `/health` двух MCP-сервисов. 5. Не включай Calendar test API вне локального профиля `apps`. @@ -33,7 +33,7 @@ pwsh ./scripts/check-apps.ps1 ``` -Она сначала создаёт предложение из текста, затем создаёт действие со случайным `requestKey`, +Она отправляет текст через Channel Gateway, затем создаёт действие с тем же случайным `requestKey`, подтверждает его и сверяет результат Action Service с событием в тестовом календарном коннекторе. Первый Docker build скачивает Gradle и Python packages и может быть заметно медленнее повторных diff --git a/scripts/check-apps.ps1 b/scripts/check-apps.ps1 index 37f603e..e3af537 100644 --- a/scripts/check-apps.ps1 +++ b/scripts/check-apps.ps1 @@ -30,7 +30,7 @@ $client = $realm.clients | Where-Object clientId -eq "portable-agent-local" $user = $realm.users | Where-Object username -eq "local-user" $password = ($user.credentials | Where-Object type -eq "password").value $keycloakUrl = "http://localhost:$(Get-LocalSetting 'KEYCLOAK_PORT')" -$agentUrl = "http://localhost:$(Get-LocalSetting 'AGENT_RUNTIME_PORT')" +$channelUrl = "http://localhost:$(Get-LocalSetting 'CHANNEL_GATEWAY_PORT')" $actionUrl = "http://localhost:$(Get-LocalSetting 'ACTION_SERVICE_PORT')" $calendarUrl = "http://localhost:$(Get-LocalSetting 'CALENDAR_MCP_PORT')" @@ -70,13 +70,14 @@ function Invoke-JsonRequest( return $response.Content | ConvertFrom-Json -DateKind String } $proposalRequest = @{ + requestKey = $requestKey text = "Создай встречу `"$($payload.title)`" с $($payload.startAt) до $($payload.endAt)" context = @{ + locale = "ru-RU" timeZone = $payload.timeZone - availableConnectors = @("fake-calendar") } } | ConvertTo-Json -Depth 5 -$proposalResult = Invoke-JsonRequest -Method Post -Uri "$agentUrl/api/v1/proposals" ` +$proposalResult = Invoke-JsonRequest -Method Post -Uri "$channelUrl/api/v1/messages" ` -Headers $headers -Body $proposalRequest $proposal = $proposalResult.proposal $proposalErrors = @() @@ -91,7 +92,7 @@ if (-not (Test-SameDateTime $proposal.payload.endAt $payload.endAt)) { $proposal if ($proposal.payload.timeZone -ne $payload.timeZone) { $proposalErrors += "timeZone" } if ($proposalErrors.Count -gt 0) { $safeResponse = $proposalResult | ConvertTo-Json -Depth 8 -Compress - throw "Agent Runtime proposal не прошёл проверки: $($proposalErrors -join ', '). Ответ: $safeResponse" + throw "Channel Gateway proposal не прошёл проверки: $($proposalErrors -join ', '). Ответ: $safeResponse" } $body = @{ kind = $proposal.kind @@ -131,4 +132,4 @@ if ($events.Count -ne 1 -or $event.eventId -ne $saved.result.eventId ` throw "Calendar MCP не сохранил ожидаемое событие." } -Write-Host "Полный срез работает: proposal $($proposal.proposalId), action $($action.id), event $($saved.result.eventId)." +Write-Host "Полный срез работает через Channel Gateway: proposal $($proposal.proposalId), action $($action.id), event $($saved.result.eventId)." diff --git a/scripts/check-compose.ps1 b/scripts/check-compose.ps1 index 8123c9c..3c7c285 100644 --- a/scripts/check-compose.ps1 +++ b/scripts/check-compose.ps1 @@ -26,6 +26,10 @@ $agentClient = $realm.clients | Where-Object clientId -eq "agent-runtime" if (-not $agentClient -or -not $agentClient.bearerOnly) { throw "Нет resource server client agent-runtime." } +$channelClient = $realm.clients | Where-Object clientId -eq "channel-gateway" +if (-not $channelClient -or -not $channelClient.bearerOnly) { + throw "Нет resource server client channel-gateway." +} $gatewayClient = $realm.clients | Where-Object clientId -eq "mcp-gateway" if (-not $gatewayClient -or -not $gatewayClient.bearerOnly) { throw "Нет resource server client mcp-gateway." @@ -48,9 +52,10 @@ if (-not $gatewayScope -or $actionClient.defaultClientScopes -notcontains "mcp:c throw "Service token action-service не получает нужные scopes." } $localAudiences = @($localClient.protocolMappers | ForEach-Object { $_.config.'included.client.audience' }) -if ($localAudiences -notcontains "agent-runtime" -or $localAudiences -notcontains "action-service" ` +if ($localAudiences -notcontains "channel-gateway" -or $localAudiences -notcontains "agent-runtime" ` + -or $localAudiences -notcontains "action-service" ` -or $localAudiences -notcontains "calendar-mcp") { - throw "Локальный JWT не получает audience agent-runtime, action-service и calendar-mcp." + throw "Локальный JWT не получает audience channel-gateway, agent-runtime, action-service и calendar-mcp." } $tenantMapper = $localClient.protocolMappers | Where-Object { $_.config.'claim.name' -eq "tenant_id" } if (-not $tenantMapper) { @@ -70,12 +75,14 @@ $composeText = Get-Content -Raw -LiteralPath "compose/compose.yaml" if ($composeText -notmatch '(?ms)^ keycloak:.*?^ healthcheck:') { throw "У Keycloak нет readiness healthcheck." } -foreach ($service in @("agent-runtime", "action-service", "mcp-gateway", "calendar-mcp")) { +foreach ($service in @("channel-gateway", "agent-runtime", "action-service", "mcp-gateway", "calendar-mcp")) { if ($composeText -notmatch "(?m)^ $([regex]::Escape($service)):") { throw "В Compose нет приложения $service." } } -if ($composeText -notmatch '(?ms)^ agent-runtime:.*?AGENT_OIDC_AUDIENCE: agent-runtime' ` +if ($composeText -notmatch '(?ms)^ channel-gateway:.*?OIDC_AUDIENCE: channel-gateway' ` + -or $composeText -notmatch '(?ms)^ channel-gateway:.*?AGENT_URL: http://agent-runtime:8080' ` + -or $composeText -notmatch '(?ms)^ agent-runtime:.*?AGENT_OIDC_AUDIENCE: agent-runtime' ` -or $composeText -notmatch '(?ms)^ agent-runtime:.*?AGENT_DOCS_ENABLED: "false"' ` -or $composeText -notmatch '(?ms)^ action-service:.*?MCP_GATEWAY_URL: http://mcp-gateway:8080' ` -or $composeText -notmatch '(?ms)^ action-service:.*?OIDC_AUDIENCE: action-service' ` @@ -111,7 +118,7 @@ if ($startScript -notmatch 'compose/apps.local.yaml' -or $startScript -notmatch throw "Локальные приложения должны собираться из соседних репозиториев." } $appsOverride = Get-Content -Raw -LiteralPath "compose/apps.local.yaml" -foreach ($image in @("portable-agent/agent-runtime:local", "portable-agent/action-service:local", "portable-agent/mcp-gateway:local", "portable-agent/calendar-mcp:local")) { +foreach ($image in @("portable-agent/channel-gateway:local", "portable-agent/agent-runtime:local", "portable-agent/action-service:local", "portable-agent/mcp-gateway:local", "portable-agent/calendar-mcp:local")) { if ($appsOverride -notmatch [regex]::Escape($image)) { throw "Local override не задаёт отдельный image tag $image." } @@ -119,6 +126,7 @@ foreach ($image in @("portable-agent/agent-runtime:local", "portable-agent/actio $keycloakCheck = Get-Content -Raw -LiteralPath "scripts/check-keycloak.ps1" if ($keycloakCheck -notmatch 'portable-agent-realm.json' ` -or $keycloakCheck -notmatch 'tenant_id' ` + -or $keycloakCheck -notmatch 'channel-gateway' ` -or $keycloakCheck -notmatch 'agent-runtime' ` -or $keycloakCheck -notmatch 'calendar-mcp' ` -or $keycloakCheck -notmatch 'calendar:write') { @@ -128,7 +136,7 @@ $appCheck = Get-Content -Raw -LiteralPath "scripts/check-apps.ps1" if ($appCheck -notmatch 'ConvertFrom-Json -DateKind String') { throw "check-apps.ps1 должен сохранять исходные смещения времени из JSON." } -foreach ($required in @("AGENT_RUNTIME_PORT", "/api/v1/proposals", "availableConnectors", "requiresApproval", "proposalId")) { +foreach ($required in @("CHANNEL_GATEWAY_PORT", "/api/v1/messages", "requestKey", "requiresApproval", "proposalId")) { if ($appCheck -notmatch [regex]::Escape($required)) { throw "Сквозная проверка приложений не использует Agent Runtime: нет $required." } @@ -137,6 +145,9 @@ $versions = Get-Content -Raw -LiteralPath "config/versions.env" if ($versions -notmatch '(?m)^AGENT_RUNTIME_IMAGE=ghcr\.io/portable-agent/agent-runtime:[0-9a-f]{40}$') { throw "Agent Runtime image должен быть закреплён полным Git SHA." } +if ($versions -notmatch '(?m)^CHANNEL_GATEWAY_IMAGE=ghcr\.io/portable-agent/channel-gateway:[0-9a-f]{40}$') { + throw "Channel Gateway image должен быть закреплён полным Git SHA." +} foreach ($oldName in @("utterance", "actor_id", "available_connectors", "requires_approval")) { if ($appCheck -match [regex]::Escape($oldName)) { throw "Сквозная проверка приложений содержит старое поле $oldName." @@ -147,7 +158,7 @@ if (-not (Test-Path -LiteralPath $appWorkflowPath)) { throw "Нет CI-проверки полного Compose-среза." } $appWorkflow = Get-Content -Raw -LiteralPath $appWorkflowPath -foreach ($required in @("versions.env", "agent-runtime", "start-local.ps1 -Apps", "check-apps.ps1", "stop-local.ps1 -DeleteData", "if: always()")) { +foreach ($required in @("versions.env", "channel-gateway", "agent-runtime", "start-local.ps1 -Apps", "check-apps.ps1", "stop-local.ps1 -DeleteData", "if: always()")) { if ($appWorkflow -notmatch [regex]::Escape($required)) { throw "CI-проверка полного среза не содержит $required." } diff --git a/scripts/check-keycloak.ps1 b/scripts/check-keycloak.ps1 index 120570d..a1d12f8 100644 --- a/scripts/check-keycloak.ps1 +++ b/scripts/check-keycloak.ps1 @@ -26,6 +26,7 @@ try { $audiences = @($claims.aud) $scopes = @($claims.scope -split ' ') if ($claims.sub -ne $user.id -or $claims.tenant_id -ne $ExpectedTenant ` + -or $audiences -notcontains "channel-gateway" ` -or $audiences -notcontains "agent-runtime" ` -or $audiences -notcontains "action-service" ` -or $audiences -notcontains "calendar-mcp" -or $scopes -notcontains "calendar:write") { From c606c5907eb258b778151db2560512bca9816beb Mon Sep 17 00:00:00 2001 From: DanliaQwerty20 Date: Mon, 14 Sep 2026 01:55:14 +0300 Subject: [PATCH 2/2] fix: use compatible Channel Gateway image --- config/versions.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/versions.env b/config/versions.env index 73ba8fc..902e5e5 100644 --- a/config/versions.env +++ b/config/versions.env @@ -11,7 +11,7 @@ GRAFANA_IMAGE=grafana/grafana:13.2.0 TEMPO_IMAGE=grafana/tempo:3.0.3 LOKI_IMAGE=grafana/loki:3.7.7 BUSYBOX_IMAGE=busybox:1.37.0 -CHANNEL_GATEWAY_IMAGE=ghcr.io/portable-agent/channel-gateway:3d6126408d4ac826b80a89a3f0b74b7912580887 +CHANNEL_GATEWAY_IMAGE=ghcr.io/portable-agent/channel-gateway:5084949e4aa1f0c4be4d2a4bce5e970197efa831 AGENT_RUNTIME_IMAGE=ghcr.io/portable-agent/agent-runtime:4dd541e459d0bdbf889ddc1fbe68e1f7c3b47b89 ACTION_SERVICE_IMAGE=ghcr.io/portable-agent/action-service:8d449ab990001e31cd42a7655a16c5716c5d6f20 MCP_GATEWAY_IMAGE=ghcr.io/portable-agent/mcp-gateway:0a3884d52ad1156ed173a4ecf9182ea3a0d2e686