synapse-sdk 1.0.0a59__py3-none-any.whl ā 1.0.0a61__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/cli/__init__.py +246 -5
- synapse_sdk/cli/alias/utils.py +1 -1
- synapse_sdk/cli/config.py +339 -0
- synapse_sdk/cli/devtools.py +61 -0
- synapse_sdk/cli/plugin/publish.py +3 -4
- synapse_sdk/clients/agent/__init__.py +7 -2
- synapse_sdk/clients/agent/ray.py +37 -6
- synapse_sdk/clients/backend/__init__.py +5 -9
- synapse_sdk/clients/backend/annotation.py +4 -0
- synapse_sdk/clients/base.py +42 -3
- synapse_sdk/devtools/__init__.py +0 -0
- synapse_sdk/devtools/config.py +94 -0
- synapse_sdk/devtools/docs/.gitignore +20 -0
- synapse_sdk/devtools/docs/README.md +41 -0
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +29 -0
- synapse_sdk/devtools/docs/blog/authors.yml +25 -0
- synapse_sdk/devtools/docs/blog/tags.yml +19 -0
- synapse_sdk/devtools/docs/docusaurus.config.ts +138 -0
- synapse_sdk/devtools/docs/package-lock.json +17455 -0
- synapse_sdk/devtools/docs/package.json +47 -0
- synapse_sdk/devtools/docs/sidebars.ts +36 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- synapse_sdk/devtools/docs/src/css/custom.css +30 -0
- synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
- synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
- synapse_sdk/devtools/docs/static/.nojekyll +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
- synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
- synapse_sdk/devtools/docs/static/img/logo.png +0 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- synapse_sdk/devtools/docs/tsconfig.json +8 -0
- synapse_sdk/devtools/models.py +55 -0
- synapse_sdk/devtools/server.py +829 -0
- synapse_sdk/devtools/web/.gitignore +2 -0
- synapse_sdk/devtools/web/README.md +34 -0
- synapse_sdk/devtools/web/dist/index.html +17 -0
- synapse_sdk/devtools/web/index.html +16 -0
- synapse_sdk/devtools/web/jsconfig.json +15 -0
- synapse_sdk/devtools/web/package-lock.json +2609 -0
- synapse_sdk/devtools/web/package.json +27 -0
- synapse_sdk/devtools/web/pnpm-lock.yaml +1055 -0
- synapse_sdk/devtools/web/src/App.jsx +14 -0
- synapse_sdk/devtools/web/src/App.module.css +33 -0
- synapse_sdk/devtools/web/src/assets/favicon.ico +0 -0
- synapse_sdk/devtools/web/src/components/Breadcrumbs.jsx +42 -0
- synapse_sdk/devtools/web/src/components/Layout.jsx +12 -0
- synapse_sdk/devtools/web/src/components/LogViewer.jsx +266 -0
- synapse_sdk/devtools/web/src/components/MessageViewer.jsx +150 -0
- synapse_sdk/devtools/web/src/components/NavigationSidebar.jsx +137 -0
- synapse_sdk/devtools/web/src/components/ServerStatusBar.jsx +245 -0
- synapse_sdk/devtools/web/src/components/icons.jsx +325 -0
- synapse_sdk/devtools/web/src/index.css +470 -0
- synapse_sdk/devtools/web/src/index.jsx +15 -0
- synapse_sdk/devtools/web/src/logo.svg +1 -0
- synapse_sdk/devtools/web/src/router.jsx +34 -0
- synapse_sdk/devtools/web/src/utils/api.js +425 -0
- synapse_sdk/devtools/web/src/views/ApplicationDetailView.jsx +241 -0
- synapse_sdk/devtools/web/src/views/ApplicationsView.jsx +224 -0
- synapse_sdk/devtools/web/src/views/HomeView.jsx +197 -0
- synapse_sdk/devtools/web/src/views/JobDetailView.jsx +310 -0
- synapse_sdk/devtools/web/src/views/PluginView.jsx +914 -0
- synapse_sdk/devtools/web/vite.config.js +13 -0
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +1 -1
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +15 -3
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +236 -64
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +14 -2
- synapse_sdk/plugins/templates/plugin-config-schema.json +409 -0
- synapse_sdk/plugins/templates/schema.json +484 -0
- synapse_sdk/utils/converters/__init__.py +145 -0
- synapse_sdk/utils/converters/coco/__init__.py +0 -0
- synapse_sdk/utils/converters/coco/from_dm.py +269 -0
- {synapse_sdk-1.0.0a59.dist-info ā synapse_sdk-1.0.0a61.dist-info}/METADATA +9 -22
- {synapse_sdk-1.0.0a59.dist-info ā synapse_sdk-1.0.0a61.dist-info}/RECORD +87 -19
- {synapse_sdk-1.0.0a59.dist-info ā synapse_sdk-1.0.0a61.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0a59.dist-info ā synapse_sdk-1.0.0a61.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a59.dist-info ā synapse_sdk-1.0.0a61.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a59.dist-info ā synapse_sdk-1.0.0a61.dist-info}/top_level.txt +0 -0
synapse_sdk/cli/__init__.py
CHANGED
|
@@ -1,13 +1,254 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
1
3
|
import click
|
|
4
|
+
import inquirer
|
|
5
|
+
import requests
|
|
2
6
|
|
|
3
|
-
from .
|
|
7
|
+
from .config import config
|
|
8
|
+
from .devtools import devtools
|
|
4
9
|
from .plugin import plugin
|
|
5
10
|
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
def clear_screen():
|
|
13
|
+
"""Clear the terminal screen"""
|
|
14
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def check_backend_status():
|
|
18
|
+
"""Check backend connection status and token validity"""
|
|
19
|
+
from synapse_sdk.devtools.config import get_backend_config
|
|
20
|
+
|
|
21
|
+
backend_config = get_backend_config()
|
|
22
|
+
if not backend_config:
|
|
23
|
+
return 'not_configured', 'No backend configured'
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Try an authenticated endpoint to verify token validity
|
|
27
|
+
# Use /users/me/ which requires authentication
|
|
28
|
+
response = requests.get(
|
|
29
|
+
f'{backend_config["host"]}/users/me/',
|
|
30
|
+
headers={'Synapse-Access-Token': f'Token {backend_config["token"]}'},
|
|
31
|
+
timeout=5,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if response.status_code == 200:
|
|
35
|
+
return 'healthy', f'Connected to {backend_config["host"]}'
|
|
36
|
+
elif response.status_code == 401:
|
|
37
|
+
return 'auth_error', 'Invalid token (401)'
|
|
38
|
+
elif response.status_code == 403:
|
|
39
|
+
return 'forbidden', 'Access forbidden (403)'
|
|
40
|
+
elif response.status_code == 404:
|
|
41
|
+
# If /users/me/ doesn't exist, try /health as fallback
|
|
42
|
+
try:
|
|
43
|
+
health_response = requests.get(
|
|
44
|
+
f'{backend_config["host"]}/health',
|
|
45
|
+
headers={'Synapse-Access-Token': f'Token {backend_config["token"]}'},
|
|
46
|
+
timeout=3,
|
|
47
|
+
)
|
|
48
|
+
if health_response.status_code == 200:
|
|
49
|
+
return 'healthy', f'Connected to {backend_config["host"]}'
|
|
50
|
+
elif health_response.status_code == 401:
|
|
51
|
+
return 'auth_error', 'Invalid token (401)'
|
|
52
|
+
elif health_response.status_code == 403:
|
|
53
|
+
return 'forbidden', 'Access forbidden (403)'
|
|
54
|
+
else:
|
|
55
|
+
return 'error', f'HTTP {health_response.status_code}'
|
|
56
|
+
except: # noqa: E722
|
|
57
|
+
return 'error', 'Endpoint not found (404)'
|
|
58
|
+
else:
|
|
59
|
+
return 'error', f'HTTP {response.status_code}'
|
|
60
|
+
|
|
61
|
+
except requests.exceptions.Timeout:
|
|
62
|
+
return 'timeout', 'Connection timeout (>5s)'
|
|
63
|
+
except requests.exceptions.ConnectionError:
|
|
64
|
+
return 'connection_error', 'Connection failed'
|
|
65
|
+
except Exception as e:
|
|
66
|
+
return 'error', f'Connection error: {str(e)}'
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def check_agent_status():
|
|
70
|
+
"""Check agent configuration status"""
|
|
71
|
+
from synapse_sdk.devtools.config import load_devtools_config
|
|
72
|
+
|
|
73
|
+
config = load_devtools_config()
|
|
74
|
+
agent_config = config.get('agent', {})
|
|
75
|
+
|
|
76
|
+
if not agent_config.get('id'):
|
|
77
|
+
return 'not_configured', 'No agent selected'
|
|
78
|
+
|
|
79
|
+
return 'configured', f'{agent_config.get("name", "")} (ID: {agent_config["id"]})'
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def display_connection_status():
|
|
83
|
+
"""Display connection status for backend and agent"""
|
|
84
|
+
click.echo(click.style('Connection Status:', fg='white', bold=True))
|
|
85
|
+
|
|
86
|
+
# Check backend status (async with timeout)
|
|
87
|
+
backend_status, backend_msg = check_backend_status()
|
|
88
|
+
|
|
89
|
+
# Backend status with specific handling for auth errors
|
|
90
|
+
if backend_status == 'healthy':
|
|
91
|
+
click.echo(f'š¢ Backend: {click.style(backend_msg, fg="green")}')
|
|
92
|
+
elif backend_status == 'not_configured':
|
|
93
|
+
click.echo(f'š“ Backend: {click.style(backend_msg, fg="yellow")}')
|
|
94
|
+
elif backend_status in ['auth_error', 'forbidden']:
|
|
95
|
+
click.echo(f'š“ Backend: {click.style(backend_msg, fg="red", bold=True)}')
|
|
96
|
+
else:
|
|
97
|
+
click.echo(f'š“ Backend: {click.style(backend_msg, fg="red")}')
|
|
98
|
+
|
|
99
|
+
# Agent status (config check only, no network call)
|
|
100
|
+
agent_status, agent_msg = check_agent_status()
|
|
101
|
+
if agent_status == 'configured':
|
|
102
|
+
click.echo(f'š¢ Agent: {click.style(agent_msg, fg="green")}')
|
|
103
|
+
else:
|
|
104
|
+
click.echo(f'š“ Agent: {click.style(agent_msg, fg="yellow")}')
|
|
105
|
+
|
|
106
|
+
click.echo() # Empty line for spacing
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def run_devtools(build=False):
|
|
110
|
+
"""Run devtools with default settings"""
|
|
111
|
+
try:
|
|
112
|
+
from synapse_sdk.devtools.server import create_devtools_server
|
|
113
|
+
|
|
114
|
+
if build:
|
|
115
|
+
click.echo('Building frontend assets...')
|
|
116
|
+
build_frontend()
|
|
117
|
+
|
|
118
|
+
click.echo('Starting Synapse Devtools...')
|
|
119
|
+
server = create_devtools_server(host='0.0.0.0', port=8080)
|
|
120
|
+
server.start_server(open_browser=True)
|
|
121
|
+
except ImportError:
|
|
122
|
+
click.echo(
|
|
123
|
+
click.style(
|
|
124
|
+
'Devtools dependencies not installed. Install with: pip install synapse-sdk[dashboard]', fg='red'
|
|
125
|
+
),
|
|
126
|
+
err=True,
|
|
127
|
+
)
|
|
128
|
+
except KeyboardInterrupt:
|
|
129
|
+
click.echo('\nDevtools stopped.')
|
|
130
|
+
except Exception as e:
|
|
131
|
+
click.echo(click.style(f'Failed to start devtools: {e}', fg='red'), err=True)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def build_frontend():
|
|
135
|
+
"""Build the frontend assets"""
|
|
136
|
+
import subprocess
|
|
137
|
+
from pathlib import Path
|
|
138
|
+
|
|
139
|
+
# Find the web directory
|
|
140
|
+
devtools_dir = Path(__file__).parent.parent / 'devtools' / 'web'
|
|
141
|
+
|
|
142
|
+
if not devtools_dir.exists():
|
|
143
|
+
click.echo(click.style(f'Frontend directory not found: {devtools_dir}', fg='red'), err=True)
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Check if npm is available
|
|
148
|
+
subprocess.run(['npm', '--version'], capture_output=True, check=True)
|
|
149
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
150
|
+
click.echo(click.style('npm not found. Please install Node.js and npm.', fg='red'), err=True)
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
# Install dependencies if node_modules doesn't exist
|
|
155
|
+
if not (devtools_dir / 'node_modules').exists():
|
|
156
|
+
click.echo('Installing frontend dependencies...')
|
|
157
|
+
result = subprocess.run(['npm', 'install'], cwd=devtools_dir, capture_output=True, text=True)
|
|
158
|
+
if result.returncode != 0:
|
|
159
|
+
click.echo(click.style(f'npm install failed:\n{result.stderr}', fg='red'), err=True)
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
# Build the frontend
|
|
163
|
+
click.echo('Building frontend...')
|
|
164
|
+
result = subprocess.run(['npm', 'run', 'build'], cwd=devtools_dir, capture_output=True, text=True)
|
|
165
|
+
|
|
166
|
+
if result.returncode != 0:
|
|
167
|
+
click.echo(click.style(f'Frontend build failed:\n{result.stderr}', fg='red'), err=True)
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
click.echo(click.style('Frontend build completed successfully!', fg='green'))
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
click.echo(click.style(f'Build failed: {e}', fg='red'), err=True)
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def run_config():
|
|
179
|
+
"""Run the configuration menu"""
|
|
180
|
+
from .config import interactive_config
|
|
181
|
+
|
|
182
|
+
interactive_config()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@click.group(invoke_without_command=True)
|
|
186
|
+
@click.option('--dev-tools', is_flag=True, help='Start devtools immediately')
|
|
187
|
+
@click.option('--build', is_flag=True, help='Build frontend before starting devtools (only with --dev-tools)')
|
|
188
|
+
@click.pass_context
|
|
189
|
+
def cli(ctx, dev_tools, build):
|
|
190
|
+
"""Synapse SDK - Interactive CLI"""
|
|
191
|
+
|
|
192
|
+
# Handle --dev-tools flag
|
|
193
|
+
if dev_tools:
|
|
194
|
+
if build:
|
|
195
|
+
run_devtools(build=True)
|
|
196
|
+
else:
|
|
197
|
+
run_devtools(build=False)
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
# Handle --build flag without --dev-tools (show warning)
|
|
201
|
+
if build and not dev_tools:
|
|
202
|
+
click.echo(click.style('Warning: --build flag requires --dev-tools flag', fg='yellow'))
|
|
203
|
+
click.echo('Use: synapse --dev-tools --build')
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
if ctx.invoked_subcommand is None:
|
|
207
|
+
while True:
|
|
208
|
+
clear_screen() # Always clear screen at start of main menu loop
|
|
209
|
+
click.echo(click.style('š Synapse SDK', fg='cyan', bold=True))
|
|
210
|
+
click.echo()
|
|
211
|
+
display_connection_status()
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
questions = [
|
|
215
|
+
inquirer.List(
|
|
216
|
+
'choice',
|
|
217
|
+
message='Select an option:',
|
|
218
|
+
choices=[
|
|
219
|
+
('š Run Dev Tools', 'devtools'),
|
|
220
|
+
('āļø Configuration', 'config'),
|
|
221
|
+
('š Plugin Management', 'plugin'),
|
|
222
|
+
('šŖ Exit', 'exit'),
|
|
223
|
+
],
|
|
224
|
+
)
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
answers = inquirer.prompt(questions)
|
|
228
|
+
if not answers or answers['choice'] == 'exit':
|
|
229
|
+
clear_screen()
|
|
230
|
+
click.echo(click.style('š Goodbye!', fg='green'))
|
|
231
|
+
break
|
|
232
|
+
|
|
233
|
+
if answers['choice'] == 'devtools':
|
|
234
|
+
run_devtools()
|
|
235
|
+
break # Exit after devtools finishes
|
|
236
|
+
elif answers['choice'] == 'config':
|
|
237
|
+
run_config()
|
|
238
|
+
# Config menu returned, continue to main menu loop
|
|
239
|
+
elif answers['choice'] == 'plugin':
|
|
240
|
+
click.echo(click.style('š Plugin Management', fg='cyan', bold=True))
|
|
241
|
+
click.echo('For plugin management, use: synapse plugin --help\n')
|
|
242
|
+
click.echo('Press Enter to return to main menu...')
|
|
243
|
+
input()
|
|
244
|
+
# Don't break - continue to main menu loop
|
|
245
|
+
|
|
246
|
+
except (KeyboardInterrupt, EOFError):
|
|
247
|
+
clear_screen()
|
|
248
|
+
click.echo(click.style('š Goodbye!', fg='yellow'))
|
|
249
|
+
break
|
|
10
250
|
|
|
11
251
|
|
|
12
252
|
cli.add_command(plugin)
|
|
13
|
-
cli.add_command(
|
|
253
|
+
cli.add_command(config)
|
|
254
|
+
cli.add_command(devtools)
|
synapse_sdk/cli/alias/utils.py
CHANGED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import inquirer
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from synapse_sdk.devtools.config import (
|
|
8
|
+
clear_backend_config,
|
|
9
|
+
get_backend_config,
|
|
10
|
+
load_devtools_config,
|
|
11
|
+
save_devtools_config,
|
|
12
|
+
set_backend_config,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def clear_screen():
|
|
17
|
+
"""Clear the terminal screen"""
|
|
18
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_agent_config():
|
|
22
|
+
"""Get current agent configuration"""
|
|
23
|
+
config = load_devtools_config()
|
|
24
|
+
return config.get('agent', {})
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_tenant_config():
|
|
28
|
+
"""Get current tenant configuration"""
|
|
29
|
+
config = load_devtools_config()
|
|
30
|
+
return config.get('tenant', {})
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def set_tenant_config(tenant_code: str, tenant_name: str = None):
|
|
34
|
+
"""Set tenant configuration"""
|
|
35
|
+
config = load_devtools_config()
|
|
36
|
+
config['tenant'] = {'code': tenant_code}
|
|
37
|
+
if tenant_name:
|
|
38
|
+
config['tenant']['name'] = tenant_name
|
|
39
|
+
save_devtools_config(config)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def clear_tenant_config():
|
|
43
|
+
"""Clear tenant configuration"""
|
|
44
|
+
config = load_devtools_config()
|
|
45
|
+
if 'tenant' in config:
|
|
46
|
+
del config['tenant']
|
|
47
|
+
save_devtools_config(config)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def set_agent_config(agent_id: str, agent_name: str = None, agent_url: str = None, agent_token: str = None):
|
|
51
|
+
"""Set agent configuration"""
|
|
52
|
+
config = load_devtools_config()
|
|
53
|
+
config['agent'] = {'id': agent_id}
|
|
54
|
+
if agent_name:
|
|
55
|
+
config['agent']['name'] = agent_name
|
|
56
|
+
if agent_url:
|
|
57
|
+
config['agent']['url'] = agent_url
|
|
58
|
+
if agent_token:
|
|
59
|
+
config['agent']['token'] = agent_token
|
|
60
|
+
|
|
61
|
+
save_devtools_config(config)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def clear_agent_config():
|
|
65
|
+
"""Clear agent configuration"""
|
|
66
|
+
config = load_devtools_config()
|
|
67
|
+
if 'agent' in config:
|
|
68
|
+
del config['agent']
|
|
69
|
+
save_devtools_config(config)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def fetch_agents_from_backend():
|
|
73
|
+
"""Fetch available agents from the backend"""
|
|
74
|
+
backend_config = get_backend_config()
|
|
75
|
+
if not backend_config:
|
|
76
|
+
return None, 'No backend configuration found. Configure backend first.'
|
|
77
|
+
|
|
78
|
+
def extract_uuid(string):
|
|
79
|
+
import re
|
|
80
|
+
|
|
81
|
+
"""Extract UUID between 'agents/' and '/node_install_script' from a string."""
|
|
82
|
+
pattern = r'agents/([a-f0-9]{40})/node_install_script'
|
|
83
|
+
match = re.search(pattern, string)
|
|
84
|
+
return match.group(1) if match else None
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
response = requests.get(
|
|
88
|
+
f'{backend_config["host"]}/agents/',
|
|
89
|
+
headers={'Synapse-Access-Token': f'Token {backend_config["token"]}'},
|
|
90
|
+
timeout=10,
|
|
91
|
+
)
|
|
92
|
+
if response.status_code == 200:
|
|
93
|
+
try:
|
|
94
|
+
data = response.json()
|
|
95
|
+
results = data.get('results', [])
|
|
96
|
+
for result in results:
|
|
97
|
+
_node_install_script = result.get('node_install_script')
|
|
98
|
+
if _node_install_script:
|
|
99
|
+
result['token'] = extract_uuid(_node_install_script)
|
|
100
|
+
return results, None
|
|
101
|
+
except ValueError:
|
|
102
|
+
return None, 'Invalid JSON response from server'
|
|
103
|
+
elif response.status_code == 401:
|
|
104
|
+
return None, 'Authentication failed. Check your backend token.'
|
|
105
|
+
elif response.status_code == 403:
|
|
106
|
+
return None, 'Access forbidden. Check your permissions.'
|
|
107
|
+
else:
|
|
108
|
+
return None, f'Failed to fetch agents: HTTP {response.status_code}'
|
|
109
|
+
|
|
110
|
+
except requests.exceptions.Timeout:
|
|
111
|
+
return None, 'Request timeout. Check your network connection.'
|
|
112
|
+
except requests.exceptions.ConnectionError:
|
|
113
|
+
return None, 'Connection failed. Check backend host URL.'
|
|
114
|
+
except Exception as e:
|
|
115
|
+
return None, f'Error fetching agents: {str(e)}'
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def configure_backend():
|
|
119
|
+
"""Interactive backend configuration"""
|
|
120
|
+
backend_config = get_backend_config()
|
|
121
|
+
|
|
122
|
+
if backend_config:
|
|
123
|
+
click.echo(f'Current backend: {backend_config["host"]}')
|
|
124
|
+
click.echo(f'Token: {backend_config["token"][:8]}...')
|
|
125
|
+
click.echo()
|
|
126
|
+
|
|
127
|
+
questions = [
|
|
128
|
+
inquirer.List(
|
|
129
|
+
'action',
|
|
130
|
+
message='What would you like to do?',
|
|
131
|
+
choices=[
|
|
132
|
+
('Configure new backend', 'configure'),
|
|
133
|
+
('Show current configuration', 'show'),
|
|
134
|
+
('Clear configuration', 'clear'),
|
|
135
|
+
('ā Back to main menu', 'back'),
|
|
136
|
+
],
|
|
137
|
+
)
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
answers = inquirer.prompt(questions)
|
|
141
|
+
if not answers or answers['action'] == 'back':
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
if answers['action'] == 'show':
|
|
145
|
+
if backend_config:
|
|
146
|
+
click.echo(click.style('\nā Backend Configuration:', fg='green'))
|
|
147
|
+
click.echo(f' Host: {backend_config["host"]}')
|
|
148
|
+
click.echo(f' Token: {backend_config["token"]}')
|
|
149
|
+
else:
|
|
150
|
+
click.echo(click.style('\nā No backend configuration found.', fg='yellow'))
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
if answers['action'] == 'clear':
|
|
154
|
+
confirm = inquirer.confirm('Are you sure you want to clear the backend configuration?')
|
|
155
|
+
if confirm:
|
|
156
|
+
clear_backend_config()
|
|
157
|
+
click.echo(click.style('\nā Backend configuration cleared.', fg='yellow'))
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if answers['action'] == 'configure':
|
|
161
|
+
config_questions = [
|
|
162
|
+
inquirer.Text(
|
|
163
|
+
'host',
|
|
164
|
+
message='Backend host URL',
|
|
165
|
+
default=backend_config['host'] if backend_config else 'https://api.synapse.sh',
|
|
166
|
+
),
|
|
167
|
+
inquirer.Password('token', message='API token'),
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
config_answers = inquirer.prompt(config_questions)
|
|
171
|
+
if config_answers and config_answers['token']:
|
|
172
|
+
set_backend_config(config_answers['host'], config_answers['token'])
|
|
173
|
+
click.echo(click.style('\nā Backend configuration saved!', fg='green'))
|
|
174
|
+
click.echo(f'Host: {config_answers["host"]}')
|
|
175
|
+
click.echo(f'Token: {config_answers["token"][:8]}...')
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def configure_agent():
|
|
179
|
+
"""Interactive agent configuration"""
|
|
180
|
+
agent_config = get_agent_config()
|
|
181
|
+
|
|
182
|
+
if agent_config:
|
|
183
|
+
click.echo(f'Current agent: {agent_config.get("name", "Unknown")} ({agent_config.get("id")})')
|
|
184
|
+
click.echo()
|
|
185
|
+
|
|
186
|
+
questions = [
|
|
187
|
+
inquirer.List(
|
|
188
|
+
'action',
|
|
189
|
+
message='What would you like to do?',
|
|
190
|
+
choices=[
|
|
191
|
+
('Select agent', 'select'),
|
|
192
|
+
('Set agent ID manually', 'manual'),
|
|
193
|
+
('ā Back to main menu', 'back'),
|
|
194
|
+
],
|
|
195
|
+
)
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
answers = inquirer.prompt(questions)
|
|
199
|
+
if not answers or answers['action'] == 'back':
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
if answers['action'] == 'select':
|
|
203
|
+
click.echo('Fetching available agents...')
|
|
204
|
+
agents, error = fetch_agents_from_backend()
|
|
205
|
+
|
|
206
|
+
if error:
|
|
207
|
+
click.echo(click.style(f'\nError: {error}', fg='red'))
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
if not agents:
|
|
211
|
+
click.echo(click.style('\nā No agents found in current workspace.', fg='yellow'))
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
# Create choices for agent selection
|
|
215
|
+
agent_choices = []
|
|
216
|
+
for agent in agents:
|
|
217
|
+
status_indicator = 'š¢' if agent.get('status', '').lower() == 'connected' else 'š“'
|
|
218
|
+
display_name = f'{status_indicator} {agent["name"]} ({agent["id"]})'
|
|
219
|
+
if agent.get('url'):
|
|
220
|
+
display_name += f' - {agent["url"]}'
|
|
221
|
+
agent_choices.append((display_name, agent))
|
|
222
|
+
|
|
223
|
+
agent_choices.append(('ā Cancel', None))
|
|
224
|
+
|
|
225
|
+
agent_questions = [inquirer.List('selected_agent', message='Select an agent:', choices=agent_choices)]
|
|
226
|
+
|
|
227
|
+
agent_answers = inquirer.prompt(agent_questions)
|
|
228
|
+
if agent_answers and agent_answers['selected_agent']:
|
|
229
|
+
selected = agent_answers['selected_agent']
|
|
230
|
+
set_agent_config(selected['id'], selected.get('name'), selected.get('url'), selected.get('token'))
|
|
231
|
+
click.echo(click.style('\nā Agent configured!', fg='green'))
|
|
232
|
+
click.echo(f'Selected: {selected["name"]} ({selected["id"]})')
|
|
233
|
+
if selected.get('url'):
|
|
234
|
+
click.echo(f'URL: {selected["url"]}')
|
|
235
|
+
|
|
236
|
+
if answers['action'] == 'manual':
|
|
237
|
+
manual_questions = [
|
|
238
|
+
inquirer.Text('agent_id', message='Agent ID', default=agent_config.get('id', '') if agent_config else ''),
|
|
239
|
+
inquirer.Text(
|
|
240
|
+
'agent_url', message='Agent URL', default=agent_config.get('url', '') if agent_config else ''
|
|
241
|
+
),
|
|
242
|
+
inquirer.Text(
|
|
243
|
+
'agent_token', message='Agent Token', default=agent_config.get('token', '') if agent_config else ''
|
|
244
|
+
),
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
manual_answers = inquirer.prompt(manual_questions)
|
|
248
|
+
if manual_answers and manual_answers['agent_id']:
|
|
249
|
+
set_agent_config(
|
|
250
|
+
manual_answers['agent_id'], None, manual_answers.get('agent_url'), manual_answers.get('agent_token')
|
|
251
|
+
)
|
|
252
|
+
click.echo(click.style('\nā Agent configured!', fg='green'))
|
|
253
|
+
click.echo(f'Agent ID: {manual_answers["agent_id"]}')
|
|
254
|
+
if manual_answers.get('agent_url'):
|
|
255
|
+
click.echo(f'Agent URL: {manual_answers["agent_url"]}')
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def show_current_config():
|
|
260
|
+
"""Show all current configuration"""
|
|
261
|
+
backend_config = get_backend_config()
|
|
262
|
+
agent_config = get_agent_config()
|
|
263
|
+
|
|
264
|
+
click.echo(click.style('\nš Current Configuration', fg='cyan', bold=True))
|
|
265
|
+
click.echo('=' * 30)
|
|
266
|
+
|
|
267
|
+
# Backend section
|
|
268
|
+
click.echo(click.style('\nš Backend:', fg='blue', bold=True))
|
|
269
|
+
if backend_config:
|
|
270
|
+
click.echo(f' Host: {backend_config["host"]}')
|
|
271
|
+
click.echo(f' Token: {backend_config["token"][:8]}...')
|
|
272
|
+
click.echo(click.style(' Status: ā Configured', fg='green'))
|
|
273
|
+
else:
|
|
274
|
+
click.echo(click.style(' Status: ā Not configured', fg='red'))
|
|
275
|
+
|
|
276
|
+
# Agent section
|
|
277
|
+
click.echo(click.style('\nš¤ Agent:', fg='blue', bold=True))
|
|
278
|
+
if agent_config:
|
|
279
|
+
click.echo(f' ID: {agent_config.get("id", "Not set")}')
|
|
280
|
+
if 'name' in agent_config:
|
|
281
|
+
click.echo(f' Name: {agent_config["name"]}')
|
|
282
|
+
click.echo(click.style(' Status: ā Configured', fg='green'))
|
|
283
|
+
else:
|
|
284
|
+
click.echo(click.style(' Status: ā Not configured', fg='red'))
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def interactive_config():
|
|
288
|
+
while True:
|
|
289
|
+
clear_screen()
|
|
290
|
+
click.echo(click.style('š§ Configuration', fg='cyan', bold=True))
|
|
291
|
+
click.echo('Configure your Synapse settings\n')
|
|
292
|
+
|
|
293
|
+
questions = [
|
|
294
|
+
inquirer.List(
|
|
295
|
+
'choice',
|
|
296
|
+
message='What would you like to configure?',
|
|
297
|
+
choices=[
|
|
298
|
+
('Synapse Backend Host', 'backend'),
|
|
299
|
+
('Synapse Agent', 'agent'),
|
|
300
|
+
('Show Current Config', 'show'),
|
|
301
|
+
('ā Back to Main Menu', 'exit'),
|
|
302
|
+
],
|
|
303
|
+
)
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
answers = inquirer.prompt(questions)
|
|
308
|
+
if not answers or answers['choice'] == 'exit':
|
|
309
|
+
clear_screen() # Clear screen when exiting config
|
|
310
|
+
break
|
|
311
|
+
|
|
312
|
+
clear_screen()
|
|
313
|
+
|
|
314
|
+
if answers['choice'] == 'backend':
|
|
315
|
+
configure_backend()
|
|
316
|
+
click.echo('\nPress Enter to continue...')
|
|
317
|
+
input()
|
|
318
|
+
elif answers['choice'] == 'agent':
|
|
319
|
+
configure_agent()
|
|
320
|
+
click.echo('\nPress Enter to continue...')
|
|
321
|
+
input()
|
|
322
|
+
elif answers['choice'] == 'show':
|
|
323
|
+
show_current_config()
|
|
324
|
+
click.echo('\nPress Enter to continue...')
|
|
325
|
+
input()
|
|
326
|
+
|
|
327
|
+
except (KeyboardInterrupt, EOFError):
|
|
328
|
+
clear_screen() # Clear screen on interrupt
|
|
329
|
+
break
|
|
330
|
+
except Exception as e:
|
|
331
|
+
click.echo(click.style(f'\nError: {str(e)}', fg='red'))
|
|
332
|
+
click.echo('\nPress Enter to continue...')
|
|
333
|
+
input()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@click.command()
|
|
337
|
+
def config():
|
|
338
|
+
"""Configure Synapse settings interactively"""
|
|
339
|
+
interactive_config()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
from synapse_sdk.i18n import gettext as _
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command()
|
|
10
|
+
@click.option('--host', default=None, help='Host to bind the devtools server')
|
|
11
|
+
@click.option('--port', default=None, type=int, help='Port to bind the devtools server')
|
|
12
|
+
@click.option('--no-browser', is_flag=True, help='Do not open browser automatically')
|
|
13
|
+
@click.option('--debug', is_flag=True, help='Run in debug mode')
|
|
14
|
+
@click.option('--build', is_flag=True, help='Build frontend assets before starting server')
|
|
15
|
+
def devtools(host, port, no_browser, debug, build):
|
|
16
|
+
"""Start the Synapse devtools web interface"""
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from synapse_sdk.devtools.config import get_server_config
|
|
20
|
+
from synapse_sdk.devtools.server import create_devtools_server
|
|
21
|
+
except ImportError:
|
|
22
|
+
click.echo(
|
|
23
|
+
click.style(
|
|
24
|
+
_('Devtools dependencies not installed. Install with: pip install synapse-sdk[devtools]'), fg='red'
|
|
25
|
+
),
|
|
26
|
+
err=True,
|
|
27
|
+
)
|
|
28
|
+
sys.exit(1)
|
|
29
|
+
|
|
30
|
+
if build:
|
|
31
|
+
click.echo('Building frontend assets...')
|
|
32
|
+
from synapse_sdk.cli import build_frontend
|
|
33
|
+
|
|
34
|
+
if not build_frontend():
|
|
35
|
+
click.echo(click.style('Build failed, continuing with existing assets...', fg='yellow'))
|
|
36
|
+
|
|
37
|
+
if debug:
|
|
38
|
+
click.echo(_('Starting devtools in debug mode...'))
|
|
39
|
+
os.environ['UVICORN_LOG_LEVEL'] = 'debug'
|
|
40
|
+
|
|
41
|
+
click.echo('Starting Synapse Devtools...')
|
|
42
|
+
|
|
43
|
+
# Get server configuration from config file
|
|
44
|
+
server_config = get_server_config()
|
|
45
|
+
|
|
46
|
+
# Use CLI arguments if provided, otherwise use config defaults
|
|
47
|
+
final_host = host if host is not None else server_config['host']
|
|
48
|
+
final_port = port if port is not None else server_config['port']
|
|
49
|
+
|
|
50
|
+
# Create and start the devtools server
|
|
51
|
+
# Pass the current working directory as the plugin directory
|
|
52
|
+
plugin_directory = os.getcwd()
|
|
53
|
+
server = create_devtools_server(host=final_host, port=final_port, plugin_directory=plugin_directory)
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
server.start_server(open_browser=not no_browser)
|
|
57
|
+
except KeyboardInterrupt:
|
|
58
|
+
click.echo(_('\nDevtools stopped.'))
|
|
59
|
+
except Exception as e:
|
|
60
|
+
click.echo(click.style(f'Failed to start devtools: {e}', fg='red'), err=True)
|
|
61
|
+
sys.exit(1)
|
|
@@ -10,11 +10,10 @@ from synapse_sdk.plugins.upload import archive
|
|
|
10
10
|
|
|
11
11
|
@click.command()
|
|
12
12
|
@click.option('--host', required=True)
|
|
13
|
-
@click.option('--
|
|
14
|
-
@click.option('--tenant', required=True)
|
|
13
|
+
@click.option('--access_token', required=True)
|
|
15
14
|
@click.option('--debug_modules', default='', envvar='SYNAPSE_DEBUG_MODULES')
|
|
16
15
|
@click.pass_context
|
|
17
|
-
def publish(ctx, host,
|
|
16
|
+
def publish(ctx, host, access_token, debug_modules):
|
|
18
17
|
debug = ctx.obj['DEBUG']
|
|
19
18
|
|
|
20
19
|
plugin_release = PluginRelease()
|
|
@@ -28,7 +27,7 @@ def publish(ctx, host, user_token, tenant, debug_modules):
|
|
|
28
27
|
modules = debug_modules.split(',') if debug_modules else []
|
|
29
28
|
data['meta'] = {'modules': modules}
|
|
30
29
|
|
|
31
|
-
client = BackendClient(host,
|
|
30
|
+
client = BackendClient(host, access_token)
|
|
32
31
|
client.create_plugin_release(data)
|
|
33
32
|
click.secho(
|
|
34
33
|
_('Successfully published "{}" ({}) to synapse backend!').format(plugin_release.name, plugin_release.code),
|