Release beta 2.0.0.1021
check / local-gate (push) Failing after 1m38s

This commit is contained in:
JWeBot Contributors
2026-10-21 17:05:09 +08:00
parent 5a3c4fb94c
commit e4365841ed
79 changed files with 1572 additions and 1840 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "jwebot-sdk"
version = "1.2.8b1+1019"
version = "2.0.0b1+1021"
description = "Typed, capability-limited extension API for JWeBot plugins"
requires-python = ">=3.9"
dependencies = []
@@ -8,6 +8,7 @@ from .context import (
PluginLogger,
PluginStorage,
Scheduler,
ServiceCommandAdapter,
SingleGroupContext,
)
from .contracts import (
@@ -35,7 +36,8 @@ __all__ = [
"PluginStorage",
"ReplyPlan",
"Scheduler",
"ServiceCommandAdapter",
"SingleGroupContext",
]
__version__ = "1.2.8b1+1019"
__version__ = "2.0.0b1+1021"
+66 -2
View File
@@ -2,10 +2,10 @@
from __future__ import annotations
from collections.abc import Callable, Mapping
from collections.abc import Callable, Iterable, Mapping
from typing import Protocol
from .contracts import Event, ExtensionResult, Fact
from .contracts import Event, ExtensionResult, Fact, ReplyPlan
CommandHandler = Callable[[Event, str], ExtensionResult]
@@ -68,3 +68,67 @@ class SingleGroupContext(Protocol):
facts: FactWriter
scheduler: Scheduler
logger: PluginLogger
class ServiceCommandAdapter:
"""Register commands that delegate only to declared kernel service profiles."""
def __init__(
self,
profile: str,
*,
operation: str = "execute",
metadata: Mapping[str, str] | None = None,
) -> None:
self.profile = self._bounded_text(profile, "profile")
self.operation = self._bounded_text(operation, "operation")
self.metadata = {
str(key): str(value)
for key, value in (metadata or {}).items()
}
@staticmethod
def _bounded_text(value: object, name: str) -> str:
text = str(value or "").strip()
if not text or len(text) > 80:
raise ValueError(f"{name} is invalid")
return text
def _handler(self, context: SingleGroupContext, command: str) -> CommandHandler:
command_name = self._bounded_text(command, "command")
def handle(event: Event, arguments: str) -> ExtensionResult:
response = context.services.call(
self.profile,
operation=self.operation,
payload={
"command": command_name,
"arguments": str(arguments),
"event": event.to_mapping(),
},
)
texts = response.get("texts")
if isinstance(texts, list) and texts:
if not all(isinstance(item, str) and item.strip() for item in texts):
raise ValueError("service returned invalid text parts")
return ExtensionResult(
reply=ReplyPlan.multi_text(texts),
metadata=self.metadata,
)
text = response.get("text")
if not isinstance(text, str) or not text.strip():
raise ValueError("service returned invalid text")
return ExtensionResult(
reply=ReplyPlan.text(text),
metadata=self.metadata,
)
return handle
def register(
self,
context: SingleGroupContext,
commands: Iterable[str],
) -> None:
for command in commands:
context.commands.register(command, self._handler(context, command))