mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-24 01:15:24 +08:00
fix(pipeline): decouple Runner from extension permissions (LANG-54) (#2723)
* fix(pipeline): decouple runner from extension allowlist Co-authored-by: multica-agent <[email protected]> * fix(pipeline): complete runner diagnostics and real runtime coverage Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
co-authored by
multica-agent
parent
5a6ddbff6f
commit
d9e4e7d9df
@@ -14,7 +14,7 @@ from ...api.http.context import ExecutionContext
|
||||
from ...api.http.service.tenant import TenantContext
|
||||
from .descriptor import RunnerDescriptor
|
||||
from .id import parse_runner_id, format_runner_id
|
||||
from .errors import RunnerNotFoundError, RunnerNotAuthorizedError
|
||||
from .errors import RunnerNotFoundError
|
||||
|
||||
|
||||
class RunnerRegistry:
|
||||
@@ -229,14 +229,13 @@ class RunnerRegistry:
|
||||
|
||||
Args:
|
||||
runner_id: Runner ID to lookup
|
||||
bound_plugins: Optional bound plugins filter
|
||||
bound_plugins: Legacy pipeline extension filter; does not authorize Runners.
|
||||
|
||||
Returns:
|
||||
RunnerDescriptor
|
||||
|
||||
Raises:
|
||||
RunnerNotFoundError: If runner not found
|
||||
RunnerNotAuthorizedError: If runner not in bound plugins
|
||||
"""
|
||||
# Parse and validate runner ID format
|
||||
try:
|
||||
@@ -259,11 +258,9 @@ class RunnerRegistry:
|
||||
if descriptor is None:
|
||||
raise RunnerNotFoundError(runner_id)
|
||||
|
||||
# Check authorization
|
||||
if bound_plugins is not None:
|
||||
plugin_id = descriptor.get_plugin_id()
|
||||
if plugin_id not in bound_plugins:
|
||||
raise RunnerNotAuthorizedError(runner_id, bound_plugins)
|
||||
# A selected Runner is independent of the pipeline extension allowlist.
|
||||
# Installation enablement and Workspace scope are enforced by the connector
|
||||
# again at invocation; never widen the extension permissions on the query.
|
||||
|
||||
return descriptor
|
||||
|
||||
|
||||
@@ -189,7 +189,8 @@ class PipelineService:
|
||||
pipeline_data['stages'] = default_stage_order.copy()
|
||||
pipeline_data['is_default'] = default
|
||||
|
||||
pipeline_data['config'] = await self.get_default_pipeline_config(context)
|
||||
if 'config' not in pipeline_data:
|
||||
pipeline_data['config'] = await self.get_default_pipeline_config(context)
|
||||
RunnerConfigResolver.validate_pipeline_config(pipeline_data['config'])
|
||||
|
||||
# Ensure extensions_preferences is set with enable_all_plugins and enable_all_mcp_servers=True by default
|
||||
|
||||
@@ -132,7 +132,13 @@ class LangBotMCPServer:
|
||||
await ap.pipeline_service.get_pipeline_extensions(_authorized(Permission.RESOURCE_VIEW), pipeline_uuid)
|
||||
)
|
||||
|
||||
@mcp.tool(description='Replace all Pipeline extension bindings. All binding lists and switches are required.')
|
||||
@mcp.tool(
|
||||
description=(
|
||||
'Replace all Pipeline extension bindings. All binding lists and switches are required. '
|
||||
'Plugin switches control EventListener, Tool and Command only, not the independently selected Runner. '
|
||||
'Do not add a Runner plugin here just to run it; installation, global enablement and Workspace access still apply.'
|
||||
)
|
||||
)
|
||||
async def update_pipeline_extensions(
|
||||
pipeline_uuid: str,
|
||||
bound_plugins: list[dict],
|
||||
@@ -394,7 +400,8 @@ class LangBotMCPServer:
|
||||
@mcp.tool(
|
||||
description=(
|
||||
'Create a pipeline. `pipeline_data` matches the LangBot POST '
|
||||
'/api/v1/pipelines body. Returns the new pipeline UUID.'
|
||||
'/api/v1/pipelines body. Supplied config and extension preferences are preserved; '
|
||||
'omitting config uses installed Runner defaults. Returns the new pipeline UUID.'
|
||||
)
|
||||
)
|
||||
async def create_pipeline(pipeline_data: dict) -> str:
|
||||
|
||||
Reference in New Issue
Block a user