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
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']
|
synapse_sdk/cli/__init__.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
"""Synapse SDK CLI.
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
+
]
|
synapse_sdk/cli/auth.py
ADDED
|
@@ -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
|
+
]
|