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
synapse_sdk/__init__.py CHANGED
@@ -0,0 +1,24 @@
1
+ """
2
+ Export / Import Guidelines
3
+ --------------------------
4
+
5
+ 1. Do NOT import the top-level package directly.
6
+ - All imports must start from at least two levels below the root package.
7
+ (e.g., `project.module.submodule` is allowed,
8
+ `project` 또는 `project.module` 단일 import는 금지)
9
+
10
+ 2. Wildcard import (`from x import *`) is strictly prohibited.
11
+ - 모든 외부 노출(export)은 명시적인 이름 기반으로 관리해야 한다.
12
+ - `__all__` 리스트를 통해 공개할 API를 명확히 정의할 것.
13
+
14
+ 3. Public API 를 구성할 때:
15
+ - 하위 모듈에서 export할 항목만 `__all__`에 선언한다.
16
+ - 내부 구현용 함수/클래스는 `_` prefix 를 사용하거나 `__all__`에 포함하지 않는다.
17
+
18
+ 4. 모듈 간 의존성은 최단 경로만 허용한다.
19
+ - 불필요한 상위/평행 패키지 import 경로는 금지하여 순환 의존성(circular dependency)을 방지한다.
20
+ """
21
+
22
+ from synapse_sdk.shared import worker_process_setup_hook
23
+
24
+ __all__ = ['worker_process_setup_hook']
@@ -1,11 +1,12 @@
1
- import click
1
+ """Synapse SDK CLI.
2
2
 
3
- from synapse_sdk.cli.create_plugin import create_plugin
3
+ Usage:
4
+ synapse --help
5
+ synapse run <plugin> <action> [--params JSON]
6
+ synapse config <path> [--format yaml|json]
7
+ synapse version
8
+ """
4
9
 
10
+ from synapse_sdk.cli.main import cli
5
11
 
6
- @click.group()
7
- def cli():
8
- pass
9
-
10
-
11
- cli.add_command(create_plugin)
12
+ __all__ = ['cli']
@@ -0,0 +1,25 @@
1
+ """Agent CLI commands."""
2
+
3
+ from synapse_sdk.cli.agent.config import (
4
+ AgentConfig,
5
+ clear_agent_config,
6
+ get_agent_config,
7
+ set_agent_config,
8
+ )
9
+ from synapse_sdk.cli.agent.select import (
10
+ check_agent_connection,
11
+ fetch_agents,
12
+ select_agent_interactive,
13
+ )
14
+
15
+ __all__ = [
16
+ # Config
17
+ 'AgentConfig',
18
+ 'get_agent_config',
19
+ 'set_agent_config',
20
+ 'clear_agent_config',
21
+ # Selection
22
+ 'fetch_agents',
23
+ 'select_agent_interactive',
24
+ 'check_agent_connection',
25
+ ]
@@ -0,0 +1,104 @@
1
+ """Agent configuration management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+ # Config file path
10
+ CONFIG_DIR = Path.home() / '.synapse'
11
+ CONFIG_FILE = CONFIG_DIR / 'config.json'
12
+
13
+
14
+ @dataclass
15
+ class AgentConfig:
16
+ """Agent configuration."""
17
+
18
+ id: int
19
+ name: str | None = None
20
+ url: str | None = None
21
+ token: str | None = None
22
+
23
+
24
+ def _ensure_config_dir() -> None:
25
+ """Ensure the config directory exists."""
26
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
27
+
28
+
29
+ def _load_config() -> dict:
30
+ """Load configuration from file."""
31
+ _ensure_config_dir()
32
+ if not CONFIG_FILE.exists():
33
+ return {}
34
+ try:
35
+ return json.loads(CONFIG_FILE.read_text())
36
+ except (json.JSONDecodeError, IOError):
37
+ return {}
38
+
39
+
40
+ def _save_config(config: dict) -> None:
41
+ """Save configuration to file."""
42
+ _ensure_config_dir()
43
+ CONFIG_FILE.write_text(json.dumps(config, indent=2))
44
+ CONFIG_FILE.chmod(0o600)
45
+
46
+
47
+ def get_agent_config() -> AgentConfig | None:
48
+ """Get current agent configuration.
49
+
50
+ Returns:
51
+ AgentConfig if configured, None otherwise.
52
+ """
53
+ config = _load_config()
54
+ agent = config.get('agent')
55
+ if not agent or 'id' not in agent:
56
+ return None
57
+ return AgentConfig(
58
+ id=agent['id'],
59
+ name=agent.get('name'),
60
+ url=agent.get('url'),
61
+ token=agent.get('token'),
62
+ )
63
+
64
+
65
+ def set_agent_config(
66
+ agent_id: int,
67
+ *,
68
+ name: str | None = None,
69
+ url: str | None = None,
70
+ token: str | None = None,
71
+ ) -> None:
72
+ """Set agent configuration.
73
+
74
+ Args:
75
+ agent_id: Agent ID.
76
+ name: Agent name.
77
+ url: Agent URL.
78
+ token: Agent authentication token.
79
+ """
80
+ config = _load_config()
81
+ config['agent'] = {'id': agent_id}
82
+ if name:
83
+ config['agent']['name'] = name
84
+ if url:
85
+ config['agent']['url'] = url
86
+ if token:
87
+ config['agent']['token'] = token
88
+ _save_config(config)
89
+
90
+
91
+ def clear_agent_config() -> None:
92
+ """Clear agent configuration."""
93
+ config = _load_config()
94
+ if 'agent' in config:
95
+ del config['agent']
96
+ _save_config(config)
97
+
98
+
99
+ __all__ = [
100
+ 'AgentConfig',
101
+ 'get_agent_config',
102
+ 'set_agent_config',
103
+ 'clear_agent_config',
104
+ ]
@@ -0,0 +1,197 @@
1
+ """Agent selection and connection utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import questionary
8
+ import requests
9
+ from rich.console import Console
10
+ from rich.panel import Panel
11
+ from rich.table import Table
12
+
13
+ from synapse_sdk.cli.agent.config import AgentConfig, set_agent_config
14
+ from synapse_sdk.cli.auth import AuthConfig
15
+ from synapse_sdk.clients.backend import Agent, BackendClient
16
+
17
+
18
+ @dataclass
19
+ class ConnectionResult:
20
+ """Result of connection test."""
21
+
22
+ success: bool
23
+ message: str
24
+
25
+
26
+ def fetch_agents(auth: AuthConfig) -> list[Agent]:
27
+ """Fetch available agents from the backend.
28
+
29
+ Args:
30
+ auth: Authentication configuration.
31
+
32
+ Returns:
33
+ List of Agent objects.
34
+ """
35
+ client = BackendClient(auth.host, access_token=auth.access_token)
36
+ return client.list_agents()
37
+
38
+
39
+ def check_agent_connection(url: str, token: str, *, timeout: int = 5) -> ConnectionResult:
40
+ """Test agent connection.
41
+
42
+ Args:
43
+ url: Agent URL.
44
+ token: Agent authentication token.
45
+ timeout: Request timeout in seconds.
46
+
47
+ Returns:
48
+ ConnectionResult with success status and message.
49
+ """
50
+ if not url or not token:
51
+ return ConnectionResult(success=True, message='Agent configured (no URL/token to test)')
52
+
53
+ try:
54
+ response = requests.get(
55
+ f'{url.rstrip("/")}/health/',
56
+ headers={'Authorization': token},
57
+ timeout=timeout,
58
+ )
59
+
60
+ if response.status_code == 200:
61
+ return ConnectionResult(success=True, message='Connection successful')
62
+ elif response.status_code == 401:
63
+ return ConnectionResult(success=False, message='Invalid agent token (401)')
64
+ elif response.status_code == 403:
65
+ return ConnectionResult(success=False, message='Access forbidden (403)')
66
+ else:
67
+ return ConnectionResult(success=False, message=f'HTTP {response.status_code}')
68
+
69
+ except requests.exceptions.Timeout:
70
+ return ConnectionResult(success=False, message=f'Connection timeout (>{timeout}s)')
71
+ except requests.exceptions.ConnectionError:
72
+ return ConnectionResult(success=False, message='Connection failed')
73
+ except Exception as e:
74
+ return ConnectionResult(success=False, message=f'Error: {e}')
75
+
76
+
77
+ def display_agents_table(agents: list[Agent], console: Console) -> None:
78
+ """Display agents in a table format.
79
+
80
+ Args:
81
+ agents: List of agents to display.
82
+ console: Rich console for output.
83
+ """
84
+ table = Table(title='Available Agents', show_header=True, header_style='bold')
85
+ table.add_column('ID', style='dim')
86
+ table.add_column('Name')
87
+ table.add_column('Status')
88
+ table.add_column('URL', style='dim')
89
+
90
+ for agent in agents:
91
+ status = agent.status or 'unknown'
92
+ status_style = 'green' if agent.is_connected else 'red'
93
+ table.add_row(
94
+ str(agent.id),
95
+ agent.name,
96
+ f'[{status_style}]{status}[/{status_style}]',
97
+ agent.url or '-',
98
+ )
99
+
100
+ console.print(table)
101
+
102
+
103
+ def select_agent_interactive(
104
+ auth: AuthConfig,
105
+ console: Console,
106
+ ) -> AgentConfig | None:
107
+ """Interactively select an agent from the backend.
108
+
109
+ Args:
110
+ auth: Authentication configuration.
111
+ console: Rich console for output.
112
+
113
+ Returns:
114
+ Selected AgentConfig if successful, None if cancelled.
115
+ """
116
+ console.print('[dim]Fetching available agents...[/dim]')
117
+
118
+ try:
119
+ agents = fetch_agents(auth)
120
+ except Exception as e:
121
+ console.print(f'[red]Error fetching agents:[/red] {e}')
122
+ return None
123
+
124
+ if not agents:
125
+ console.print('[yellow]No agents found in current workspace.[/yellow]')
126
+ return None
127
+
128
+ # Create ID lookup map
129
+ agents_by_id = {agent.id: agent for agent in agents}
130
+
131
+ # Display the nice Rich table
132
+ console.print()
133
+ display_agents_table(agents, console)
134
+ console.print()
135
+
136
+ # Prompt for agent ID
137
+ agent_id_str = questionary.text(
138
+ 'Enter agent ID (leave empty to cancel):',
139
+ ).ask()
140
+
141
+ if not agent_id_str:
142
+ return None
143
+
144
+ try:
145
+ agent_id = int(agent_id_str)
146
+ except ValueError:
147
+ console.print(f'[red]Invalid ID:[/red] {agent_id_str}')
148
+ return None
149
+
150
+ selected = agents_by_id.get(agent_id)
151
+ if not selected:
152
+ console.print(f'[red]Agent not found:[/red] {agent_id}')
153
+ return None
154
+
155
+ # Save configuration
156
+ set_agent_config(
157
+ selected.id,
158
+ name=selected.name,
159
+ url=selected.url,
160
+ token=selected.token,
161
+ )
162
+
163
+ # Display success
164
+ console.print()
165
+ console.print(
166
+ Panel(
167
+ f'[bold]{selected.name}[/bold]\n\nID: {selected.id}\nURL: {selected.url or "Not set"}',
168
+ title='Agent Selected',
169
+ border_style='green',
170
+ )
171
+ )
172
+
173
+ # Test connection if URL and token are available
174
+ if selected.url and selected.token:
175
+ console.print()
176
+ console.print('[dim]Testing connection...[/dim]')
177
+ result = check_agent_connection(selected.url, selected.token)
178
+ if result.success:
179
+ console.print(f'[green]{result.message}[/green]')
180
+ else:
181
+ console.print(f'[red]{result.message}[/red]')
182
+
183
+ return AgentConfig(
184
+ id=selected.id,
185
+ name=selected.name,
186
+ url=selected.url,
187
+ token=selected.token,
188
+ )
189
+
190
+
191
+ __all__ = [
192
+ 'ConnectionResult',
193
+ 'fetch_agents',
194
+ 'check_agent_connection',
195
+ 'display_agents_table',
196
+ 'select_agent_interactive',
197
+ ]
@@ -0,0 +1,104 @@
1
+ """CLI authentication utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import questionary
8
+ import typer
9
+ from rich.console import Console
10
+
11
+ from synapse_sdk.utils.auth import (
12
+ CONFIG_FILE,
13
+ DEFAULT_HOST,
14
+ ENV_SYNAPSE_ACCESS_TOKEN,
15
+ ENV_SYNAPSE_HOST,
16
+ load_credentials,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class AuthConfig:
22
+ """Authentication configuration."""
23
+
24
+ host: str
25
+ access_token: str
26
+
27
+
28
+ def load_credentials_file() -> dict[str, str]:
29
+ """Load credentials from ~/.synapse/config.json file.
30
+
31
+ Returns:
32
+ Dict with SYNAPSE_HOST and SYNAPSE_ACCESS_TOKEN if found.
33
+ """
34
+ host, token = load_credentials()
35
+ credentials: dict[str, str] = {}
36
+ if host:
37
+ credentials[ENV_SYNAPSE_HOST] = host
38
+ if token:
39
+ credentials[ENV_SYNAPSE_ACCESS_TOKEN] = token
40
+ return credentials
41
+
42
+
43
+ def get_auth_config(
44
+ *,
45
+ host: str | None = None,
46
+ token: str | None = None,
47
+ console: Console | None = None,
48
+ interactive: bool = True,
49
+ ) -> AuthConfig:
50
+ """Get authentication configuration.
51
+
52
+ Priority order:
53
+ 1. CLI options (--host, --token)
54
+ 2. Environment variables (SYNAPSE_HOST, SYNAPSE_ACCESS_TOKEN)
55
+ 3. Config file (~/.synapse/config.json)
56
+ 4. Interactive prompt (if interactive=True)
57
+
58
+ Args:
59
+ host: Host override from CLI.
60
+ token: Token override from CLI.
61
+ console: Rich console for output.
62
+ interactive: Whether to prompt for missing values.
63
+
64
+ Returns:
65
+ AuthConfig with host and access_token.
66
+
67
+ Raises:
68
+ typer.Exit: If authentication cannot be resolved.
69
+ """
70
+ # Load from env/credentials file
71
+ loaded_host, loaded_token = load_credentials()
72
+
73
+ # Resolve host (CLI > loaded > default)
74
+ resolved_host = host or loaded_host or DEFAULT_HOST
75
+
76
+ # Resolve token (CLI > loaded)
77
+ resolved_token = token or loaded_token
78
+
79
+ if not resolved_token and interactive:
80
+ resolved_token = questionary.text(
81
+ 'Enter your Synapse access token:',
82
+ validate=lambda x: len(x) > 0 or 'Token cannot be empty',
83
+ ).ask()
84
+
85
+ if not resolved_token:
86
+ raise typer.Exit(1)
87
+
88
+ if not resolved_token:
89
+ raise typer.BadParameter(
90
+ f'Not authenticated. Run `synapse login` or set {ENV_SYNAPSE_ACCESS_TOKEN} environment variable.'
91
+ )
92
+
93
+ return AuthConfig(host=resolved_host, access_token=resolved_token)
94
+
95
+
96
+ __all__ = [
97
+ 'AuthConfig',
98
+ 'get_auth_config',
99
+ 'load_credentials_file',
100
+ 'ENV_SYNAPSE_HOST',
101
+ 'ENV_SYNAPSE_ACCESS_TOKEN',
102
+ 'DEFAULT_HOST',
103
+ 'CONFIG_FILE',
104
+ ]