orrin-cli 0.3.0b2__tar.gz → 0.3.0b4__tar.gz
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.
- orrin_cli-0.3.0b4/PKG-INFO +16 -0
- orrin_cli-0.3.0b4/README.md +5 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin/cli.py +508 -8
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin/default_websocket_handler.py +22 -6
- orrin_cli-0.3.0b4/orrin_cli.egg-info/PKG-INFO +16 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/pyproject.toml +1 -1
- orrin_cli-0.3.0b2/PKG-INFO +0 -16
- orrin_cli-0.3.0b2/README.md +0 -5
- orrin_cli-0.3.0b2/orrin_cli.egg-info/PKG-INFO +0 -16
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/LICENSE +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin/Spinner.py +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin/__init__.py +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin_cli.egg-info/SOURCES.txt +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin_cli.egg-info/dependency_links.txt +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin_cli.egg-info/entry_points.txt +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin_cli.egg-info/requires.txt +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/orrin_cli.egg-info/top_level.txt +0 -0
- {orrin_cli-0.3.0b2 → orrin_cli-0.3.0b4}/setup.cfg +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orrin-cli
|
|
3
|
+
Version: 0.3.0b4
|
|
4
|
+
Summary: Orrin CLI
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: typer[all]>=0.12.0
|
|
9
|
+
Requires-Dist: click<8.2
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# Orrin CLI v0.3.0b3
|
|
13
|
+
|
|
14
|
+
## What's New
|
|
15
|
+
|
|
16
|
+
In v0.3.0b4, we introduce support for creating "mounts" for websocket connectivity with our newly released Exegesis Specification for Interactivity.
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import webbrowser
|
|
2
|
-
from click import Choice
|
|
3
|
-
import typer
|
|
4
|
-
import requests
|
|
2
|
+
from click import Choice # type: ignore
|
|
3
|
+
import typer # type: ignore
|
|
4
|
+
import requests # type: ignore
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from platformdirs import user_config_dir
|
|
9
|
-
import yaml, tomli, toml
|
|
8
|
+
from platformdirs import user_config_dir # type: ignore
|
|
9
|
+
import yaml, tomli, toml # type: ignore
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from orrin.Spinner import Spinner
|
|
12
|
-
import websockets
|
|
12
|
+
import websockets # type: ignore
|
|
13
13
|
import asyncio
|
|
14
|
+
import importlib.util
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from urllib.parse import urlparse
|
|
18
|
+
import ipaddress
|
|
14
19
|
|
|
15
20
|
app = typer.Typer(help="""
|
|
16
21
|
Welcome to Orrin CLI v0.1.9!\n\n
|
|
@@ -33,6 +38,9 @@ app.add_typer(projects, name='projects')
|
|
|
33
38
|
sessions = typer.Typer(help='Terminal Sessions enabling projects to be created in your terminal env from iOS and Web app')
|
|
34
39
|
app.add_typer(sessions, name='sessions')
|
|
35
40
|
|
|
41
|
+
especi = typer.Typer(help='Exegesis Specification for Interactivity (ESPECI) configurations')
|
|
42
|
+
app.add_typer(especi, name='especi')
|
|
43
|
+
|
|
36
44
|
API_BASE = "https://stellr-company.com"
|
|
37
45
|
|
|
38
46
|
APP_NAME = "orrin"
|
|
@@ -42,6 +50,7 @@ config_dir = Path(user_config_dir(APP_NAME, APP_AUTHOR))
|
|
|
42
50
|
config_dir.mkdir(parents=True, exist_ok=True)
|
|
43
51
|
|
|
44
52
|
config_file = config_dir / "config.json"
|
|
53
|
+
print(config_file)
|
|
45
54
|
|
|
46
55
|
# ---- Configuration based commands/helper functions ----
|
|
47
56
|
|
|
@@ -1196,7 +1205,276 @@ def start_default_handler():
|
|
|
1196
1205
|
typer.echo('\nValue must be non-zero, or within bounds.')
|
|
1197
1206
|
return
|
|
1198
1207
|
|
|
1199
|
-
|
|
1208
|
+
try:
|
|
1209
|
+
asyncio.run(establish_default_handler(config['terminal_sessions'][session_to_start]['session_id'], config['api_key'], refresh))
|
|
1210
|
+
except KeyboardInterrupt as _:
|
|
1211
|
+
typer.echo('Exited handler connection. Connection closed.')
|
|
1212
|
+
|
|
1213
|
+
# Volantarily call `refresh` to ensure sessions are updated
|
|
1214
|
+
refresh()
|
|
1215
|
+
except Exception as e:
|
|
1216
|
+
typer.echo(f'Exception: {str(e)}')
|
|
1217
|
+
|
|
1218
|
+
def load_handler_module(handler_path: str | Path):
|
|
1219
|
+
"""Load a handler module with proper support for relative imports."""
|
|
1220
|
+
handler_path = Path(handler_path).resolve()
|
|
1221
|
+
|
|
1222
|
+
if not handler_path.is_file():
|
|
1223
|
+
typer.echo(f"\n❌ Could not load file: {handler_path}")
|
|
1224
|
+
raise typer.Exit(1)
|
|
1225
|
+
|
|
1226
|
+
module_name = handler_path.stem
|
|
1227
|
+
module_dir = handler_path.parent
|
|
1228
|
+
|
|
1229
|
+
# Create module spec
|
|
1230
|
+
spec = importlib.util.spec_from_file_location(module_name, handler_path)
|
|
1231
|
+
if spec is None:
|
|
1232
|
+
typer.echo(f"\n❌ Could not create spec for: {handler_path}")
|
|
1233
|
+
raise typer.Exit(1)
|
|
1234
|
+
|
|
1235
|
+
# Create module
|
|
1236
|
+
module = importlib.util.module_from_spec(spec)
|
|
1237
|
+
|
|
1238
|
+
# === Critical for relative imports (especially 'from . import TSHandler') ===
|
|
1239
|
+
# Set the package name so relative imports work
|
|
1240
|
+
try:
|
|
1241
|
+
# If the file is inside the orrinsdk package, use proper package name
|
|
1242
|
+
if "orrinsdk" in str(handler_path):
|
|
1243
|
+
module.__package__ = "orrinsdk"
|
|
1244
|
+
else:
|
|
1245
|
+
# Fallback
|
|
1246
|
+
module.__package__ = module_dir.name
|
|
1247
|
+
except:
|
|
1248
|
+
module.__package__ = module_name
|
|
1249
|
+
|
|
1250
|
+
# Register and execute
|
|
1251
|
+
sys.modules[spec.name] = module
|
|
1252
|
+
spec.loader.exec_module(module)
|
|
1253
|
+
|
|
1254
|
+
return module
|
|
1255
|
+
|
|
1256
|
+
@sessions.command(help='Mount handler to a terminal session')
|
|
1257
|
+
def mount_handler(
|
|
1258
|
+
handler_file: str,
|
|
1259
|
+
run: bool = typer.Option(False, "--run", "-r", help="Run the handler immediately after mounting")
|
|
1260
|
+
):
|
|
1261
|
+
"""Mount a custom handler to a terminal session."""
|
|
1262
|
+
|
|
1263
|
+
# === Validation ===
|
|
1264
|
+
if not os.path.isfile(handler_file):
|
|
1265
|
+
typer.echo("\n❌ " + typer.style("File not found:", fg=typer.colors.RED) + f" {handler_file}")
|
|
1266
|
+
raise typer.Exit(1)
|
|
1267
|
+
|
|
1268
|
+
config = load_config()
|
|
1269
|
+
|
|
1270
|
+
if config is None or "api_key" not in config:
|
|
1271
|
+
typer.echo("\n⚠️ " + typer.style("Developer API not configured.", fg=typer.colors.YELLOW))
|
|
1272
|
+
typer.echo(" Run: " + typer.style("orrin config configure-dev", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1273
|
+
raise typer.Exit(1)
|
|
1274
|
+
|
|
1275
|
+
if not config.get("terminal_sessions"):
|
|
1276
|
+
typer.echo("\n⚠️ " + typer.style("No terminal sessions found.", fg=typer.colors.YELLOW))
|
|
1277
|
+
typer.echo(" Create one with: " + typer.style("orrin sessions create", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1278
|
+
raise typer.Exit(1)
|
|
1279
|
+
|
|
1280
|
+
# === Show available sessions ===
|
|
1281
|
+
typer.echo("\n" + typer.style("Available Terminal Sessions:", fg=typer.colors.BLUE, bold=True))
|
|
1282
|
+
for i, session in enumerate(config["terminal_sessions"]):
|
|
1283
|
+
typer.echo(f" {i+1}. {session['session_name']} (ID: {session['session_id']})")
|
|
1284
|
+
|
|
1285
|
+
# === User selection ===
|
|
1286
|
+
try:
|
|
1287
|
+
selection = typer.prompt(
|
|
1288
|
+
"\nSelect session number",
|
|
1289
|
+
type=int,
|
|
1290
|
+
default=1
|
|
1291
|
+
)
|
|
1292
|
+
session_idx = selection - 1
|
|
1293
|
+
|
|
1294
|
+
if session_idx < 0 or session_idx >= len(config["terminal_sessions"]):
|
|
1295
|
+
typer.echo("\n❌ " + typer.style("Invalid selection.", fg=typer.colors.RED))
|
|
1296
|
+
raise typer.Exit(1)
|
|
1297
|
+
|
|
1298
|
+
except Exception:
|
|
1299
|
+
typer.echo("\n❌ " + typer.style("Invalid input.", fg=typer.colors.RED))
|
|
1300
|
+
raise typer.Exit(1)
|
|
1301
|
+
|
|
1302
|
+
selected_session = config["terminal_sessions"][session_idx]
|
|
1303
|
+
|
|
1304
|
+
# === Load and validate handler ===
|
|
1305
|
+
try:
|
|
1306
|
+
module = load_handler_module(handler_file) # ← Recommended to extract this
|
|
1307
|
+
if not hasattr(module, "handler_entry"):
|
|
1308
|
+
raise AttributeError("Function 'handler_entry' not found")
|
|
1309
|
+
handler_func = getattr(module, "handler_entry")
|
|
1310
|
+
except Exception as e:
|
|
1311
|
+
typer.echo("\n❌ " + typer.style(f"Failed to load handler: {e}", fg=typer.colors.RED))
|
|
1312
|
+
raise typer.Exit(1)
|
|
1313
|
+
|
|
1314
|
+
# === Check for existing mounted handler ===
|
|
1315
|
+
if "mounted_handlers" in config:
|
|
1316
|
+
for mh in config["mounted_handlers"]:
|
|
1317
|
+
if mh.get("terminal_session_id") == selected_session["session_id"]:
|
|
1318
|
+
typer.echo("\n⚠️ " + typer.style("This session already has a mounted handler.", fg=typer.colors.YELLOW))
|
|
1319
|
+
typer.echo(f" Current handler: {mh['path']}")
|
|
1320
|
+
typer.echo(" Unmount it first using: " + typer.style("orrin sessions unmount-handler", fg=typer.colors.BRIGHT_YELLOW))
|
|
1321
|
+
raise typer.Exit(1)
|
|
1322
|
+
|
|
1323
|
+
# === Run handler if requested ===
|
|
1324
|
+
if run:
|
|
1325
|
+
typer.echo(f"\n▶️ Running handler {typer.style(Path(handler_file).name, fg=typer.colors.BRIGHT_GREEN)}...")
|
|
1326
|
+
try:
|
|
1327
|
+
handler_func(terminal_session_id=selected_session["session_id"])
|
|
1328
|
+
typer.echo("✅ Handler executed successfully.\n")
|
|
1329
|
+
except Exception as e:
|
|
1330
|
+
typer.echo("⚠️ " + typer.style(f"Handler execution failed: {e}", fg=typer.colors.RED))
|
|
1331
|
+
|
|
1332
|
+
# === Save mounted handler ===
|
|
1333
|
+
if "mounted_handlers" not in config:
|
|
1334
|
+
config["mounted_handlers"] = []
|
|
1335
|
+
|
|
1336
|
+
config["mounted_handlers"].append({
|
|
1337
|
+
"path": str(handler_file),
|
|
1338
|
+
"terminal_session_id": selected_session["session_id"]
|
|
1339
|
+
})
|
|
1340
|
+
|
|
1341
|
+
write_config(config)
|
|
1342
|
+
|
|
1343
|
+
# === Final success message ===
|
|
1344
|
+
typer.echo("\n" + typer.style("✅ Successfully mounted handler!", fg=typer.colors.GREEN, bold=True))
|
|
1345
|
+
typer.echo(f" Handler : {typer.style(Path(handler_file).name, fg=typer.colors.BRIGHT_GREEN)}")
|
|
1346
|
+
typer.echo(f" Session : {typer.style(selected_session['session_name'], bold=True)}")
|
|
1347
|
+
typer.echo(f" Session ID : {selected_session['session_id']}")
|
|
1348
|
+
|
|
1349
|
+
@sessions.command(help='Unmount a custom handler from a terminal session')
|
|
1350
|
+
def unmount_handler():
|
|
1351
|
+
config = load_config()
|
|
1352
|
+
|
|
1353
|
+
if config is None or "api_key" not in config:
|
|
1354
|
+
typer.echo("\n⚠️ " + typer.style("Developer API not configured.", fg=typer.colors.YELLOW))
|
|
1355
|
+
typer.echo(" Run: " + typer.style("orrin config configure-dev", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1356
|
+
raise typer.Exit(1)
|
|
1357
|
+
|
|
1358
|
+
if not config.get("terminal_sessions"):
|
|
1359
|
+
typer.echo("\n⚠️ " + typer.style("No terminal sessions found.", fg=typer.colors.YELLOW))
|
|
1360
|
+
typer.echo(" Create one with: " + typer.style("orrin sessions create", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1361
|
+
raise typer.Exit(1)
|
|
1362
|
+
|
|
1363
|
+
if not config.get('mounted_handlers'):
|
|
1364
|
+
typer.echo("\n⚠️ " + typer.style("No mounted handlers.", fg=typer.colors.YELLOW))
|
|
1365
|
+
typer.echo(" Mount a new handler with: " + typer.style("orrin sessions mount-handler", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1366
|
+
raise typer.Exit(1)
|
|
1367
|
+
|
|
1368
|
+
# Get terminal sessions with custom handlers mounted
|
|
1369
|
+
sessions_with_mh = []
|
|
1370
|
+
for mh in config.get('mounted_handlers'):
|
|
1371
|
+
sessions_with_mh.append(mh['terminal_session_id'])
|
|
1372
|
+
|
|
1373
|
+
# === Show available sessions ===
|
|
1374
|
+
typer.echo("\n" + typer.style("Available Terminal Sessions:", fg=typer.colors.BLUE, bold=True))
|
|
1375
|
+
for i, sessionID in enumerate(sessions_with_mh):
|
|
1376
|
+
typer.echo(f" {i+1}. {sessionID}")
|
|
1377
|
+
|
|
1378
|
+
# === User selection ===
|
|
1379
|
+
try:
|
|
1380
|
+
selection = typer.prompt(
|
|
1381
|
+
"\nSelect session number",
|
|
1382
|
+
type=int,
|
|
1383
|
+
default=1
|
|
1384
|
+
)
|
|
1385
|
+
session_idx = selection - 1
|
|
1386
|
+
|
|
1387
|
+
if session_idx < 0 or session_idx >= len(sessions_with_mh):
|
|
1388
|
+
typer.echo("\n❌ " + typer.style("Invalid selection.", fg=typer.colors.RED))
|
|
1389
|
+
raise typer.Exit(1)
|
|
1390
|
+
except Exception as e:
|
|
1391
|
+
typer.echo("\n❌ " + typer.style(f"Failed to load handler: {e}", fg=typer.colors.RED))
|
|
1392
|
+
raise typer.Exit(1)
|
|
1393
|
+
|
|
1394
|
+
for i in range(len(config.get('mounted_handlers'))):
|
|
1395
|
+
if config.get('mounted_handlers')[i]['terminal_session_id'] == sessions_with_mh[session_idx]:
|
|
1396
|
+
del config.get('mounted_handlers')[i]
|
|
1397
|
+
break
|
|
1398
|
+
|
|
1399
|
+
if config['mounted_handlers'] == []:
|
|
1400
|
+
del config['mounted_handlers']
|
|
1401
|
+
|
|
1402
|
+
write_config(config)
|
|
1403
|
+
typer.echo("✅ Handler unmounted successfully.\n")
|
|
1404
|
+
|
|
1405
|
+
@sessions.command(help='Run a terminal sessions custom handler')
|
|
1406
|
+
def run_handler():
|
|
1407
|
+
config = load_config()
|
|
1408
|
+
|
|
1409
|
+
if config is None or "api_key" not in config:
|
|
1410
|
+
typer.echo("\n⚠️ " + typer.style("Developer API not configured.", fg=typer.colors.YELLOW))
|
|
1411
|
+
typer.echo(" Run: " + typer.style("orrin config configure-dev", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1412
|
+
raise typer.Exit(1)
|
|
1413
|
+
|
|
1414
|
+
if not config.get("terminal_sessions"):
|
|
1415
|
+
typer.echo("\n⚠️ " + typer.style("No terminal sessions found.", fg=typer.colors.YELLOW))
|
|
1416
|
+
typer.echo(" Create one with: " + typer.style("orrin sessions create", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1417
|
+
raise typer.Exit(1)
|
|
1418
|
+
|
|
1419
|
+
if not config.get('mounted_handlers'):
|
|
1420
|
+
typer.echo("\n⚠️ " + typer.style("No mounted handlers.", fg=typer.colors.YELLOW))
|
|
1421
|
+
typer.echo(" Mount a new handler with: " + typer.style("orrin sessions mount-handler", fg=typer.colors.BRIGHT_YELLOW, bold=True))
|
|
1422
|
+
raise typer.Exit(1)
|
|
1423
|
+
|
|
1424
|
+
# Get terminal sessions with custom handlers mounted
|
|
1425
|
+
sessions_with_mh = []
|
|
1426
|
+
for mh in config.get('mounted_handlers'):
|
|
1427
|
+
sessions_with_mh.append(mh['terminal_session_id'])
|
|
1428
|
+
|
|
1429
|
+
# === Show available sessions ===
|
|
1430
|
+
typer.echo("\n" + typer.style("Available Terminal Sessions:", fg=typer.colors.BLUE, bold=True))
|
|
1431
|
+
for i, sessionID in enumerate(sessions_with_mh):
|
|
1432
|
+
typer.echo(f" {i+1}. {sessionID}")
|
|
1433
|
+
|
|
1434
|
+
# === User selection ===
|
|
1435
|
+
try:
|
|
1436
|
+
selection = typer.prompt(
|
|
1437
|
+
"\nSelect session number",
|
|
1438
|
+
type=int,
|
|
1439
|
+
default=1
|
|
1440
|
+
)
|
|
1441
|
+
session_idx = selection - 1
|
|
1442
|
+
|
|
1443
|
+
if session_idx < 0 or session_idx >= len(sessions_with_mh):
|
|
1444
|
+
typer.echo("\n❌ " + typer.style("Invalid selection.", fg=typer.colors.RED))
|
|
1445
|
+
raise typer.Exit(1)
|
|
1446
|
+
except Exception as e:
|
|
1447
|
+
typer.echo("\n❌ " + typer.style(f"Failed to load handler: {e}", fg=typer.colors.RED))
|
|
1448
|
+
raise typer.Exit(1)
|
|
1449
|
+
|
|
1450
|
+
# === Load and validate handler ===
|
|
1451
|
+
try:
|
|
1452
|
+
handler_file = next(
|
|
1453
|
+
(
|
|
1454
|
+
mh['path']
|
|
1455
|
+
for mh in config.get('mounted_handlers')
|
|
1456
|
+
if mh['terminal_session_id'] == sessions_with_mh[session_idx]
|
|
1457
|
+
),
|
|
1458
|
+
None
|
|
1459
|
+
)
|
|
1460
|
+
|
|
1461
|
+
if handler_file is None:
|
|
1462
|
+
raise Exception('No mounted handler found for selected terminal session')
|
|
1463
|
+
|
|
1464
|
+
module = load_handler_module(handler_file) # ← Recommended to extract this
|
|
1465
|
+
if not hasattr(module, "handler_entry"):
|
|
1466
|
+
raise AttributeError("Function 'handler_entry' not found")
|
|
1467
|
+
handler_func = getattr(module, "handler_entry")
|
|
1468
|
+
except Exception as e:
|
|
1469
|
+
typer.echo("\n❌ " + typer.style(f"Failed to load handler: {e}", fg=typer.colors.RED))
|
|
1470
|
+
raise typer.Exit(1)
|
|
1471
|
+
|
|
1472
|
+
typer.echo(f"\n▶️ Running handler {typer.style(Path(handler_file).name, fg=typer.colors.BRIGHT_GREEN)}...")
|
|
1473
|
+
try:
|
|
1474
|
+
handler_func(terminal_session_id=sessions_with_mh[session_idx])
|
|
1475
|
+
typer.echo("✅ Handler executed successfully.\n")
|
|
1476
|
+
except Exception as e:
|
|
1477
|
+
typer.echo("⚠️ " + typer.style(f"Handler execution failed: {e}", fg=typer.colors.RED))
|
|
1200
1478
|
|
|
1201
1479
|
@sessions.command(help='Remove a specific connection')
|
|
1202
1480
|
def remove_connection(session_name: str, type: str, connection_name: str):
|
|
@@ -1273,7 +1551,7 @@ def refresh():
|
|
|
1273
1551
|
|
|
1274
1552
|
if resp.ok:
|
|
1275
1553
|
sessions = resp.json()['Sessions']
|
|
1276
|
-
config['
|
|
1554
|
+
config['terminal_sessions'] = sessions
|
|
1277
1555
|
write_config(config)
|
|
1278
1556
|
|
|
1279
1557
|
typer.echo(
|
|
@@ -1302,8 +1580,230 @@ def refresh():
|
|
|
1302
1580
|
fg=typer.colors.RED
|
|
1303
1581
|
)
|
|
1304
1582
|
)
|
|
1583
|
+
|
|
1584
|
+
@sessions.command(help='Show all active and inactive sessions')
|
|
1585
|
+
def show():
|
|
1586
|
+
config = load_config()
|
|
1587
|
+
|
|
1588
|
+
if config is None:
|
|
1589
|
+
typer.echo('\nYou have not configured a developer API. Please run:')
|
|
1590
|
+
typer.echo('\t' + typer.style(' orrin config configure-dev ', fg=typer.colors.YELLOW) + '\n')
|
|
1591
|
+
return
|
|
1592
|
+
|
|
1593
|
+
if not 'terminal_sessions' in config:
|
|
1594
|
+
typer.echo('\nYou have no sessions. You can create one by running:')
|
|
1595
|
+
typer.echo('\t' + typer.style(' orrin sessions create ', fg=typer.colors.YELLOW) + '\n')
|
|
1596
|
+
return
|
|
1597
|
+
|
|
1598
|
+
active_sessions = [session for session in config['terminal_sessions'] if session['active']]
|
|
1599
|
+
inactive_sessions = [session for session in config['terminal_sessions'] if not session['active']]
|
|
1600
|
+
|
|
1601
|
+
typer.echo('\nActive Terminal Sessions:')
|
|
1602
|
+
|
|
1603
|
+
if active_sessions == []:
|
|
1604
|
+
typer.echo('\tNo active sessions')
|
|
1605
|
+
else:
|
|
1606
|
+
for i in range(len(active_sessions)):
|
|
1607
|
+
typer.echo('\t' + f'{active_sessions[i]["session_name"]}, id: {active_sessions[i]["session_id"]}')
|
|
1608
|
+
|
|
1609
|
+
|
|
1610
|
+
typer.echo('\nInactive Terminal Sessions:')
|
|
1611
|
+
|
|
1612
|
+
if inactive_sessions == []:
|
|
1613
|
+
typer.echo('\tAll sessions are active')
|
|
1614
|
+
else:
|
|
1615
|
+
for i in range(len(inactive_sessions)):
|
|
1616
|
+
typer.echo('\t' + f'{inactive_sessions[i]["session_name"]}, id: {inactive_sessions[i]["session_id"]}')
|
|
1617
|
+
|
|
1618
|
+
typer.echo('')
|
|
1619
|
+
|
|
1305
1620
|
# ---------------------------
|
|
1306
1621
|
|
|
1622
|
+
def is_development_url(url: str) -> bool:
|
|
1623
|
+
parsed = urlparse(url)
|
|
1624
|
+
host = parsed.hostname
|
|
1625
|
+
|
|
1626
|
+
if host is None:
|
|
1627
|
+
return False
|
|
1628
|
+
|
|
1629
|
+
# Common localhost names
|
|
1630
|
+
if host.lower() in {
|
|
1631
|
+
"localhost",
|
|
1632
|
+
"localhost.localdomain",
|
|
1633
|
+
}:
|
|
1634
|
+
return True
|
|
1635
|
+
|
|
1636
|
+
try:
|
|
1637
|
+
ip = ipaddress.ip_address(host)
|
|
1638
|
+
|
|
1639
|
+
# Loopback: 127.0.0.0/8, ::1
|
|
1640
|
+
if ip.is_loopback:
|
|
1641
|
+
return True
|
|
1642
|
+
|
|
1643
|
+
# Private networks: 10.x, 172.16-31.x, 192.168.x
|
|
1644
|
+
if ip.is_private:
|
|
1645
|
+
return True
|
|
1646
|
+
|
|
1647
|
+
# Link-local: 169.254.x.x
|
|
1648
|
+
if ip.is_link_local:
|
|
1649
|
+
return True
|
|
1650
|
+
|
|
1651
|
+
except ValueError:
|
|
1652
|
+
# Not an IP address
|
|
1653
|
+
pass
|
|
1654
|
+
|
|
1655
|
+
return False
|
|
1656
|
+
|
|
1657
|
+
@especi.command(help='Create a mount for your webpage to use to enable websocket-based dynamic adaptability')
|
|
1658
|
+
def cmount():
|
|
1659
|
+
config = load_config()
|
|
1660
|
+
|
|
1661
|
+
if config is None or not 'api_key' in config:
|
|
1662
|
+
typer.echo('\nYou have not configured a developer API. Please run:')
|
|
1663
|
+
typer.echo('\t' + typer.style(' orrin config configure-dev ', fg=typer.colors.YELLOW) + '\n')
|
|
1664
|
+
return
|
|
1665
|
+
|
|
1666
|
+
url = typer.prompt('Production URL')
|
|
1667
|
+
|
|
1668
|
+
while is_development_url(url):
|
|
1669
|
+
typer.echo(
|
|
1670
|
+
typer.style(
|
|
1671
|
+
f'Invalid URL. A production URL is required.',
|
|
1672
|
+
fg=typer.colors.RED
|
|
1673
|
+
)
|
|
1674
|
+
)
|
|
1675
|
+
|
|
1676
|
+
url = typer.prompt('Production URL')
|
|
1677
|
+
|
|
1678
|
+
r = requests.post(
|
|
1679
|
+
'http://192.168.1.96:8081/v1/create_especi_mount',
|
|
1680
|
+
headers={ 'fsdk': 'yes', 'authorization': config['api_key'] },
|
|
1681
|
+
json={
|
|
1682
|
+
'url': url
|
|
1683
|
+
}
|
|
1684
|
+
)
|
|
1685
|
+
|
|
1686
|
+
if r.ok:
|
|
1687
|
+
typer.echo(
|
|
1688
|
+
'\n' + typer.style(
|
|
1689
|
+
f'ESPECI mount for {url} created.',
|
|
1690
|
+
fg=typer.colors.GREEN
|
|
1691
|
+
) + '\n'
|
|
1692
|
+
)
|
|
1693
|
+
|
|
1694
|
+
r = r.json()
|
|
1695
|
+
|
|
1696
|
+
typer.echo(f'Mount ID: {r["mount_id"]}')
|
|
1697
|
+
|
|
1698
|
+
if 'especi_mounts' in config:
|
|
1699
|
+
config['especi_mounts'].append({
|
|
1700
|
+
'url': url,
|
|
1701
|
+
'mount_id': r['mount_id']
|
|
1702
|
+
})
|
|
1703
|
+
else:
|
|
1704
|
+
config['especi_mounts'] = [{
|
|
1705
|
+
'url': url,
|
|
1706
|
+
'mount_id': r['mount_id']
|
|
1707
|
+
}]
|
|
1708
|
+
|
|
1709
|
+
write_config(config)
|
|
1710
|
+
else:
|
|
1711
|
+
try:
|
|
1712
|
+
r = r.json()
|
|
1713
|
+
|
|
1714
|
+
if r['message'] == 'url_mount_exists':
|
|
1715
|
+
typer.echo(
|
|
1716
|
+
'\n' + typer.style(
|
|
1717
|
+
f'Failed to create ESPECI mount: mount exists for {url}.',
|
|
1718
|
+
fg=typer.colors.RED
|
|
1719
|
+
)
|
|
1720
|
+
)
|
|
1721
|
+
return
|
|
1722
|
+
|
|
1723
|
+
raise Exception
|
|
1724
|
+
except:
|
|
1725
|
+
typer.echo(
|
|
1726
|
+
'\n' + typer.style(
|
|
1727
|
+
f'Failed to create ESPECI mount.',
|
|
1728
|
+
fg=typer.colors.RED
|
|
1729
|
+
)
|
|
1730
|
+
)
|
|
1731
|
+
|
|
1732
|
+
@especi.command(help='Create a mount for your webpage that runs on local addresses to use to enable websocket-based dynamic adaptability')
|
|
1733
|
+
def cmount_dev():
|
|
1734
|
+
config = load_config()
|
|
1735
|
+
|
|
1736
|
+
if config is None or not 'api_key' in config:
|
|
1737
|
+
typer.echo('\nYou have not configured a developer API. Please run:')
|
|
1738
|
+
typer.echo('\t' + typer.style(' orrin config configure-dev ', fg=typer.colors.YELLOW) + '\n')
|
|
1739
|
+
return
|
|
1740
|
+
|
|
1741
|
+
url = typer.prompt('Development URL')
|
|
1742
|
+
|
|
1743
|
+
while not is_development_url(url):
|
|
1744
|
+
typer.echo(
|
|
1745
|
+
typer.style(
|
|
1746
|
+
f'Invalid URL. A production URL is required.',
|
|
1747
|
+
fg=typer.colors.RED
|
|
1748
|
+
)
|
|
1749
|
+
)
|
|
1750
|
+
|
|
1751
|
+
url = typer.prompt('Production URL')
|
|
1752
|
+
|
|
1753
|
+
r = requests.post(
|
|
1754
|
+
'http://192.168.1.96:8081/v1/create_especi_mount',
|
|
1755
|
+
headers={ 'fsdk': 'yes', 'authorization': config['api_key'] },
|
|
1756
|
+
json={
|
|
1757
|
+
'url': url
|
|
1758
|
+
}
|
|
1759
|
+
)
|
|
1760
|
+
|
|
1761
|
+
if r.ok:
|
|
1762
|
+
typer.echo(
|
|
1763
|
+
'\n' + typer.style(
|
|
1764
|
+
f'ESPECI mount for {url} created.',
|
|
1765
|
+
fg=typer.colors.GREEN
|
|
1766
|
+
) + '\n'
|
|
1767
|
+
)
|
|
1768
|
+
|
|
1769
|
+
r = r.json()
|
|
1770
|
+
|
|
1771
|
+
typer.echo(f'Mount ID: {r["mount_id"]}')
|
|
1772
|
+
|
|
1773
|
+
if 'especi_mounts' in config:
|
|
1774
|
+
config['especi_mounts'].append({
|
|
1775
|
+
'url': url,
|
|
1776
|
+
'mount_id': r['mount_id']
|
|
1777
|
+
})
|
|
1778
|
+
else:
|
|
1779
|
+
config['especi_mounts'] = [{
|
|
1780
|
+
'url': url,
|
|
1781
|
+
'mount_id': r['mount_id']
|
|
1782
|
+
}]
|
|
1783
|
+
|
|
1784
|
+
write_config(config)
|
|
1785
|
+
else:
|
|
1786
|
+
try:
|
|
1787
|
+
r = r.json()
|
|
1788
|
+
|
|
1789
|
+
if r['message'] == 'url_mount_exists':
|
|
1790
|
+
typer.echo(
|
|
1791
|
+
'\n' + typer.style(
|
|
1792
|
+
f'Failed to create ESPECI mount: mount exists for {url}.',
|
|
1793
|
+
fg=typer.colors.RED
|
|
1794
|
+
)
|
|
1795
|
+
)
|
|
1796
|
+
return
|
|
1797
|
+
|
|
1798
|
+
raise Exception
|
|
1799
|
+
except:
|
|
1800
|
+
typer.echo(
|
|
1801
|
+
'\n' + typer.style(
|
|
1802
|
+
f'Failed to create ESPECI mount.',
|
|
1803
|
+
fg=typer.colors.RED
|
|
1804
|
+
)
|
|
1805
|
+
)
|
|
1806
|
+
|
|
1307
1807
|
def main():
|
|
1308
1808
|
app()
|
|
1309
1809
|
|
|
@@ -1,26 +1,42 @@
|
|
|
1
1
|
import websockets
|
|
2
|
-
import json
|
|
2
|
+
import json, subprocess
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
5
|
# Helper function: establish websockt connection as default handler
|
|
6
6
|
# Developer can create a sender websocket
|
|
7
|
-
async def establish_default_handler(session_id, auth):
|
|
7
|
+
async def establish_default_handler(session_id, auth, r):
|
|
8
8
|
async with websockets.connect(
|
|
9
|
-
f"wss://stellr-company.com/terminal_sessions/connect/{session_id}"
|
|
10
|
-
additional_headers={ 'authorization': auth }
|
|
9
|
+
f"wss://stellr-company.com/terminal_sessions/connect/{session_id}"
|
|
11
10
|
) as ws:
|
|
12
|
-
|
|
11
|
+
# Send authorization
|
|
12
|
+
await ws.send(f'AUTH: {auth}')
|
|
13
|
+
|
|
14
|
+
# Send intitial data telling connection what we are
|
|
15
|
+
await ws.send(json.dumps({
|
|
16
|
+
'connection_type': 'handler',
|
|
17
|
+
'websocket_dependencies': [], # no inherent dependencies
|
|
18
|
+
'handler_name': 'test'
|
|
19
|
+
}))
|
|
20
|
+
|
|
13
21
|
data = await ws.recv()
|
|
14
22
|
data = json.loads(data)
|
|
15
23
|
connection_id = data['connection_id']
|
|
16
24
|
|
|
17
25
|
typer.echo(f'Default handler connection id: {connection_id}\nSession ID: {session_id}\nUse this when creating sender websockets.\nYou are free to create as many custom handlers/senders as you would like, and are not restricted to the default handler.\n')
|
|
18
26
|
|
|
27
|
+
r()
|
|
28
|
+
|
|
19
29
|
while True:
|
|
20
30
|
data = await ws.recv()
|
|
21
31
|
data = json.loads(data)
|
|
22
32
|
|
|
23
|
-
if not 'type' in data:
|
|
33
|
+
if not 'type' in data:
|
|
34
|
+
await ws.send(json.dumps({
|
|
35
|
+
'type': 'respond',
|
|
36
|
+
'message': 'fuck off.',
|
|
37
|
+
'to': data['sender'],
|
|
38
|
+
'response_type': 'string'
|
|
39
|
+
}))
|
|
24
40
|
|
|
25
41
|
match data['type']:
|
|
26
42
|
case 'close':
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orrin-cli
|
|
3
|
+
Version: 0.3.0b4
|
|
4
|
+
Summary: Orrin CLI
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: typer[all]>=0.12.0
|
|
9
|
+
Requires-Dist: click<8.2
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# Orrin CLI v0.3.0b3
|
|
13
|
+
|
|
14
|
+
## What's New
|
|
15
|
+
|
|
16
|
+
In v0.3.0b4, we introduce support for creating "mounts" for websocket connectivity with our newly released Exegesis Specification for Interactivity.
|
orrin_cli-0.3.0b2/PKG-INFO
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: orrin-cli
|
|
3
|
-
Version: 0.3.0b2
|
|
4
|
-
Summary: Orrin CLI
|
|
5
|
-
Requires-Python: >=3.9
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
License-File: LICENSE
|
|
8
|
-
Requires-Dist: typer[all]>=0.12.0
|
|
9
|
-
Requires-Dist: click<8.2
|
|
10
|
-
Dynamic: license-file
|
|
11
|
-
|
|
12
|
-
# Orrin CLI v0.3.0b1
|
|
13
|
-
|
|
14
|
-
## What's New
|
|
15
|
-
|
|
16
|
-
In v0.3.0b1, the ability to create terminal sessions have been added. This includes many sub commands surrounding terminal sessions, including commands to pause and start sessions, as well as start default terminal session handlers.
|
orrin_cli-0.3.0b2/README.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Orrin CLI v0.3.0b1
|
|
2
|
-
|
|
3
|
-
## What's New
|
|
4
|
-
|
|
5
|
-
In v0.3.0b1, the ability to create terminal sessions have been added. This includes many sub commands surrounding terminal sessions, including commands to pause and start sessions, as well as start default terminal session handlers.
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: orrin-cli
|
|
3
|
-
Version: 0.3.0b2
|
|
4
|
-
Summary: Orrin CLI
|
|
5
|
-
Requires-Python: >=3.9
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
License-File: LICENSE
|
|
8
|
-
Requires-Dist: typer[all]>=0.12.0
|
|
9
|
-
Requires-Dist: click<8.2
|
|
10
|
-
Dynamic: license-file
|
|
11
|
-
|
|
12
|
-
# Orrin CLI v0.3.0b1
|
|
13
|
-
|
|
14
|
-
## What's New
|
|
15
|
-
|
|
16
|
-
In v0.3.0b1, the ability to create terminal sessions have been added. This includes many sub commands surrounding terminal sessions, including commands to pause and start sessions, as well as start default terminal session handlers.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|