release: public alpha 0.6.0 baseline
This commit is contained in:
Executable
+233
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create a reviewed command-extension skeleton without runtime privileges."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path[:0] = [
|
||||
str(ROOT / "packages" / "jwebot-sdk" / "src"),
|
||||
str(ROOT / "packages" / "jwebot-kernel" / "src"),
|
||||
]
|
||||
|
||||
from jwebot_kernel import ManifestError, parse_manifest # noqa: E402
|
||||
|
||||
|
||||
def module_name(plugin_id: str) -> str:
|
||||
return plugin_id.replace("-", "_")
|
||||
|
||||
|
||||
def manifest_text(
|
||||
plugin_id: str,
|
||||
command: str,
|
||||
author: str,
|
||||
description: str,
|
||||
help_entry: str,
|
||||
) -> str:
|
||||
module = module_name(plugin_id)
|
||||
return f"""id = "{plugin_id}"
|
||||
author = "{author}"
|
||||
description = {json.dumps(description, ensure_ascii=False)}
|
||||
help = [{json.dumps(help_entry, ensure_ascii=False)}]
|
||||
version = "1.0.0"
|
||||
api_version = "1"
|
||||
entrypoint = "{module}:plugin"
|
||||
type = "command"
|
||||
|
||||
[capabilities]
|
||||
commands = ["{command}"]
|
||||
storage = false
|
||||
network_profiles = []
|
||||
service_profiles = []
|
||||
"""
|
||||
|
||||
|
||||
def module_text(plugin_id: str, command: str) -> str:
|
||||
return f'''"""Command extension: {plugin_id}."""
|
||||
|
||||
from jwebot_sdk import Event, ExtensionResult, ReplyPlan, SingleGroupContext
|
||||
|
||||
|
||||
def handle(event: Event, arguments: str) -> ExtensionResult:
|
||||
del event, arguments
|
||||
return ExtensionResult(reply=ReplyPlan.text("TODO"))
|
||||
|
||||
|
||||
def plugin(context: SingleGroupContext) -> None:
|
||||
context.commands.register("{command}", handle)
|
||||
'''
|
||||
|
||||
|
||||
def test_text(plugin_id: str) -> str:
|
||||
module = module_name(plugin_id)
|
||||
return f'''import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SOURCE = Path(__file__).resolve().parents[1] / "src"
|
||||
sys.path.insert(0, str(SOURCE))
|
||||
|
||||
from jwebot_sdk import Event, PlanKind
|
||||
|
||||
from {module} import handle
|
||||
|
||||
|
||||
class PluginTests(unittest.TestCase):
|
||||
def test_command_returns_a_structured_reply(self):
|
||||
event = Event(
|
||||
event_id="event-1",
|
||||
observed_at="2026-01-01T00:00:00+08:00",
|
||||
sender="tester",
|
||||
text="@JWeBot test",
|
||||
group_fingerprint="group-test",
|
||||
mention_bot=True,
|
||||
)
|
||||
result = handle(event, "")
|
||||
self.assertEqual(result.reply.kind, PlanKind.TEXT)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
'''
|
||||
|
||||
|
||||
def readme_text(
|
||||
plugin_id: str,
|
||||
command: str,
|
||||
author: str,
|
||||
description: str,
|
||||
help_entry: str,
|
||||
) -> str:
|
||||
return f"""# {plugin_id}
|
||||
|
||||
Author: `{author}`
|
||||
|
||||
{description}
|
||||
|
||||
Command: `{command}`
|
||||
|
||||
Help: `{help_entry}`
|
||||
|
||||
This extension may return structured JWeBot SDK results only. It must not use
|
||||
GUI automation, raw storage, environment variables, subprocesses, SSH, Docker,
|
||||
or unrestricted network access.
|
||||
"""
|
||||
|
||||
|
||||
def validate_inputs(
|
||||
plugin_id: str,
|
||||
command: str,
|
||||
author: str,
|
||||
description: str,
|
||||
help_entry: str,
|
||||
) -> None:
|
||||
parse_manifest(
|
||||
{
|
||||
"id": plugin_id,
|
||||
"author": author,
|
||||
"description": description,
|
||||
"help": [help_entry],
|
||||
"version": "1.0.0",
|
||||
"api_version": "1",
|
||||
"entrypoint": f"{module_name(plugin_id)}:plugin",
|
||||
"type": "command",
|
||||
"capabilities": {
|
||||
"commands": [command],
|
||||
"storage": False,
|
||||
"network_profiles": [],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def create_plugin(
|
||||
output: Path,
|
||||
plugin_id: str,
|
||||
command: str,
|
||||
author: str,
|
||||
description: str,
|
||||
help_entry: str | None = None,
|
||||
) -> Path:
|
||||
help_entry = help_entry or command
|
||||
validate_inputs(
|
||||
plugin_id,
|
||||
command,
|
||||
author,
|
||||
description,
|
||||
help_entry,
|
||||
)
|
||||
destination = output / plugin_id
|
||||
if destination.exists():
|
||||
raise FileExistsError(f"destination already exists: {destination}")
|
||||
source = destination / "src"
|
||||
tests = destination / "tests"
|
||||
source.mkdir(parents=True)
|
||||
tests.mkdir()
|
||||
module = module_name(plugin_id)
|
||||
(destination / "plugin.toml").write_text(
|
||||
manifest_text(
|
||||
plugin_id,
|
||||
command,
|
||||
author,
|
||||
description,
|
||||
help_entry,
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
(source / f"{module}.py").write_text(
|
||||
module_text(plugin_id, command),
|
||||
encoding="utf-8",
|
||||
)
|
||||
(tests / "test_plugin.py").write_text(
|
||||
test_text(plugin_id),
|
||||
encoding="utf-8",
|
||||
)
|
||||
(destination / "README.md").write_text(
|
||||
readme_text(
|
||||
plugin_id,
|
||||
command,
|
||||
author,
|
||||
description,
|
||||
help_entry,
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
return destination
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("plugin_id")
|
||||
parser.add_argument("command")
|
||||
parser.add_argument("--author", required=True)
|
||||
parser.add_argument("--description", required=True)
|
||||
parser.add_argument("--help-entry")
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=Path,
|
||||
default=ROOT / "extensions" / "friends",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
destination = create_plugin(
|
||||
args.output,
|
||||
args.plugin_id,
|
||||
args.command,
|
||||
args.author,
|
||||
args.description,
|
||||
args.help_entry,
|
||||
)
|
||||
except (ManifestError, FileExistsError) as error:
|
||||
parser.error(str(error))
|
||||
print(destination)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user