synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__py3-none-any.whl
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.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +9 -8
- synapse_sdk/cli/agent/__init__.py +25 -0
- synapse_sdk/cli/agent/config.py +104 -0
- synapse_sdk/cli/agent/select.py +197 -0
- synapse_sdk/cli/auth.py +104 -0
- synapse_sdk/cli/main.py +1025 -0
- synapse_sdk/cli/plugin/__init__.py +58 -0
- synapse_sdk/cli/plugin/create.py +566 -0
- synapse_sdk/cli/plugin/job.py +196 -0
- synapse_sdk/cli/plugin/publish.py +322 -0
- synapse_sdk/cli/plugin/run.py +131 -0
- synapse_sdk/cli/plugin/test.py +200 -0
- synapse_sdk/clients/README.md +239 -0
- synapse_sdk/clients/__init__.py +5 -0
- synapse_sdk/clients/_template.py +266 -0
- synapse_sdk/clients/agent/__init__.py +84 -29
- synapse_sdk/clients/agent/async_ray.py +289 -0
- synapse_sdk/clients/agent/container.py +83 -0
- synapse_sdk/clients/agent/plugin.py +101 -0
- synapse_sdk/clients/agent/ray.py +296 -39
- synapse_sdk/clients/backend/__init__.py +152 -12
- synapse_sdk/clients/backend/annotation.py +164 -22
- synapse_sdk/clients/backend/core.py +101 -0
- synapse_sdk/clients/backend/data_collection.py +292 -0
- synapse_sdk/clients/backend/hitl.py +87 -0
- synapse_sdk/clients/backend/integration.py +374 -46
- synapse_sdk/clients/backend/ml.py +134 -22
- synapse_sdk/clients/backend/models.py +247 -0
- synapse_sdk/clients/base.py +538 -59
- synapse_sdk/clients/exceptions.py +35 -7
- synapse_sdk/clients/pipeline/__init__.py +5 -0
- synapse_sdk/clients/pipeline/client.py +636 -0
- synapse_sdk/clients/protocols.py +178 -0
- synapse_sdk/clients/utils.py +86 -8
- synapse_sdk/clients/validation.py +58 -0
- synapse_sdk/enums.py +76 -0
- synapse_sdk/exceptions.py +168 -0
- synapse_sdk/integrations/__init__.py +74 -0
- synapse_sdk/integrations/_base.py +119 -0
- synapse_sdk/integrations/_context.py +53 -0
- synapse_sdk/integrations/ultralytics/__init__.py +78 -0
- synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
- synapse_sdk/integrations/ultralytics/_patches.py +124 -0
- synapse_sdk/loggers.py +476 -95
- synapse_sdk/mcp/MCP.md +69 -0
- synapse_sdk/mcp/__init__.py +48 -0
- synapse_sdk/mcp/__main__.py +6 -0
- synapse_sdk/mcp/config.py +349 -0
- synapse_sdk/mcp/prompts/__init__.py +4 -0
- synapse_sdk/mcp/resources/__init__.py +4 -0
- synapse_sdk/mcp/server.py +1352 -0
- synapse_sdk/mcp/tools/__init__.py +6 -0
- synapse_sdk/plugins/__init__.py +133 -9
- synapse_sdk/plugins/action.py +229 -0
- synapse_sdk/plugins/actions/__init__.py +82 -0
- synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
- synapse_sdk/plugins/actions/dataset/action.py +471 -0
- synapse_sdk/plugins/actions/export/__init__.py +55 -0
- synapse_sdk/plugins/actions/export/action.py +183 -0
- synapse_sdk/plugins/actions/export/context.py +59 -0
- synapse_sdk/plugins/actions/inference/__init__.py +84 -0
- synapse_sdk/plugins/actions/inference/action.py +285 -0
- synapse_sdk/plugins/actions/inference/context.py +81 -0
- synapse_sdk/plugins/actions/inference/deployment.py +322 -0
- synapse_sdk/plugins/actions/inference/serve.py +252 -0
- synapse_sdk/plugins/actions/train/__init__.py +54 -0
- synapse_sdk/plugins/actions/train/action.py +326 -0
- synapse_sdk/plugins/actions/train/context.py +57 -0
- synapse_sdk/plugins/actions/upload/__init__.py +49 -0
- synapse_sdk/plugins/actions/upload/action.py +165 -0
- synapse_sdk/plugins/actions/upload/context.py +61 -0
- synapse_sdk/plugins/config.py +98 -0
- synapse_sdk/plugins/context/__init__.py +109 -0
- synapse_sdk/plugins/context/env.py +113 -0
- synapse_sdk/plugins/datasets/__init__.py +113 -0
- synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
- synapse_sdk/plugins/datasets/converters/base.py +347 -0
- synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
- synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
- synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
- synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
- synapse_sdk/plugins/datasets/formats/dm.py +351 -0
- synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
- synapse_sdk/plugins/decorators.py +83 -0
- synapse_sdk/plugins/discovery.py +790 -0
- synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
- synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
- synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
- synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
- synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
- synapse_sdk/plugins/docs/README.md +513 -0
- synapse_sdk/plugins/docs/STEP.md +656 -0
- synapse_sdk/plugins/enums.py +70 -10
- synapse_sdk/plugins/errors.py +92 -0
- synapse_sdk/plugins/executors/__init__.py +43 -0
- synapse_sdk/plugins/executors/local.py +99 -0
- synapse_sdk/plugins/executors/ray/__init__.py +18 -0
- synapse_sdk/plugins/executors/ray/base.py +282 -0
- synapse_sdk/plugins/executors/ray/job.py +298 -0
- synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
- synapse_sdk/plugins/executors/ray/packaging.py +137 -0
- synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
- synapse_sdk/plugins/executors/ray/task.py +257 -0
- synapse_sdk/plugins/models/__init__.py +26 -0
- synapse_sdk/plugins/models/logger.py +173 -0
- synapse_sdk/plugins/models/pipeline.py +25 -0
- synapse_sdk/plugins/pipelines/__init__.py +81 -0
- synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
- synapse_sdk/plugins/pipelines/context.py +107 -0
- synapse_sdk/plugins/pipelines/display.py +311 -0
- synapse_sdk/plugins/runner.py +114 -0
- synapse_sdk/plugins/schemas/__init__.py +19 -0
- synapse_sdk/plugins/schemas/results.py +152 -0
- synapse_sdk/plugins/steps/__init__.py +63 -0
- synapse_sdk/plugins/steps/base.py +128 -0
- synapse_sdk/plugins/steps/context.py +90 -0
- synapse_sdk/plugins/steps/orchestrator.py +128 -0
- synapse_sdk/plugins/steps/registry.py +103 -0
- synapse_sdk/plugins/steps/utils/__init__.py +20 -0
- synapse_sdk/plugins/steps/utils/logging.py +85 -0
- synapse_sdk/plugins/steps/utils/timing.py +71 -0
- synapse_sdk/plugins/steps/utils/validation.py +68 -0
- synapse_sdk/plugins/templates/__init__.py +50 -0
- synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
- synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
- synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
- synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
- synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
- synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
- synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
- synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
- synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
- synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
- synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
- synapse_sdk/plugins/testing/__init__.py +25 -0
- synapse_sdk/plugins/testing/sample_actions.py +98 -0
- synapse_sdk/plugins/types.py +206 -0
- synapse_sdk/plugins/upload.py +595 -64
- synapse_sdk/plugins/utils.py +325 -37
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/__init__.py +1 -0
- synapse_sdk/utils/auth.py +74 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +449 -0
- synapse_sdk/utils/file/checksum.py +167 -0
- synapse_sdk/utils/file/download.py +286 -0
- synapse_sdk/utils/file/io.py +129 -0
- synapse_sdk/utils/file/requirements.py +36 -0
- synapse_sdk/utils/network.py +168 -0
- synapse_sdk/utils/storage/__init__.py +238 -0
- synapse_sdk/utils/storage/config.py +188 -0
- synapse_sdk/utils/storage/errors.py +52 -0
- synapse_sdk/utils/storage/providers/__init__.py +13 -0
- synapse_sdk/utils/storage/providers/base.py +76 -0
- synapse_sdk/utils/storage/providers/gcs.py +168 -0
- synapse_sdk/utils/storage/providers/http.py +250 -0
- synapse_sdk/utils/storage/providers/local.py +126 -0
- synapse_sdk/utils/storage/providers/s3.py +177 -0
- synapse_sdk/utils/storage/providers/sftp.py +208 -0
- synapse_sdk/utils/storage/registry.py +125 -0
- synapse_sdk/utils/websocket.py +99 -0
- synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
- synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
- synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
- locale/en/LC_MESSAGES/messages.mo +0 -0
- locale/en/LC_MESSAGES/messages.po +0 -39
- locale/ko/LC_MESSAGES/messages.mo +0 -0
- locale/ko/LC_MESSAGES/messages.po +0 -34
- synapse_sdk/cli/create_plugin.py +0 -10
- synapse_sdk/clients/agent/core.py +0 -7
- synapse_sdk/clients/agent/service.py +0 -15
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/clients/ray/__init__.py +0 -6
- synapse_sdk/clients/ray/core.py +0 -22
- synapse_sdk/clients/ray/serve.py +0 -20
- synapse_sdk/i18n.py +0 -35
- synapse_sdk/plugins/categories/__init__.py +0 -0
- synapse_sdk/plugins/categories/base.py +0 -235
- synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
- synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
- synapse_sdk/plugins/categories/decorators.py +0 -13
- synapse_sdk/plugins/categories/export/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/export.py +0 -10
- synapse_sdk/plugins/categories/import/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
- synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
- synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
- synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
- synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
- synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
- synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
- synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
- synapse_sdk/plugins/categories/registry.py +0 -16
- synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
- synapse_sdk/plugins/categories/templates.py +0 -32
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/cli/run.py +0 -67
- synapse_sdk/plugins/exceptions.py +0 -22
- synapse_sdk/plugins/models.py +0 -121
- synapse_sdk/plugins/templates/cookiecutter.json +0 -11
- synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
- synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
- synapse_sdk/shared/enums.py +0 -8
- synapse_sdk/utils/debug.py +0 -5
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/module_loading.py +0 -29
- synapse_sdk/utils/pydantic/__init__.py +0 -0
- synapse_sdk/utils/pydantic/config.py +0 -4
- synapse_sdk/utils/pydantic/errors.py +0 -33
- synapse_sdk/utils/pydantic/validators.py +0 -7
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk/utils/string.py +0 -11
- synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
- synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Plugin job command implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
12
|
+
from synapse_sdk.plugins.errors import PluginRunError
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from synapse_sdk.cli.auth import AuthConfig
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_job(
|
|
19
|
+
job_id: str,
|
|
20
|
+
auth: AuthConfig,
|
|
21
|
+
console: Console,
|
|
22
|
+
) -> dict:
|
|
23
|
+
"""Get job details.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
job_id: Job ID.
|
|
27
|
+
auth: Authentication configuration.
|
|
28
|
+
console: Rich console.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Job details dict.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
PluginRunError: If not authenticated or request fails.
|
|
35
|
+
"""
|
|
36
|
+
if not auth.access_token:
|
|
37
|
+
raise PluginRunError('Not authenticated. Run `synapse login` to authenticate.')
|
|
38
|
+
|
|
39
|
+
client = BackendClient(
|
|
40
|
+
base_url=auth.host,
|
|
41
|
+
access_token=auth.access_token,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
return client.get_job(job_id)
|
|
46
|
+
except Exception as e:
|
|
47
|
+
raise PluginRunError(f'Failed to get job: {e}') from e
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def display_job(job: dict, console: Console) -> None:
|
|
51
|
+
"""Display job details in a formatted table.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
job: Job details dict.
|
|
55
|
+
console: Rich console.
|
|
56
|
+
"""
|
|
57
|
+
table = Table(show_header=False, box=None, padding=(0, 2))
|
|
58
|
+
table.add_column('Key', style='dim')
|
|
59
|
+
table.add_column('Value')
|
|
60
|
+
|
|
61
|
+
table.add_row('ID', str(job.get('id', '-')))
|
|
62
|
+
table.add_row('Status', job.get('status', '-'))
|
|
63
|
+
table.add_row('Action', job.get('action', '-'))
|
|
64
|
+
|
|
65
|
+
if job.get('plugin_release'):
|
|
66
|
+
pr = job.get('plugin_release', {})
|
|
67
|
+
if isinstance(pr, dict):
|
|
68
|
+
table.add_row('Plugin', pr.get('plugin', '-'))
|
|
69
|
+
table.add_row('Version', pr.get('version', '-'))
|
|
70
|
+
|
|
71
|
+
if job.get('agent'):
|
|
72
|
+
agent = job.get('agent', {})
|
|
73
|
+
if isinstance(agent, dict):
|
|
74
|
+
table.add_row('Agent', agent.get('name', '-'))
|
|
75
|
+
|
|
76
|
+
if job.get('created'):
|
|
77
|
+
table.add_row('Created', str(job.get('created')))
|
|
78
|
+
|
|
79
|
+
if job.get('started'):
|
|
80
|
+
table.add_row('Started', str(job.get('started')))
|
|
81
|
+
|
|
82
|
+
if job.get('finished'):
|
|
83
|
+
table.add_row('Finished', str(job.get('finished')))
|
|
84
|
+
|
|
85
|
+
console.print(Panel(table, title=f'Job: {job.get("id", "Unknown")}', border_style='blue'))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_job_logs(
|
|
89
|
+
job_id: str,
|
|
90
|
+
auth: AuthConfig,
|
|
91
|
+
console: Console,
|
|
92
|
+
) -> dict:
|
|
93
|
+
"""Get job logs (non-streaming).
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
job_id: Job ID.
|
|
97
|
+
auth: Authentication configuration.
|
|
98
|
+
console: Rich console.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Job logs response.
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
PluginRunError: If not authenticated or request fails.
|
|
105
|
+
"""
|
|
106
|
+
if not auth.access_token:
|
|
107
|
+
raise PluginRunError('Not authenticated. Run `synapse login` to authenticate.')
|
|
108
|
+
|
|
109
|
+
client = BackendClient(
|
|
110
|
+
base_url=auth.host,
|
|
111
|
+
access_token=auth.access_token,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
return client.list_job_console_logs(job_id)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
raise PluginRunError(f'Failed to get job logs: {e}') from e
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def tail_job_logs(
|
|
121
|
+
job_id: str,
|
|
122
|
+
auth: AuthConfig,
|
|
123
|
+
console: Console,
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Tail job logs (streaming).
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
job_id: Job ID.
|
|
129
|
+
auth: Authentication configuration.
|
|
130
|
+
console: Rich console.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
PluginRunError: If not authenticated or request fails.
|
|
134
|
+
"""
|
|
135
|
+
import json
|
|
136
|
+
|
|
137
|
+
if not auth.access_token:
|
|
138
|
+
raise PluginRunError('Not authenticated. Run `synapse login` to authenticate.')
|
|
139
|
+
|
|
140
|
+
client = BackendClient(
|
|
141
|
+
base_url=auth.host,
|
|
142
|
+
access_token=auth.access_token,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
console.print(f'[dim]Tailing logs for job {job_id}...[/dim]')
|
|
146
|
+
console.print('[dim]Press Ctrl+C to stop.[/dim]\n')
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
for line in client.tail_job_console_logs(job_id):
|
|
150
|
+
if not line:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
# Parse SSE format: "data: {...}"
|
|
154
|
+
if line.startswith('data: '):
|
|
155
|
+
data_str = line[6:] # Remove "data: " prefix
|
|
156
|
+
try:
|
|
157
|
+
event = json.loads(data_str)
|
|
158
|
+
event_type = event.get('type')
|
|
159
|
+
|
|
160
|
+
if event_type == 'connected':
|
|
161
|
+
continue # Skip connection message
|
|
162
|
+
elif event_type == 'complete':
|
|
163
|
+
break # Done
|
|
164
|
+
elif event_type == 'log':
|
|
165
|
+
# The data field contains another SSE message
|
|
166
|
+
inner_data = event.get('data', '')
|
|
167
|
+
if inner_data.startswith('data: '):
|
|
168
|
+
inner_str = inner_data[6:].rstrip('\n')
|
|
169
|
+
try:
|
|
170
|
+
inner_event = json.loads(inner_str)
|
|
171
|
+
if inner_event.get('type') == 'log':
|
|
172
|
+
message = inner_event.get('message', '')
|
|
173
|
+
if message:
|
|
174
|
+
console.print(message, end='')
|
|
175
|
+
elif inner_event.get('type') == 'complete':
|
|
176
|
+
break
|
|
177
|
+
except json.JSONDecodeError:
|
|
178
|
+
console.print(inner_data)
|
|
179
|
+
else:
|
|
180
|
+
console.print(inner_data)
|
|
181
|
+
except json.JSONDecodeError:
|
|
182
|
+
console.print(data_str)
|
|
183
|
+
else:
|
|
184
|
+
console.print(line)
|
|
185
|
+
except KeyboardInterrupt:
|
|
186
|
+
console.print('\n[dim]Stopped.[/dim]')
|
|
187
|
+
except Exception as e:
|
|
188
|
+
raise PluginRunError(f'Failed to tail job logs: {e}') from e
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
__all__ = [
|
|
192
|
+
'get_job',
|
|
193
|
+
'display_job',
|
|
194
|
+
'get_job_logs',
|
|
195
|
+
'tail_job_logs',
|
|
196
|
+
]
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"""Plugin publish command implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import tempfile
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
|
|
15
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
16
|
+
from synapse_sdk.plugins.discovery import PluginDiscovery
|
|
17
|
+
from synapse_sdk.plugins.errors import ArchiveError, PluginUploadError
|
|
18
|
+
from synapse_sdk.utils.file.archive import (
|
|
19
|
+
ArchiveFilter,
|
|
20
|
+
create_archive,
|
|
21
|
+
get_archive_size,
|
|
22
|
+
list_archive_contents,
|
|
23
|
+
)
|
|
24
|
+
from synapse_sdk.utils.file.checksum import calculate_checksum
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from synapse_sdk.cli.auth import AuthConfig
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class PublishResult:
|
|
32
|
+
"""Result of plugin publish operation."""
|
|
33
|
+
|
|
34
|
+
release_id: int
|
|
35
|
+
version: str
|
|
36
|
+
checksum: str
|
|
37
|
+
file_count: int
|
|
38
|
+
archive_size: int
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_synapseignore(path: Path) -> list[str]:
|
|
42
|
+
"""Load patterns from .synapseignore file.
|
|
43
|
+
|
|
44
|
+
Supports gitignore-style patterns:
|
|
45
|
+
- `*.pyc` - glob patterns
|
|
46
|
+
- `__pycache__/` - directories
|
|
47
|
+
- `# comment` - comments
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
path: Plugin directory path.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
List of exclude patterns.
|
|
54
|
+
"""
|
|
55
|
+
ignore_file = path / '.synapseignore'
|
|
56
|
+
patterns: list[str] = []
|
|
57
|
+
|
|
58
|
+
if ignore_file.exists():
|
|
59
|
+
with ignore_file.open() as f:
|
|
60
|
+
for line in f:
|
|
61
|
+
line = line.strip()
|
|
62
|
+
# Skip empty lines and comments
|
|
63
|
+
if line and not line.startswith('#'):
|
|
64
|
+
patterns.append(line)
|
|
65
|
+
|
|
66
|
+
return patterns
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def find_config_file(path: Path, config_path: Path | None = None) -> Path:
|
|
70
|
+
"""Find plugin configuration file.
|
|
71
|
+
|
|
72
|
+
Search order:
|
|
73
|
+
1. Explicit --config path
|
|
74
|
+
2. config.yaml in path
|
|
75
|
+
3. synapse.yaml in path
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
path: Plugin directory.
|
|
79
|
+
config_path: Explicit config path from --config.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Path to config file.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
FileNotFoundError: If no config found.
|
|
86
|
+
"""
|
|
87
|
+
if config_path:
|
|
88
|
+
if not config_path.exists():
|
|
89
|
+
raise FileNotFoundError(f'Config file not found: {config_path}')
|
|
90
|
+
return config_path
|
|
91
|
+
|
|
92
|
+
for name in ('config.yaml', 'synapse.yaml'):
|
|
93
|
+
candidate = path / name
|
|
94
|
+
if candidate.exists():
|
|
95
|
+
return candidate
|
|
96
|
+
|
|
97
|
+
raise FileNotFoundError(f'No config.yaml or synapse.yaml found in {path}. Use --config to specify a custom path.')
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def create_plugin_archive(
|
|
101
|
+
source_path: Path,
|
|
102
|
+
console: Console,
|
|
103
|
+
) -> tuple[Path, str, list[str]]:
|
|
104
|
+
"""Create plugin archive with progress display.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
source_path: Plugin directory.
|
|
108
|
+
console: Rich console for output.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Tuple of (archive_path, checksum, file_list).
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
ArchiveError: If archive creation fails.
|
|
115
|
+
"""
|
|
116
|
+
# Load ignore patterns
|
|
117
|
+
ignore_patterns = load_synapseignore(source_path)
|
|
118
|
+
|
|
119
|
+
# Create filter with custom and default excludes
|
|
120
|
+
archive_filter = ArchiveFilter.from_patterns(
|
|
121
|
+
exclude=ignore_patterns,
|
|
122
|
+
use_defaults=True,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Create temp archive
|
|
126
|
+
temp_dir = tempfile.mkdtemp()
|
|
127
|
+
archive_path = Path(temp_dir) / 'plugin.zip'
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
with Progress(
|
|
131
|
+
SpinnerColumn(),
|
|
132
|
+
TextColumn('[progress.description]{task.description}'),
|
|
133
|
+
console=console,
|
|
134
|
+
) as progress:
|
|
135
|
+
task = progress.add_task('Creating archive...', total=None)
|
|
136
|
+
|
|
137
|
+
create_archive(
|
|
138
|
+
source_path,
|
|
139
|
+
archive_path,
|
|
140
|
+
filter=archive_filter,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
progress.update(task, description='Calculating checksum...')
|
|
144
|
+
checksum = calculate_checksum(archive_path)
|
|
145
|
+
|
|
146
|
+
except Exception as e:
|
|
147
|
+
raise ArchiveError(f'Failed to create archive: {e}') from e
|
|
148
|
+
|
|
149
|
+
# Get file list for display
|
|
150
|
+
file_list = list_archive_contents(archive_path)
|
|
151
|
+
|
|
152
|
+
return archive_path, checksum, file_list
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def display_files_preview(
|
|
156
|
+
file_list: list[str],
|
|
157
|
+
archive_size: int,
|
|
158
|
+
console: Console,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""Display preview of files to be uploaded.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
file_list: List of files in archive.
|
|
164
|
+
archive_size: Archive size in bytes.
|
|
165
|
+
console: Rich console for output.
|
|
166
|
+
"""
|
|
167
|
+
table = Table(title='Files to upload', show_lines=False, show_header=False)
|
|
168
|
+
table.add_column('File', style='cyan')
|
|
169
|
+
|
|
170
|
+
# Show first 15 files
|
|
171
|
+
for file_path in sorted(file_list)[:15]:
|
|
172
|
+
table.add_row(file_path)
|
|
173
|
+
|
|
174
|
+
if len(file_list) > 15:
|
|
175
|
+
table.add_row(f'... and {len(file_list) - 15} more files', style='dim')
|
|
176
|
+
|
|
177
|
+
console.print(table)
|
|
178
|
+
console.print(f'\nTotal: [bold]{len(file_list)}[/bold] files, [bold]{archive_size / 1024:.1f} KB[/bold]')
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def publish_plugin(
|
|
182
|
+
path: Path,
|
|
183
|
+
auth: AuthConfig,
|
|
184
|
+
console: Console,
|
|
185
|
+
*,
|
|
186
|
+
config_path: Path | None = None,
|
|
187
|
+
dry_run: bool = False,
|
|
188
|
+
debug: bool = False,
|
|
189
|
+
) -> PublishResult:
|
|
190
|
+
"""Execute plugin publish workflow.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
path: Plugin directory.
|
|
194
|
+
auth: Authentication config.
|
|
195
|
+
console: Rich console.
|
|
196
|
+
config_path: Optional explicit config path.
|
|
197
|
+
dry_run: If True, skip actual upload.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
PublishResult with release details.
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
FileNotFoundError: If config not found.
|
|
204
|
+
ArchiveError: If archive creation fails.
|
|
205
|
+
PluginUploadError: If upload fails.
|
|
206
|
+
"""
|
|
207
|
+
# 0. Check authentication
|
|
208
|
+
if not auth.access_token:
|
|
209
|
+
console.print('[red]Error:[/red] Not authenticated. Run [bold]synapse login[/bold] to authenticate.')
|
|
210
|
+
raise PluginUploadError('Not authenticated. Run `synapse login` to authenticate.')
|
|
211
|
+
|
|
212
|
+
# 1. Find and load config
|
|
213
|
+
config_file = find_config_file(path, config_path)
|
|
214
|
+
console.print(f'[dim]Using config:[/dim] {config_file}')
|
|
215
|
+
|
|
216
|
+
discovery = PluginDiscovery.from_path(config_file)
|
|
217
|
+
config = discovery.config
|
|
218
|
+
|
|
219
|
+
# 1.5. Sync input_type/output_type from code to config.yaml
|
|
220
|
+
try:
|
|
221
|
+
changes = discovery.sync_config_file(config_file)
|
|
222
|
+
if changes:
|
|
223
|
+
console.print('[dim]Synced types from code:[/dim]')
|
|
224
|
+
for action_name, type_info in changes.items():
|
|
225
|
+
console.print(f' [cyan]{action_name}[/cyan]: {type_info}')
|
|
226
|
+
# Reload discovery after sync
|
|
227
|
+
discovery = PluginDiscovery.from_path(config_file)
|
|
228
|
+
config = discovery.config
|
|
229
|
+
except Exception as e:
|
|
230
|
+
console.print(f'[yellow]Warning: Could not sync types: {e}[/yellow]')
|
|
231
|
+
|
|
232
|
+
# Display plugin info
|
|
233
|
+
actions_str = ', '.join(discovery.list_actions())
|
|
234
|
+
console.print(
|
|
235
|
+
Panel(
|
|
236
|
+
f'[bold]{config.name}[/bold] v{config.version}\n'
|
|
237
|
+
f'Code: {config.code}\n'
|
|
238
|
+
f'Category: {config.category.value}\n'
|
|
239
|
+
f'Actions: {actions_str}',
|
|
240
|
+
title='Plugin',
|
|
241
|
+
border_style='blue',
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# 2. Create archive
|
|
246
|
+
archive_path, checksum, file_list = create_plugin_archive(path, console)
|
|
247
|
+
archive_size = get_archive_size(archive_path)
|
|
248
|
+
|
|
249
|
+
# 3. Display preview
|
|
250
|
+
display_files_preview(file_list, archive_size, console)
|
|
251
|
+
|
|
252
|
+
if dry_run:
|
|
253
|
+
console.print('\n[yellow]Dry run - skipping upload[/yellow]')
|
|
254
|
+
# Clean up temp file
|
|
255
|
+
archive_path.unlink(missing_ok=True)
|
|
256
|
+
return PublishResult(
|
|
257
|
+
release_id=0,
|
|
258
|
+
version=config.version,
|
|
259
|
+
checksum=checksum,
|
|
260
|
+
file_count=len(file_list),
|
|
261
|
+
archive_size=archive_size,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# 4. Upload to backend
|
|
265
|
+
try:
|
|
266
|
+
client = BackendClient(
|
|
267
|
+
base_url=auth.host,
|
|
268
|
+
access_token=auth.access_token,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
with Progress(
|
|
272
|
+
SpinnerColumn(),
|
|
273
|
+
TextColumn('[progress.description]{task.description}'),
|
|
274
|
+
BarColumn(),
|
|
275
|
+
console=console,
|
|
276
|
+
) as progress:
|
|
277
|
+
task = progress.add_task('Uploading...', total=100)
|
|
278
|
+
|
|
279
|
+
# Prepare release data
|
|
280
|
+
release_data = {
|
|
281
|
+
'plugin': config.code,
|
|
282
|
+
'version': config.version,
|
|
283
|
+
'config': discovery.to_config_dict(include_ui_schemas=True),
|
|
284
|
+
'debug': debug,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# Create release with file upload
|
|
288
|
+
response = client.create_plugin_release(
|
|
289
|
+
release_data,
|
|
290
|
+
file=archive_path,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
progress.update(task, completed=100)
|
|
294
|
+
|
|
295
|
+
if debug:
|
|
296
|
+
console.print(f'[dim]Response: {response}[/dim]')
|
|
297
|
+
|
|
298
|
+
release_id = response.get('id', 0)
|
|
299
|
+
|
|
300
|
+
except Exception as e:
|
|
301
|
+
raise PluginUploadError(f'Failed to upload plugin: {e}') from e
|
|
302
|
+
finally:
|
|
303
|
+
# Clean up temp file
|
|
304
|
+
archive_path.unlink(missing_ok=True)
|
|
305
|
+
|
|
306
|
+
return PublishResult(
|
|
307
|
+
release_id=release_id,
|
|
308
|
+
version=config.version,
|
|
309
|
+
checksum=checksum,
|
|
310
|
+
file_count=len(file_list),
|
|
311
|
+
archive_size=archive_size,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
__all__ = [
|
|
316
|
+
'PublishResult',
|
|
317
|
+
'load_synapseignore',
|
|
318
|
+
'find_config_file',
|
|
319
|
+
'create_plugin_archive',
|
|
320
|
+
'display_files_preview',
|
|
321
|
+
'publish_plugin',
|
|
322
|
+
]
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Plugin run command implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
11
|
+
|
|
12
|
+
from synapse_sdk.cli.plugin.publish import find_config_file
|
|
13
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
14
|
+
from synapse_sdk.plugins.discovery import PluginDiscovery
|
|
15
|
+
from synapse_sdk.plugins.errors import PluginRunError
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from synapse_sdk.cli.agent.config import AgentConfig
|
|
19
|
+
from synapse_sdk.cli.auth import AuthConfig
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class RunResult:
|
|
24
|
+
"""Result of plugin run operation."""
|
|
25
|
+
|
|
26
|
+
action: str
|
|
27
|
+
plugin: str
|
|
28
|
+
result: Any
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def resolve_plugin_code(
|
|
32
|
+
plugin: str | None,
|
|
33
|
+
path: Path | None = None,
|
|
34
|
+
) -> str:
|
|
35
|
+
"""Resolve plugin code from argument or config file.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
plugin: Explicit plugin code.
|
|
39
|
+
path: Directory to search for config file.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Plugin code string.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
FileNotFoundError: If no config found when plugin not specified.
|
|
46
|
+
"""
|
|
47
|
+
if plugin:
|
|
48
|
+
return plugin
|
|
49
|
+
|
|
50
|
+
config_file = find_config_file(path or Path.cwd())
|
|
51
|
+
discovery = PluginDiscovery.from_path(config_file)
|
|
52
|
+
return discovery.config.code
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run_plugin(
|
|
56
|
+
action: str,
|
|
57
|
+
auth: AuthConfig,
|
|
58
|
+
agent: AgentConfig,
|
|
59
|
+
console: Console,
|
|
60
|
+
*,
|
|
61
|
+
plugin: str,
|
|
62
|
+
params: dict[str, Any] | None = None,
|
|
63
|
+
debug: bool = False,
|
|
64
|
+
) -> RunResult:
|
|
65
|
+
"""Execute plugin run workflow.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
action: Action name to execute (e.g., deployment, train).
|
|
69
|
+
auth: Authentication config with host and access token.
|
|
70
|
+
agent: Agent configuration with id.
|
|
71
|
+
console: Rich console.
|
|
72
|
+
plugin: Plugin code.
|
|
73
|
+
params: Optional parameters to pass to the action.
|
|
74
|
+
debug: Debug mode flag.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
RunResult with action result.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
PluginRunError: If auth/agent not configured or run fails.
|
|
81
|
+
"""
|
|
82
|
+
# Validate auth configuration
|
|
83
|
+
if not auth.access_token:
|
|
84
|
+
raise PluginRunError('Not authenticated. Run `synapse login` to authenticate.')
|
|
85
|
+
|
|
86
|
+
console.print(f'[dim]Plugin:[/dim] {plugin}')
|
|
87
|
+
console.print(f'[dim]Action:[/dim] {action}')
|
|
88
|
+
console.print(f'[dim]Agent:[/dim] {agent.name or agent.id}')
|
|
89
|
+
|
|
90
|
+
# Create backend client
|
|
91
|
+
client = BackendClient(
|
|
92
|
+
base_url=auth.host,
|
|
93
|
+
access_token=auth.access_token,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Build request data
|
|
97
|
+
data: dict[str, Any] = {
|
|
98
|
+
'agent': agent.id,
|
|
99
|
+
'action': action,
|
|
100
|
+
}
|
|
101
|
+
if params:
|
|
102
|
+
data['params'] = params
|
|
103
|
+
if debug:
|
|
104
|
+
data['debug'] = debug
|
|
105
|
+
|
|
106
|
+
# Run the action
|
|
107
|
+
try:
|
|
108
|
+
with Progress(
|
|
109
|
+
SpinnerColumn(),
|
|
110
|
+
TextColumn('[progress.description]{task.description}'),
|
|
111
|
+
console=console,
|
|
112
|
+
) as progress:
|
|
113
|
+
progress.add_task(f'Running {action}...', total=None)
|
|
114
|
+
|
|
115
|
+
result = client.run_plugin(plugin, data)
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
raise PluginRunError(f'Failed to run plugin: {e}') from e
|
|
119
|
+
|
|
120
|
+
return RunResult(
|
|
121
|
+
action=action,
|
|
122
|
+
plugin=plugin,
|
|
123
|
+
result=result,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
__all__ = [
|
|
128
|
+
'RunResult',
|
|
129
|
+
'resolve_plugin_code',
|
|
130
|
+
'run_plugin',
|
|
131
|
+
]
|