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.

Files changed (261) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +9 -8
  3. synapse_sdk/cli/agent/__init__.py +25 -0
  4. synapse_sdk/cli/agent/config.py +104 -0
  5. synapse_sdk/cli/agent/select.py +197 -0
  6. synapse_sdk/cli/auth.py +104 -0
  7. synapse_sdk/cli/main.py +1025 -0
  8. synapse_sdk/cli/plugin/__init__.py +58 -0
  9. synapse_sdk/cli/plugin/create.py +566 -0
  10. synapse_sdk/cli/plugin/job.py +196 -0
  11. synapse_sdk/cli/plugin/publish.py +322 -0
  12. synapse_sdk/cli/plugin/run.py +131 -0
  13. synapse_sdk/cli/plugin/test.py +200 -0
  14. synapse_sdk/clients/README.md +239 -0
  15. synapse_sdk/clients/__init__.py +5 -0
  16. synapse_sdk/clients/_template.py +266 -0
  17. synapse_sdk/clients/agent/__init__.py +84 -29
  18. synapse_sdk/clients/agent/async_ray.py +289 -0
  19. synapse_sdk/clients/agent/container.py +83 -0
  20. synapse_sdk/clients/agent/plugin.py +101 -0
  21. synapse_sdk/clients/agent/ray.py +296 -39
  22. synapse_sdk/clients/backend/__init__.py +152 -12
  23. synapse_sdk/clients/backend/annotation.py +164 -22
  24. synapse_sdk/clients/backend/core.py +101 -0
  25. synapse_sdk/clients/backend/data_collection.py +292 -0
  26. synapse_sdk/clients/backend/hitl.py +87 -0
  27. synapse_sdk/clients/backend/integration.py +374 -46
  28. synapse_sdk/clients/backend/ml.py +134 -22
  29. synapse_sdk/clients/backend/models.py +247 -0
  30. synapse_sdk/clients/base.py +538 -59
  31. synapse_sdk/clients/exceptions.py +35 -7
  32. synapse_sdk/clients/pipeline/__init__.py +5 -0
  33. synapse_sdk/clients/pipeline/client.py +636 -0
  34. synapse_sdk/clients/protocols.py +178 -0
  35. synapse_sdk/clients/utils.py +86 -8
  36. synapse_sdk/clients/validation.py +58 -0
  37. synapse_sdk/enums.py +76 -0
  38. synapse_sdk/exceptions.py +168 -0
  39. synapse_sdk/integrations/__init__.py +74 -0
  40. synapse_sdk/integrations/_base.py +119 -0
  41. synapse_sdk/integrations/_context.py +53 -0
  42. synapse_sdk/integrations/ultralytics/__init__.py +78 -0
  43. synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
  44. synapse_sdk/integrations/ultralytics/_patches.py +124 -0
  45. synapse_sdk/loggers.py +476 -95
  46. synapse_sdk/mcp/MCP.md +69 -0
  47. synapse_sdk/mcp/__init__.py +48 -0
  48. synapse_sdk/mcp/__main__.py +6 -0
  49. synapse_sdk/mcp/config.py +349 -0
  50. synapse_sdk/mcp/prompts/__init__.py +4 -0
  51. synapse_sdk/mcp/resources/__init__.py +4 -0
  52. synapse_sdk/mcp/server.py +1352 -0
  53. synapse_sdk/mcp/tools/__init__.py +6 -0
  54. synapse_sdk/plugins/__init__.py +133 -9
  55. synapse_sdk/plugins/action.py +229 -0
  56. synapse_sdk/plugins/actions/__init__.py +82 -0
  57. synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
  58. synapse_sdk/plugins/actions/dataset/action.py +471 -0
  59. synapse_sdk/plugins/actions/export/__init__.py +55 -0
  60. synapse_sdk/plugins/actions/export/action.py +183 -0
  61. synapse_sdk/plugins/actions/export/context.py +59 -0
  62. synapse_sdk/plugins/actions/inference/__init__.py +84 -0
  63. synapse_sdk/plugins/actions/inference/action.py +285 -0
  64. synapse_sdk/plugins/actions/inference/context.py +81 -0
  65. synapse_sdk/plugins/actions/inference/deployment.py +322 -0
  66. synapse_sdk/plugins/actions/inference/serve.py +252 -0
  67. synapse_sdk/plugins/actions/train/__init__.py +54 -0
  68. synapse_sdk/plugins/actions/train/action.py +326 -0
  69. synapse_sdk/plugins/actions/train/context.py +57 -0
  70. synapse_sdk/plugins/actions/upload/__init__.py +49 -0
  71. synapse_sdk/plugins/actions/upload/action.py +165 -0
  72. synapse_sdk/plugins/actions/upload/context.py +61 -0
  73. synapse_sdk/plugins/config.py +98 -0
  74. synapse_sdk/plugins/context/__init__.py +109 -0
  75. synapse_sdk/plugins/context/env.py +113 -0
  76. synapse_sdk/plugins/datasets/__init__.py +113 -0
  77. synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
  78. synapse_sdk/plugins/datasets/converters/base.py +347 -0
  79. synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
  80. synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
  81. synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
  82. synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
  83. synapse_sdk/plugins/datasets/formats/dm.py +351 -0
  84. synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
  85. synapse_sdk/plugins/decorators.py +83 -0
  86. synapse_sdk/plugins/discovery.py +790 -0
  87. synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
  88. synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
  89. synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
  90. synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
  91. synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
  92. synapse_sdk/plugins/docs/README.md +513 -0
  93. synapse_sdk/plugins/docs/STEP.md +656 -0
  94. synapse_sdk/plugins/enums.py +70 -10
  95. synapse_sdk/plugins/errors.py +92 -0
  96. synapse_sdk/plugins/executors/__init__.py +43 -0
  97. synapse_sdk/plugins/executors/local.py +99 -0
  98. synapse_sdk/plugins/executors/ray/__init__.py +18 -0
  99. synapse_sdk/plugins/executors/ray/base.py +282 -0
  100. synapse_sdk/plugins/executors/ray/job.py +298 -0
  101. synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
  102. synapse_sdk/plugins/executors/ray/packaging.py +137 -0
  103. synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
  104. synapse_sdk/plugins/executors/ray/task.py +257 -0
  105. synapse_sdk/plugins/models/__init__.py +26 -0
  106. synapse_sdk/plugins/models/logger.py +173 -0
  107. synapse_sdk/plugins/models/pipeline.py +25 -0
  108. synapse_sdk/plugins/pipelines/__init__.py +81 -0
  109. synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
  110. synapse_sdk/plugins/pipelines/context.py +107 -0
  111. synapse_sdk/plugins/pipelines/display.py +311 -0
  112. synapse_sdk/plugins/runner.py +114 -0
  113. synapse_sdk/plugins/schemas/__init__.py +19 -0
  114. synapse_sdk/plugins/schemas/results.py +152 -0
  115. synapse_sdk/plugins/steps/__init__.py +63 -0
  116. synapse_sdk/plugins/steps/base.py +128 -0
  117. synapse_sdk/plugins/steps/context.py +90 -0
  118. synapse_sdk/plugins/steps/orchestrator.py +128 -0
  119. synapse_sdk/plugins/steps/registry.py +103 -0
  120. synapse_sdk/plugins/steps/utils/__init__.py +20 -0
  121. synapse_sdk/plugins/steps/utils/logging.py +85 -0
  122. synapse_sdk/plugins/steps/utils/timing.py +71 -0
  123. synapse_sdk/plugins/steps/utils/validation.py +68 -0
  124. synapse_sdk/plugins/templates/__init__.py +50 -0
  125. synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
  126. synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
  127. synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
  128. synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
  129. synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
  130. synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
  131. synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
  132. synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
  133. synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
  134. synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
  135. synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
  136. synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
  137. synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
  138. synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
  139. synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
  140. synapse_sdk/plugins/testing/__init__.py +25 -0
  141. synapse_sdk/plugins/testing/sample_actions.py +98 -0
  142. synapse_sdk/plugins/types.py +206 -0
  143. synapse_sdk/plugins/upload.py +595 -64
  144. synapse_sdk/plugins/utils.py +325 -37
  145. synapse_sdk/shared/__init__.py +25 -0
  146. synapse_sdk/utils/__init__.py +1 -0
  147. synapse_sdk/utils/auth.py +74 -0
  148. synapse_sdk/utils/file/__init__.py +58 -0
  149. synapse_sdk/utils/file/archive.py +449 -0
  150. synapse_sdk/utils/file/checksum.py +167 -0
  151. synapse_sdk/utils/file/download.py +286 -0
  152. synapse_sdk/utils/file/io.py +129 -0
  153. synapse_sdk/utils/file/requirements.py +36 -0
  154. synapse_sdk/utils/network.py +168 -0
  155. synapse_sdk/utils/storage/__init__.py +238 -0
  156. synapse_sdk/utils/storage/config.py +188 -0
  157. synapse_sdk/utils/storage/errors.py +52 -0
  158. synapse_sdk/utils/storage/providers/__init__.py +13 -0
  159. synapse_sdk/utils/storage/providers/base.py +76 -0
  160. synapse_sdk/utils/storage/providers/gcs.py +168 -0
  161. synapse_sdk/utils/storage/providers/http.py +250 -0
  162. synapse_sdk/utils/storage/providers/local.py +126 -0
  163. synapse_sdk/utils/storage/providers/s3.py +177 -0
  164. synapse_sdk/utils/storage/providers/sftp.py +208 -0
  165. synapse_sdk/utils/storage/registry.py +125 -0
  166. synapse_sdk/utils/websocket.py +99 -0
  167. synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
  168. synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
  169. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
  170. synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
  171. locale/en/LC_MESSAGES/messages.mo +0 -0
  172. locale/en/LC_MESSAGES/messages.po +0 -39
  173. locale/ko/LC_MESSAGES/messages.mo +0 -0
  174. locale/ko/LC_MESSAGES/messages.po +0 -34
  175. synapse_sdk/cli/create_plugin.py +0 -10
  176. synapse_sdk/clients/agent/core.py +0 -7
  177. synapse_sdk/clients/agent/service.py +0 -15
  178. synapse_sdk/clients/backend/dataset.py +0 -51
  179. synapse_sdk/clients/ray/__init__.py +0 -6
  180. synapse_sdk/clients/ray/core.py +0 -22
  181. synapse_sdk/clients/ray/serve.py +0 -20
  182. synapse_sdk/i18n.py +0 -35
  183. synapse_sdk/plugins/categories/__init__.py +0 -0
  184. synapse_sdk/plugins/categories/base.py +0 -235
  185. synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
  186. synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
  187. synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
  188. synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
  189. synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
  190. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
  191. synapse_sdk/plugins/categories/decorators.py +0 -13
  192. synapse_sdk/plugins/categories/export/__init__.py +0 -0
  193. synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
  194. synapse_sdk/plugins/categories/export/actions/export.py +0 -10
  195. synapse_sdk/plugins/categories/import/__init__.py +0 -0
  196. synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
  197. synapse_sdk/plugins/categories/import/actions/import.py +0 -10
  198. synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
  199. synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
  200. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
  201. synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
  202. synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
  203. synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
  204. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
  205. synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
  206. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
  207. synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
  208. synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
  209. synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
  210. synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
  211. synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
  212. synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
  213. synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
  214. synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
  215. synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
  216. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
  217. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
  218. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
  219. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
  220. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
  221. synapse_sdk/plugins/categories/registry.py +0 -16
  222. synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
  223. synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
  224. synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
  225. synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
  226. synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
  227. synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
  228. synapse_sdk/plugins/categories/templates.py +0 -32
  229. synapse_sdk/plugins/cli/__init__.py +0 -21
  230. synapse_sdk/plugins/cli/publish.py +0 -37
  231. synapse_sdk/plugins/cli/run.py +0 -67
  232. synapse_sdk/plugins/exceptions.py +0 -22
  233. synapse_sdk/plugins/models.py +0 -121
  234. synapse_sdk/plugins/templates/cookiecutter.json +0 -11
  235. synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
  236. synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
  237. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
  238. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
  239. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
  240. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
  241. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
  242. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
  243. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
  244. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
  245. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
  246. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
  247. synapse_sdk/shared/enums.py +0 -8
  248. synapse_sdk/utils/debug.py +0 -5
  249. synapse_sdk/utils/file.py +0 -87
  250. synapse_sdk/utils/module_loading.py +0 -29
  251. synapse_sdk/utils/pydantic/__init__.py +0 -0
  252. synapse_sdk/utils/pydantic/config.py +0 -4
  253. synapse_sdk/utils/pydantic/errors.py +0 -33
  254. synapse_sdk/utils/pydantic/validators.py +0 -7
  255. synapse_sdk/utils/storage.py +0 -91
  256. synapse_sdk/utils/string.py +0 -11
  257. synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
  258. synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
  259. synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
  260. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
  261. {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
+ ]