synapse-sdk 1.0.0a98__py3-none-any.whl → 1.0.0b2__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 +139 -84
- synapse_sdk/cli/code_server.py +169 -0
- synapse_sdk/cli/config.py +105 -4
- synapse_sdk/cli/devtools.py +54 -34
- synapse_sdk/clients/base.py +3 -4
- synapse_sdk/devtools/server.py +24 -791
- synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
- synapse_sdk/devtools/streamlit_app/app.py +128 -0
- synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
- synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
- synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
- synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
- synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
- synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
- synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
- synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
- synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
- synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
- synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
- synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
- synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
- synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
- synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
- synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
- synapse_sdk/devtools/streamlit_app.py +10 -0
- synapse_sdk/plugins/categories/upload/actions/upload.py +2 -1
- synapse_sdk/utils/converters/coco/from_dm.py +2 -2
- synapse_sdk/utils/converters/dm/__init__.py +0 -1
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/METADATA +4 -6
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/RECORD +34 -45
- synapse_sdk/devtools/models.py +0 -55
- synapse_sdk/devtools/utils.py +0 -52
- synapse_sdk/devtools/web/.gitignore +0 -2
- synapse_sdk/devtools/web/README.md +0 -34
- synapse_sdk/devtools/web/dist/index.html +0 -17
- synapse_sdk/devtools/web/index.html +0 -16
- synapse_sdk/devtools/web/jsconfig.json +0 -15
- synapse_sdk/devtools/web/package-lock.json +0 -2609
- synapse_sdk/devtools/web/package.json +0 -27
- synapse_sdk/devtools/web/pnpm-lock.yaml +0 -1055
- synapse_sdk/devtools/web/src/App.jsx +0 -14
- synapse_sdk/devtools/web/src/App.module.css +0 -33
- synapse_sdk/devtools/web/src/assets/favicon.ico +0 -0
- synapse_sdk/devtools/web/src/components/Breadcrumbs.jsx +0 -42
- synapse_sdk/devtools/web/src/components/Layout.jsx +0 -12
- synapse_sdk/devtools/web/src/components/LogViewer.jsx +0 -280
- synapse_sdk/devtools/web/src/components/MessageViewer.jsx +0 -150
- synapse_sdk/devtools/web/src/components/NavigationSidebar.jsx +0 -128
- synapse_sdk/devtools/web/src/components/ServerStatusBar.jsx +0 -245
- synapse_sdk/devtools/web/src/components/icons.jsx +0 -325
- synapse_sdk/devtools/web/src/index.css +0 -470
- synapse_sdk/devtools/web/src/index.jsx +0 -15
- synapse_sdk/devtools/web/src/logo.svg +0 -1
- synapse_sdk/devtools/web/src/router.jsx +0 -34
- synapse_sdk/devtools/web/src/utils/api.js +0 -442
- synapse_sdk/devtools/web/src/views/ApplicationDetailView.jsx +0 -241
- synapse_sdk/devtools/web/src/views/ApplicationsView.jsx +0 -224
- synapse_sdk/devtools/web/src/views/HomeView.jsx +0 -197
- synapse_sdk/devtools/web/src/views/JobDetailView.jsx +0 -310
- synapse_sdk/devtools/web/src/views/PluginView.jsx +0 -914
- synapse_sdk/devtools/web/vite.config.js +0 -13
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a98.dist-info → synapse_sdk-1.0.0b2.dist-info}/top_level.txt +0 -0
synapse_sdk/cli/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ import click
|
|
|
4
4
|
import inquirer
|
|
5
5
|
import requests
|
|
6
6
|
|
|
7
|
+
from .code_server import code_server
|
|
7
8
|
from .config import config
|
|
8
9
|
from .devtools import devtools
|
|
9
10
|
from .plugin import plugin
|
|
@@ -108,88 +109,15 @@ def display_connection_status():
|
|
|
108
109
|
|
|
109
110
|
def run_devtools(build=True):
|
|
110
111
|
"""Run devtools with default settings"""
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
from synapse_sdk.devtools.server import create_devtools_server
|
|
114
|
-
from synapse_sdk.devtools.utils import find_available_port, is_port_available
|
|
115
|
-
|
|
116
|
-
if build:
|
|
117
|
-
click.echo('Building...')
|
|
118
|
-
build_frontend()
|
|
119
|
-
|
|
120
|
-
click.echo('Starting Synapse Devtools...')
|
|
121
|
-
|
|
122
|
-
# Get server configuration defaults
|
|
123
|
-
server_config = get_server_config()
|
|
124
|
-
host = server_config['host']
|
|
125
|
-
port = server_config['port']
|
|
126
|
-
|
|
127
|
-
# Check if the port is available, fallback to next available port if not
|
|
128
|
-
if not is_port_available(host, port):
|
|
129
|
-
try:
|
|
130
|
-
fallback_port = find_available_port(host, port + 1)
|
|
131
|
-
click.echo(click.style(f'Port {port} is in use, falling back to port {fallback_port}', fg='yellow'))
|
|
132
|
-
port = fallback_port
|
|
133
|
-
except RuntimeError as e:
|
|
134
|
-
click.echo(click.style(f'Failed to find available port: {e}', fg='red'), err=True)
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
server = create_devtools_server(host=host, port=port)
|
|
138
|
-
server.start_server()
|
|
139
|
-
except ImportError:
|
|
140
|
-
click.echo(
|
|
141
|
-
click.style(
|
|
142
|
-
'Devtools dependencies not installed. Install with: pip install synapse-sdk[dashboard]', fg='red'
|
|
143
|
-
),
|
|
144
|
-
err=True,
|
|
145
|
-
)
|
|
146
|
-
except KeyboardInterrupt:
|
|
147
|
-
click.echo('\nDevtools stopped.')
|
|
148
|
-
except Exception as e:
|
|
149
|
-
click.echo(click.style(f'Failed to start devtools: {e}', fg='red'), err=True)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def build_frontend():
|
|
153
|
-
"""Build the frontend assets"""
|
|
154
|
-
import subprocess
|
|
155
|
-
from pathlib import Path
|
|
156
|
-
|
|
157
|
-
# Find the web directory
|
|
158
|
-
devtools_dir = Path(__file__).parent.parent / 'devtools' / 'web'
|
|
159
|
-
|
|
160
|
-
if not devtools_dir.exists():
|
|
161
|
-
click.echo(click.style(f'Frontend directory not found: {devtools_dir}', fg='red'), err=True)
|
|
162
|
-
return False
|
|
163
|
-
|
|
164
|
-
try:
|
|
165
|
-
# Check if npm is available
|
|
166
|
-
subprocess.run(['npm', '--version'], capture_output=True, check=True)
|
|
167
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
168
|
-
click.echo(click.style('npm not found. Please install Node.js and npm.', fg='red'), err=True)
|
|
169
|
-
return False
|
|
170
|
-
|
|
171
|
-
try:
|
|
172
|
-
# Install dependencies if node_modules doesn't exist
|
|
173
|
-
if not (devtools_dir / 'node_modules').exists():
|
|
174
|
-
click.echo('Installing dependencies...')
|
|
175
|
-
result = subprocess.run(['npm', 'install'], cwd=devtools_dir, capture_output=True, text=True)
|
|
176
|
-
if result.returncode != 0:
|
|
177
|
-
click.echo(click.style(f'npm install failed:\n{result.stderr}', fg='red'), err=True)
|
|
178
|
-
return False
|
|
112
|
+
# Import the devtools command and run it
|
|
113
|
+
from .devtools import devtools
|
|
179
114
|
|
|
180
|
-
|
|
181
|
-
|
|
115
|
+
# Use default settings
|
|
116
|
+
ctx = click.Context(devtools)
|
|
117
|
+
ctx.invoke(devtools, host=None, port=None, debug=False)
|
|
182
118
|
|
|
183
|
-
if result.returncode != 0:
|
|
184
|
-
click.echo(click.style(f'Frontend build failed:\n{result.stderr}', fg='red'), err=True)
|
|
185
|
-
return False
|
|
186
119
|
|
|
187
|
-
|
|
188
|
-
return True
|
|
189
|
-
|
|
190
|
-
except Exception as e:
|
|
191
|
-
click.echo(click.style(f'Build failed: {e}', fg='red'), err=True)
|
|
192
|
-
return False
|
|
120
|
+
# build_frontend function removed - no longer needed with Streamlit
|
|
193
121
|
|
|
194
122
|
|
|
195
123
|
def run_config():
|
|
@@ -199,6 +127,128 @@ def run_config():
|
|
|
199
127
|
interactive_config()
|
|
200
128
|
|
|
201
129
|
|
|
130
|
+
def run_plugin_management():
|
|
131
|
+
"""Run interactive plugin management"""
|
|
132
|
+
while True:
|
|
133
|
+
clear_screen()
|
|
134
|
+
click.echo(click.style('🔌 Plugin Management', fg='cyan', bold=True))
|
|
135
|
+
click.echo('Manage your Synapse plugins\n')
|
|
136
|
+
|
|
137
|
+
questions = [
|
|
138
|
+
inquirer.List(
|
|
139
|
+
'action',
|
|
140
|
+
message='What would you like to do?',
|
|
141
|
+
choices=[
|
|
142
|
+
('Create new plugin', 'create'),
|
|
143
|
+
('Run plugin locally', 'run'),
|
|
144
|
+
('Publish plugin', 'publish'),
|
|
145
|
+
('← Back to main menu', 'back'),
|
|
146
|
+
],
|
|
147
|
+
)
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
answers = inquirer.prompt(questions)
|
|
151
|
+
if not answers or answers['action'] == 'back':
|
|
152
|
+
clear_screen()
|
|
153
|
+
break
|
|
154
|
+
|
|
155
|
+
if answers['action'] == 'create':
|
|
156
|
+
click.echo('\nCreating new plugin...')
|
|
157
|
+
from .plugin.create import create
|
|
158
|
+
|
|
159
|
+
ctx = click.Context(create)
|
|
160
|
+
ctx.invoke(create)
|
|
161
|
+
click.echo('\nPress Enter to continue...')
|
|
162
|
+
input()
|
|
163
|
+
|
|
164
|
+
elif answers['action'] == 'run':
|
|
165
|
+
# Get plugin action and parameters
|
|
166
|
+
run_questions = [
|
|
167
|
+
inquirer.Text('action', message='Plugin action to run'),
|
|
168
|
+
inquirer.Text('params', message='Parameters (JSON format)', default='{}'),
|
|
169
|
+
inquirer.List(
|
|
170
|
+
'run_by',
|
|
171
|
+
message='Run by',
|
|
172
|
+
choices=[
|
|
173
|
+
('Script (local)', 'script'),
|
|
174
|
+
('Agent', 'agent'),
|
|
175
|
+
('Backend', 'backend'),
|
|
176
|
+
],
|
|
177
|
+
default='script',
|
|
178
|
+
),
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
run_answers = inquirer.prompt(run_questions)
|
|
182
|
+
if run_answers:
|
|
183
|
+
click.echo('\nRunning plugin...')
|
|
184
|
+
from .plugin.run import run
|
|
185
|
+
|
|
186
|
+
ctx = click.Context(run)
|
|
187
|
+
ctx.obj = {'DEBUG': False}
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
if run_answers['run_by'] == 'script':
|
|
191
|
+
ctx.invoke(
|
|
192
|
+
run,
|
|
193
|
+
action=run_answers['action'],
|
|
194
|
+
params=run_answers['params'],
|
|
195
|
+
job_id=None,
|
|
196
|
+
direct=False,
|
|
197
|
+
run_by='script',
|
|
198
|
+
agent_host=None,
|
|
199
|
+
agent_token=None,
|
|
200
|
+
host=None,
|
|
201
|
+
agent=None,
|
|
202
|
+
user_token=None,
|
|
203
|
+
tenant=None,
|
|
204
|
+
)
|
|
205
|
+
else:
|
|
206
|
+
click.echo(click.style('\nNote: For agent/backend runs, use the command line:', fg='yellow'))
|
|
207
|
+
cmd = (
|
|
208
|
+
f"synapse plugin run {run_answers['action']} '{run_answers['params']}' "
|
|
209
|
+
f'--run-by {run_answers["run_by"]}'
|
|
210
|
+
)
|
|
211
|
+
click.echo(cmd)
|
|
212
|
+
except Exception as e:
|
|
213
|
+
click.echo(click.style(f'\nError: {str(e)}', fg='red'))
|
|
214
|
+
|
|
215
|
+
click.echo('\nPress Enter to continue...')
|
|
216
|
+
input()
|
|
217
|
+
|
|
218
|
+
elif answers['action'] == 'publish':
|
|
219
|
+
# Get backend configuration
|
|
220
|
+
from synapse_sdk.devtools.config import get_backend_config
|
|
221
|
+
|
|
222
|
+
backend_config = get_backend_config()
|
|
223
|
+
|
|
224
|
+
if not backend_config:
|
|
225
|
+
click.echo(click.style('\n⚠ No backend configured. Please configure backend first.', fg='yellow'))
|
|
226
|
+
click.echo('\nPress Enter to continue...')
|
|
227
|
+
input()
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
publish_questions = [
|
|
231
|
+
inquirer.Confirm('debug', message='Publish in debug mode?', default=True),
|
|
232
|
+
inquirer.Confirm('confirm', message=f'Publish plugin to {backend_config["host"]}?', default=True),
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
publish_answers = inquirer.prompt(publish_questions)
|
|
236
|
+
if publish_answers and publish_answers['confirm']:
|
|
237
|
+
click.echo('\nPublishing plugin...')
|
|
238
|
+
from .plugin.publish import _publish
|
|
239
|
+
|
|
240
|
+
debug_mode = publish_answers.get('debug', True)
|
|
241
|
+
try:
|
|
242
|
+
_publish(backend_config['host'], backend_config['token'], debug=debug_mode)
|
|
243
|
+
except Exception as e:
|
|
244
|
+
click.echo(click.style(f'\nError: {str(e)}', fg='red'))
|
|
245
|
+
click.echo('\nPress Enter to continue...')
|
|
246
|
+
input()
|
|
247
|
+
else:
|
|
248
|
+
click.echo('\nPress Enter to continue...')
|
|
249
|
+
input()
|
|
250
|
+
|
|
251
|
+
|
|
202
252
|
@click.group(invoke_without_command=True)
|
|
203
253
|
@click.option('--dev-tools', is_flag=True, help='Start devtools immediately')
|
|
204
254
|
@click.pass_context
|
|
@@ -215,7 +265,6 @@ def cli(ctx, dev_tools):
|
|
|
215
265
|
clear_screen() # Always clear screen at start of main menu loop
|
|
216
266
|
click.echo(click.style('🚀 Synapse SDK', fg='cyan', bold=True))
|
|
217
267
|
click.echo()
|
|
218
|
-
display_connection_status()
|
|
219
268
|
|
|
220
269
|
try:
|
|
221
270
|
questions = [
|
|
@@ -224,6 +273,7 @@ def cli(ctx, dev_tools):
|
|
|
224
273
|
message='Select an option:',
|
|
225
274
|
choices=[
|
|
226
275
|
('🌐 Run Dev Tools', 'devtools'),
|
|
276
|
+
('💻 Code-Server IDE', 'code_server'),
|
|
227
277
|
('⚙️ Configuration', 'config'),
|
|
228
278
|
('🔌 Plugin Management', 'plugin'),
|
|
229
279
|
('🚪 Exit', 'exit'),
|
|
@@ -240,14 +290,18 @@ def cli(ctx, dev_tools):
|
|
|
240
290
|
if answers['choice'] == 'devtools':
|
|
241
291
|
run_devtools()
|
|
242
292
|
break # Exit after devtools finishes
|
|
293
|
+
elif answers['choice'] == 'code_server':
|
|
294
|
+
from .code_server import code_server
|
|
295
|
+
|
|
296
|
+
ctx.invoke(code_server)
|
|
297
|
+
click.echo('\nPress Enter to return to main menu...')
|
|
298
|
+
input()
|
|
299
|
+
# Continue to main menu loop
|
|
243
300
|
elif answers['choice'] == 'config':
|
|
244
301
|
run_config()
|
|
245
302
|
# Config menu returned, continue to main menu loop
|
|
246
303
|
elif answers['choice'] == 'plugin':
|
|
247
|
-
|
|
248
|
-
click.echo('For plugin management, use: synapse plugin --help\n')
|
|
249
|
-
click.echo('Press Enter to return to main menu...')
|
|
250
|
-
input()
|
|
304
|
+
run_plugin_management()
|
|
251
305
|
# Don't break - continue to main menu loop
|
|
252
306
|
|
|
253
307
|
except (KeyboardInterrupt, EOFError):
|
|
@@ -259,3 +313,4 @@ def cli(ctx, dev_tools):
|
|
|
259
313
|
cli.add_command(plugin)
|
|
260
314
|
cli.add_command(config)
|
|
261
315
|
cli.add_command(devtools)
|
|
316
|
+
cli.add_command(code_server)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Code-server integration for remote plugin development."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from synapse_sdk.cli.config import fetch_agents_from_backend, get_agent_config
|
|
8
|
+
from synapse_sdk.devtools.config import get_backend_config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command()
|
|
12
|
+
@click.option('--agent', help='Agent name or ID')
|
|
13
|
+
@click.option('--open-browser/--no-open-browser', default=True, help='Open in browser')
|
|
14
|
+
def code_server(agent: Optional[str], open_browser: bool):
|
|
15
|
+
"""Connect to web-based code-server on an agent for plugin development."""
|
|
16
|
+
|
|
17
|
+
# Get current agent configuration
|
|
18
|
+
agent_config = get_agent_config()
|
|
19
|
+
backend_config = get_backend_config()
|
|
20
|
+
|
|
21
|
+
if not backend_config:
|
|
22
|
+
click.echo("❌ No backend configured. Run 'synapse config' first.")
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
# If no agent specified, use current agent or let user choose
|
|
26
|
+
if not agent:
|
|
27
|
+
if agent_config and agent_config.get('id'):
|
|
28
|
+
agent = agent_config['id']
|
|
29
|
+
click.echo(f'Using current agent: {agent_config.get("name", agent)}')
|
|
30
|
+
else:
|
|
31
|
+
# List available agents
|
|
32
|
+
agents, error = fetch_agents_from_backend()
|
|
33
|
+
if not agents:
|
|
34
|
+
click.echo('❌ No agents available. Check your backend configuration.')
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
if len(agents) == 1:
|
|
38
|
+
# If only one agent, use it
|
|
39
|
+
agent = agents[0]['id']
|
|
40
|
+
click.echo(f'Using agent: {agents[0].get("name", agent)}')
|
|
41
|
+
else:
|
|
42
|
+
# Let user choose
|
|
43
|
+
click.echo('Available agents:')
|
|
44
|
+
for i, agent_info in enumerate(agents, 1):
|
|
45
|
+
status = agent_info.get('status_display', 'Unknown')
|
|
46
|
+
name = agent_info.get('name', agent_info['id'])
|
|
47
|
+
click.echo(f' {i}. {name} ({status})')
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
choice = click.prompt('Select agent', type=int)
|
|
51
|
+
if 1 <= choice <= len(agents):
|
|
52
|
+
agent = agents[choice - 1]['id']
|
|
53
|
+
else:
|
|
54
|
+
click.echo('❌ Invalid selection')
|
|
55
|
+
return
|
|
56
|
+
except (ValueError, EOFError, KeyboardInterrupt):
|
|
57
|
+
click.echo('\n❌ Cancelled')
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
# Connect to agent and get code-server info
|
|
61
|
+
try:
|
|
62
|
+
# Get agent details from backend to get the agent URL
|
|
63
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
64
|
+
|
|
65
|
+
backend_client = BackendClient(backend_config['host'], access_token=backend_config['token'])
|
|
66
|
+
|
|
67
|
+
# Get agent information
|
|
68
|
+
try:
|
|
69
|
+
agent_info = backend_client._get(f'agents/{agent}/')
|
|
70
|
+
except Exception as e:
|
|
71
|
+
click.echo(f'❌ Failed to get agent information for: {agent}')
|
|
72
|
+
click.echo(f'Error: {e}')
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
if not agent_info or not agent_info.get('url'):
|
|
76
|
+
click.echo(f'❌ Agent {agent} does not have a valid URL')
|
|
77
|
+
click.echo(f'Agent info: {agent_info}')
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# Get the agent token from local configuration
|
|
81
|
+
agent_token = agent_config.get('token')
|
|
82
|
+
if not agent_token:
|
|
83
|
+
click.echo('❌ No agent token found in configuration')
|
|
84
|
+
click.echo("Run 'synapse config' to configure the agent")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
# Create agent client
|
|
88
|
+
from synapse_sdk.clients.agent import AgentClient
|
|
89
|
+
|
|
90
|
+
client = AgentClient(base_url=agent_info['url'], agent_token=agent_token, user_token=backend_config['token'])
|
|
91
|
+
|
|
92
|
+
# Get code-server information
|
|
93
|
+
try:
|
|
94
|
+
info = client.get_code_server_info()
|
|
95
|
+
except AttributeError:
|
|
96
|
+
# Fallback to direct API call if method doesn't exist
|
|
97
|
+
response = client._get('code-server/info/')
|
|
98
|
+
info = response if isinstance(response, dict) else {}
|
|
99
|
+
except Exception as e:
|
|
100
|
+
# Handle other errors
|
|
101
|
+
click.echo(f'❌ Failed to get code-server info: {e}')
|
|
102
|
+
click.echo(f'Agent URL: {agent_info.get("url")}')
|
|
103
|
+
click.echo('\nNote: The agent might not have code-server endpoint implemented yet.')
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
if not info or not info.get('available', False):
|
|
107
|
+
message = info.get('message', 'Code-server is not available') if info else 'Failed to get code-server info'
|
|
108
|
+
click.echo(f'❌ {message}')
|
|
109
|
+
click.echo('\nTo enable code-server, reinstall the agent with code-server support.')
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
# Display connection information
|
|
113
|
+
click.echo('\n✅ Code-Server is available!')
|
|
114
|
+
|
|
115
|
+
workspace = info.get('workspace', '/home/coder/workspace')
|
|
116
|
+
|
|
117
|
+
# Show web browser access
|
|
118
|
+
click.echo('\n🌐 Web-based VS Code:')
|
|
119
|
+
click.echo(f' URL: {info["url"]}')
|
|
120
|
+
password = info.get('password')
|
|
121
|
+
if password:
|
|
122
|
+
click.echo(f' Password: {password}')
|
|
123
|
+
else:
|
|
124
|
+
click.echo(' Password: Not required (passwordless mode)')
|
|
125
|
+
|
|
126
|
+
click.echo(f'\n📁 Workspace: {workspace}')
|
|
127
|
+
|
|
128
|
+
# Optionally open in browser
|
|
129
|
+
if open_browser and info.get('url'):
|
|
130
|
+
import subprocess
|
|
131
|
+
|
|
132
|
+
click.echo('\nAttempting to open in browser...')
|
|
133
|
+
|
|
134
|
+
# Try to open browser, suppressing stderr to avoid xdg-open noise
|
|
135
|
+
try:
|
|
136
|
+
# Use subprocess to suppress xdg-open errors
|
|
137
|
+
result = subprocess.run(['xdg-open', info['url']], capture_output=True, text=True, timeout=2)
|
|
138
|
+
if result.returncode != 0:
|
|
139
|
+
click.echo('⚠️ Could not open browser automatically (no display?)')
|
|
140
|
+
click.echo(f'👉 Please manually open: {info["url"]}')
|
|
141
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
142
|
+
# xdg-open not available or timed out
|
|
143
|
+
click.echo('⚠️ Could not open browser (headless environment)')
|
|
144
|
+
click.echo(f'👉 Please manually open: {info["url"]}')
|
|
145
|
+
except Exception:
|
|
146
|
+
# Fallback for other errors
|
|
147
|
+
click.echo(f'👉 Please manually open: {info["url"]}')
|
|
148
|
+
|
|
149
|
+
# Show additional instructions
|
|
150
|
+
click.echo('\n📝 Quick Start:')
|
|
151
|
+
click.echo('1. Open the URL in your browser')
|
|
152
|
+
click.echo('2. Enter the password if prompted')
|
|
153
|
+
click.echo('3. Start coding in the web-based VS Code!')
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
import traceback
|
|
157
|
+
|
|
158
|
+
click.echo(f'❌ Failed to connect to agent: {e}')
|
|
159
|
+
|
|
160
|
+
# Show more detailed error in debug mode
|
|
161
|
+
if '--debug' in click.get_current_context().params:
|
|
162
|
+
click.echo('\nDebug trace:')
|
|
163
|
+
click.echo(traceback.format_exc())
|
|
164
|
+
|
|
165
|
+
click.echo('\nTroubleshooting:')
|
|
166
|
+
click.echo('1. Check if agent is running and accessible')
|
|
167
|
+
click.echo('2. Verify the agent has code-server support (may need agent update)')
|
|
168
|
+
click.echo('3. Check agent logs for more details')
|
|
169
|
+
click.echo('4. Try running with --no-open-browser to see connection details')
|
synapse_sdk/cli/config.py
CHANGED
|
@@ -18,6 +18,81 @@ def clear_screen():
|
|
|
18
18
|
os.system('cls' if os.name == 'nt' else 'clear')
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
def check_backend_connection(host, token):
|
|
22
|
+
"""Test backend connection with given credentials"""
|
|
23
|
+
try:
|
|
24
|
+
# Try an authenticated endpoint to verify token validity
|
|
25
|
+
response = requests.get(
|
|
26
|
+
f'{host}/users/me/',
|
|
27
|
+
headers={'Synapse-Access-Token': f'Token {token}'},
|
|
28
|
+
timeout=5,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if response.status_code == 200:
|
|
32
|
+
return True, 'Connection successful'
|
|
33
|
+
elif response.status_code == 401:
|
|
34
|
+
return False, 'Invalid token (401)'
|
|
35
|
+
elif response.status_code == 403:
|
|
36
|
+
return False, 'Access forbidden (403)'
|
|
37
|
+
elif response.status_code == 404:
|
|
38
|
+
# If /users/me/ doesn't exist, try /health as fallback
|
|
39
|
+
try:
|
|
40
|
+
health_response = requests.get(
|
|
41
|
+
f'{host}/health',
|
|
42
|
+
headers={'Synapse-Access-Token': f'Token {token}'},
|
|
43
|
+
timeout=3,
|
|
44
|
+
)
|
|
45
|
+
if health_response.status_code == 200:
|
|
46
|
+
return True, 'Connection successful'
|
|
47
|
+
elif health_response.status_code == 401:
|
|
48
|
+
return False, 'Invalid token (401)'
|
|
49
|
+
elif health_response.status_code == 403:
|
|
50
|
+
return False, 'Access forbidden (403)'
|
|
51
|
+
else:
|
|
52
|
+
return False, f'HTTP {health_response.status_code}'
|
|
53
|
+
except Exception:
|
|
54
|
+
return False, 'Endpoint not found (404)'
|
|
55
|
+
else:
|
|
56
|
+
return False, f'HTTP {response.status_code}'
|
|
57
|
+
|
|
58
|
+
except requests.exceptions.Timeout:
|
|
59
|
+
return False, 'Connection timeout (>5s)'
|
|
60
|
+
except requests.exceptions.ConnectionError:
|
|
61
|
+
return False, 'Connection failed'
|
|
62
|
+
except Exception as e:
|
|
63
|
+
return False, f'Connection error: {str(e)}'
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def check_agent_connection(agent_url, agent_token):
|
|
67
|
+
"""Test agent connection with given credentials"""
|
|
68
|
+
if not agent_url or not agent_token:
|
|
69
|
+
return True, 'Agent configured (no URL/token to test)'
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
# Try to connect to the agent
|
|
73
|
+
response = requests.get(
|
|
74
|
+
f'{agent_url}/health/',
|
|
75
|
+
headers={'Authorization': agent_token},
|
|
76
|
+
timeout=5,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if response.status_code == 200:
|
|
80
|
+
return True, 'Agent connection successful'
|
|
81
|
+
elif response.status_code == 401:
|
|
82
|
+
return False, 'Invalid agent token (401)'
|
|
83
|
+
elif response.status_code == 403:
|
|
84
|
+
return False, 'Agent access forbidden (403)'
|
|
85
|
+
else:
|
|
86
|
+
return False, f'Agent HTTP {response.status_code}'
|
|
87
|
+
|
|
88
|
+
except requests.exceptions.Timeout:
|
|
89
|
+
return False, 'Agent connection timeout (>5s)'
|
|
90
|
+
except requests.exceptions.ConnectionError:
|
|
91
|
+
return False, 'Agent connection failed'
|
|
92
|
+
except Exception as e:
|
|
93
|
+
return False, f'Agent error: {str(e)}'
|
|
94
|
+
|
|
95
|
+
|
|
21
96
|
def get_agent_config():
|
|
22
97
|
"""Get current agent configuration"""
|
|
23
98
|
config = load_devtools_config()
|
|
@@ -121,7 +196,7 @@ def configure_backend():
|
|
|
121
196
|
|
|
122
197
|
if backend_config:
|
|
123
198
|
click.echo(f'Current backend: {backend_config["host"]}')
|
|
124
|
-
click.echo(f'Token: {backend_config["token"]
|
|
199
|
+
click.echo(f'Token: {backend_config["token"]}')
|
|
125
200
|
click.echo()
|
|
126
201
|
|
|
127
202
|
questions = [
|
|
@@ -164,7 +239,7 @@ def configure_backend():
|
|
|
164
239
|
message='Backend host URL',
|
|
165
240
|
default=backend_config['host'] if backend_config else 'https://api.synapse.sh',
|
|
166
241
|
),
|
|
167
|
-
inquirer.
|
|
242
|
+
inquirer.Text('token', default=backend_config['token'] if backend_config else '', message='API token'),
|
|
168
243
|
]
|
|
169
244
|
|
|
170
245
|
config_answers = inquirer.prompt(config_questions)
|
|
@@ -172,7 +247,15 @@ def configure_backend():
|
|
|
172
247
|
set_backend_config(config_answers['host'], config_answers['token'])
|
|
173
248
|
click.echo(click.style('\n✓ Backend configuration saved!', fg='green'))
|
|
174
249
|
click.echo(f'Host: {config_answers["host"]}')
|
|
175
|
-
click.echo(f'Token: {config_answers["token"]
|
|
250
|
+
click.echo(f'Token: {config_answers["token"]}')
|
|
251
|
+
|
|
252
|
+
# Test the connection
|
|
253
|
+
click.echo('\nTesting connection...')
|
|
254
|
+
success, message = check_backend_connection(config_answers['host'], config_answers['token'])
|
|
255
|
+
if success:
|
|
256
|
+
click.echo(click.style(f'🟢 {message}', fg='green'))
|
|
257
|
+
else:
|
|
258
|
+
click.echo(click.style(f'🔴 {message}', fg='red'))
|
|
176
259
|
|
|
177
260
|
|
|
178
261
|
def configure_agent():
|
|
@@ -233,6 +316,15 @@ def configure_agent():
|
|
|
233
316
|
if selected.get('url'):
|
|
234
317
|
click.echo(f'URL: {selected["url"]}')
|
|
235
318
|
|
|
319
|
+
# Test the agent connection if URL and token are provided
|
|
320
|
+
if selected.get('url') and selected.get('token'):
|
|
321
|
+
click.echo('\nTesting agent connection...')
|
|
322
|
+
success, message = check_agent_connection(selected['url'], selected['token'])
|
|
323
|
+
if success:
|
|
324
|
+
click.echo(click.style(f'🟢 {message}', fg='green'))
|
|
325
|
+
else:
|
|
326
|
+
click.echo(click.style(f'🔴 {message}', fg='red'))
|
|
327
|
+
|
|
236
328
|
if answers['action'] == 'manual':
|
|
237
329
|
manual_questions = [
|
|
238
330
|
inquirer.Text('agent_id', message='Agent ID', default=agent_config.get('id', '') if agent_config else ''),
|
|
@@ -253,6 +345,15 @@ def configure_agent():
|
|
|
253
345
|
click.echo(f'Agent ID: {manual_answers["agent_id"]}')
|
|
254
346
|
if manual_answers.get('agent_url'):
|
|
255
347
|
click.echo(f'Agent URL: {manual_answers["agent_url"]}')
|
|
348
|
+
|
|
349
|
+
# Test the agent connection if URL and token are provided
|
|
350
|
+
if manual_answers.get('agent_url') and manual_answers.get('agent_token'):
|
|
351
|
+
click.echo('\nTesting agent connection...')
|
|
352
|
+
success, message = check_agent_connection(manual_answers['agent_url'], manual_answers['agent_token'])
|
|
353
|
+
if success:
|
|
354
|
+
click.echo(click.style(f'🟢 {message}', fg='green'))
|
|
355
|
+
else:
|
|
356
|
+
click.echo(click.style(f'🔴 {message}', fg='red'))
|
|
256
357
|
return
|
|
257
358
|
|
|
258
359
|
|
|
@@ -268,7 +369,7 @@ def show_current_config():
|
|
|
268
369
|
click.echo(click.style('\n🔗 Backend:', fg='blue', bold=True))
|
|
269
370
|
if backend_config:
|
|
270
371
|
click.echo(f' Host: {backend_config["host"]}')
|
|
271
|
-
click.echo(f' Token: {backend_config["token"]
|
|
372
|
+
click.echo(f' Token: {backend_config["token"]}')
|
|
272
373
|
click.echo(click.style(' Status: ✓ Configured', fg='green'))
|
|
273
374
|
else:
|
|
274
375
|
click.echo(click.style(' Status: ✗ Not configured', fg='red'))
|