qbraid-lex 0.1.0__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.
- qbraid_lab/BUILD_MANIFEST.json +25 -0
- qbraid_lab/__init__.py +91 -0
- qbraid_lab/_version.py +4 -0
- qbraid_lab/handlers/__init__.py +399 -0
- qbraid_lab/handlers/auth.py +638 -0
- qbraid_lab/handlers/cloud_jobs.py +570 -0
- qbraid_lab/handlers/compute.py +295 -0
- qbraid_lab/handlers/connected_accounts.py +1144 -0
- qbraid_lab/handlers/credits.py +50 -0
- qbraid_lab/handlers/dashboard.py +806 -0
- qbraid_lab/handlers/dashboard_agents.py +760 -0
- qbraid_lab/handlers/dashboard_ws.py +726 -0
- qbraid_lab/handlers/devices.py +144 -0
- qbraid_lab/handlers/environments/__init__.py +14 -0
- qbraid_lab/handlers/environments/_version.py +4 -0
- qbraid_lab/handlers/environments/config_logger.py +39 -0
- qbraid_lab/handlers/environments/configure.py +144 -0
- qbraid_lab/handlers/environments/devices.py +57 -0
- qbraid_lab/handlers/environments/envs_create.py +237 -0
- qbraid_lab/handlers/environments/envs_delete.py +85 -0
- qbraid_lab/handlers/environments/envs_groups.py +245 -0
- qbraid_lab/handlers/environments/envs_install.py +280 -0
- qbraid_lab/handlers/environments/envs_install_stream.py +181 -0
- qbraid_lab/handlers/environments/envs_list.py +441 -0
- qbraid_lab/handlers/environments/envs_pin.py +106 -0
- qbraid_lab/handlers/environments/envs_pkgs_stream.py +246 -0
- qbraid_lab/handlers/environments/envs_remove.py +56 -0
- qbraid_lab/handlers/environments/envs_share_codes.py +302 -0
- qbraid_lab/handlers/environments/envs_share_publish.py +834 -0
- qbraid_lab/handlers/environments/envs_state.py +55 -0
- qbraid_lab/handlers/environments/envs_sync.py +207 -0
- qbraid_lab/handlers/environments/envs_update.py +236 -0
- qbraid_lab/handlers/environments/envs_upload_stream.py +167 -0
- qbraid_lab/handlers/environments/envs_validation.py +413 -0
- qbraid_lab/handlers/environments/handlers.py +230 -0
- qbraid_lab/handlers/environments/kernels.py +409 -0
- qbraid_lab/handlers/environments/logos.py +98 -0
- qbraid_lab/handlers/environments/server_version.py +25 -0
- qbraid_lab/handlers/environments/user_identity.py +136 -0
- qbraid_lab/handlers/environments/version_info.py +61 -0
- qbraid_lab/handlers/file_sync.py +862 -0
- qbraid_lab/handlers/group_jobs.py +190 -0
- qbraid_lab/handlers/hub_projects.py +221 -0
- qbraid_lab/handlers/ibm_proxy.py +110 -0
- qbraid_lab/handlers/jobs.py +391 -0
- qbraid_lab/handlers/notebook_fetch.py +110 -0
- qbraid_lab/handlers/package_check.py +78 -0
- qbraid_lab/handlers/projects.py +139 -0
- qbraid_lab/handlers/published_tours.py +110 -0
- qbraid_lab/handlers/settings.py +99 -0
- qbraid_lab/handlers/slack_callback.py +212 -0
- qbraid_lab/handlers/slack_inbox.py +125 -0
- qbraid_lab/handlers/slack_notify.py +63 -0
- qbraid_lab/handlers/ssh.py +183 -0
- qbraid_lab/handlers/sync_progress.py +134 -0
- qbraid_lab/handlers/system_metrics.py +253 -0
- qbraid_lab/handlers/tours.py +143 -0
- qbraid_lab/handlers/vault.py +539 -0
- qbraid_lab/handlers/version.py +162 -0
- qbraid_lex-0.1.0.data/data/etc/jupyter/jupyter_server_config.d/qbraid_lab.json +7 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/install.json +5 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/package.json +179 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1148.c0e537701d37a843.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1266.dfd6cfbcb9936714.js +8702 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1293.9c60f7db262e1ea0.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1558.2ddb54bb8163a13a.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1602.4f04b36108161716.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1618.54ee118032b2da06.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1626.4df0f5b8cbc47e10.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1668.5b2796c333ffda93.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1911.639694ec05d995eb.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2075.551792ee4078712c.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2362.f701e89c51fa791f.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2622.a1a1711bb6ac46ed.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/291.dbb5eae29b61efb7.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2959.671fe285301aed42.js +5 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2994.045f70ffaf4af1a1.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3073.ac823393693928b1.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3095.4cf0c5b87984fd9a.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3217.69246d42552c16d3.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3312.191245c77ccf4288.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3674.a7eb2a4033e0f98b.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3815.9b53d46f9e0bf214.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/39.66b9394bab234507.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3999.757f09f2ba409901.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4044.182ed6cacfbef7d4.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4231.eff465efa954bce0.js +5 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4292.0d02c4632454d266.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4318.69b0a174f3b08b02.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4403.7c41ea532916f64a.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4417.69123f1f9e129c3c.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/455.3e317ac8505962e8.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5045.48f8830cc9d7b2f5.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5098.15e63f55c8470d7f.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5340.256d1ae16d9266ff.js +5 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5598.18c597df8d250a3e.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5651.bc0f2804b6498974.js +6 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5655.d26ec38ef81ce240.js +3 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5855.98a5b76285470d99.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5909.bf8f484653d6ba57.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/613.467b4932fe93f76d.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6237.22a86cfc7d8aa6af.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6380.6cd6ac3de357a8ab.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6429.6c3751714e655e65.js +625 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6784.b69af43504a479bb.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6966.649d4be7d919fdcc.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6997.53fdc7b2456a0233.js +345 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7075.f9807c0fbe92c422.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7584.69e2234382e1403e.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7891.fa6de2f3473f76e6.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7997.e25594fc52ef8a03.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8032.efa743bcb71402e6.js +6 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8088.26975831219793ab.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8290.803f907931099bd0.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8395.ff9ed5e20b63f5aa.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8728.5ad4a9c7d79c1444.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8992.595781f9fb27224a.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8997.8eb08536f6f7634e.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9076.9e3f34b75ce450c6.js +3 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9153.6ab5d3049825e584.js +11 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9245.8d8c828a200c0682.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9531.6e05c1e58a761843.js +1 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9586.e5871bb54c2e8973.js +2 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/remoteEntry.218b554777ff60c7.js +6 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/style.js +4 -0
- qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/third-party-licenses.json +418 -0
- qbraid_lex-0.1.0.dist-info/METADATA +192 -0
- qbraid_lex-0.1.0.dist-info/RECORD +130 -0
- qbraid_lex-0.1.0.dist-info/WHEEL +4 -0
- qbraid_lex-0.1.0.dist-info/licenses/LICENSE +135 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"build_type": "production",
|
|
4
|
+
"timestamp": "2026-05-30T18:09:38Z",
|
|
5
|
+
"git": {
|
|
6
|
+
"branch": "main",
|
|
7
|
+
"commit": "262db95",
|
|
8
|
+
"dirty": true,
|
|
9
|
+
"repo": "qBraid/qbraid-lab-extensions"
|
|
10
|
+
},
|
|
11
|
+
"vendor": {
|
|
12
|
+
"qbraid_core": "not bundled",
|
|
13
|
+
"qbraid_cli": "not bundled"
|
|
14
|
+
},
|
|
15
|
+
"jupyterlab": "4.5.7",
|
|
16
|
+
"features": {
|
|
17
|
+
"environments": "262db95 chore(jobs): use typed delete_group from qbraid-core (>=0.3.3) (#99)",
|
|
18
|
+
"devices": "262db95 chore(jobs): use typed delete_group from qbraid-core (>=0.3.3) (#99)",
|
|
19
|
+
"jobs": "262db95 chore(jobs): use typed delete_group from qbraid-core (>=0.3.3) (#99)",
|
|
20
|
+
"dashboard": "262db95 chore(jobs): use typed delete_group from qbraid-core (>=0.3.3) (#99)",
|
|
21
|
+
"vault": "262db95 chore(jobs): use typed delete_group from qbraid-core (>=0.3.3) (#99)",
|
|
22
|
+
"walkthrough": "262db95 chore(jobs): use typed delete_group from qbraid-core (>=0.3.3) (#99)",
|
|
23
|
+
"sync": "262db95 chore(jobs): use typed delete_group from qbraid-core (>=0.3.3) (#99)"
|
|
24
|
+
}
|
|
25
|
+
}
|
qbraid_lab/__init__.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""qBraid Lab - Unified JupyterLab extension for qBraid."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from ._version import __version__
|
|
6
|
+
from .handlers import setup_handlers # Now imports from handlers/ package
|
|
7
|
+
|
|
8
|
+
__all__ = ["__version__"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _jupyter_labextension_paths():
|
|
12
|
+
"""Return the paths to the JupyterLab extension."""
|
|
13
|
+
return [{"src": "labextension", "dest": "@qbraid/lab"}]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _jupyter_server_extension_points():
|
|
17
|
+
"""Return the server extension points."""
|
|
18
|
+
return [{"module": "qbraid_lab"}]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _load_jupyter_server_extension(server_app):
|
|
22
|
+
"""Load the Jupyter server extension.
|
|
23
|
+
|
|
24
|
+
Sets up handlers, installs agent CLI hooks, prunes dead sessions,
|
|
25
|
+
and writes server connection info for hook callbacks.
|
|
26
|
+
"""
|
|
27
|
+
setup_handlers(server_app.web_app)
|
|
28
|
+
|
|
29
|
+
# Install agent hooks and initialise registry (non-blocking)
|
|
30
|
+
try:
|
|
31
|
+
from qbraid_core.services.agents import install_all_hooks
|
|
32
|
+
from qbraid_core.services.agents.registry import AgentSessionRegistryManager
|
|
33
|
+
|
|
34
|
+
registry = AgentSessionRegistryManager()
|
|
35
|
+
registry.prune_dead_sessions()
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
install_all_hooks()
|
|
39
|
+
except Exception as e:
|
|
40
|
+
import logging
|
|
41
|
+
logging.getLogger(__name__).debug("Hook installation skipped: %s", e)
|
|
42
|
+
|
|
43
|
+
def _write_server_info_deferred():
|
|
44
|
+
port = getattr(server_app, "port", 8888)
|
|
45
|
+
token = ""
|
|
46
|
+
try:
|
|
47
|
+
token = server_app.identity_provider.token or ""
|
|
48
|
+
except AttributeError:
|
|
49
|
+
pass
|
|
50
|
+
AgentSessionRegistryManager.write_server_info(port, os.getpid(), token)
|
|
51
|
+
|
|
52
|
+
from tornado.ioloop import IOLoop
|
|
53
|
+
IOLoop.current().call_later(1, _write_server_info_deferred)
|
|
54
|
+
|
|
55
|
+
except ImportError:
|
|
56
|
+
import logging
|
|
57
|
+
logging.getLogger(__name__).debug("qbraid-core agents module not available, skipping hook setup")
|
|
58
|
+
except Exception as e:
|
|
59
|
+
import logging
|
|
60
|
+
logging.getLogger(__name__).warning("Agent service init error (non-fatal): %s", e)
|
|
61
|
+
|
|
62
|
+
# Inject analytics config into PageConfig so the frontend can read it
|
|
63
|
+
# without hardcoding keys in the client bundle
|
|
64
|
+
events_url = os.environ.get("QBRAID_EVENTS_URL", "")
|
|
65
|
+
events_key = os.environ.get("QBRAID_EVENTS_KEY", "")
|
|
66
|
+
if events_url and events_key:
|
|
67
|
+
server_app.web_app.settings.setdefault("page_config_data", {})
|
|
68
|
+
server_app.web_app.settings["page_config_data"]["qbraidEventsUrl"] = events_url
|
|
69
|
+
server_app.web_app.settings["page_config_data"]["qbraidEventsKey"] = events_key
|
|
70
|
+
server_app.log.info("qbraid_lab: analytics events endpoint configured")
|
|
71
|
+
|
|
72
|
+
# Auto-trigger file sync on cloud pods.
|
|
73
|
+
# Schedule on the next IOLoop tick (not call_later(2, ...)) — a 2s delay
|
|
74
|
+
# lets the frontend's SSE stream connect first, see _sync_task=None and
|
|
75
|
+
# an empty manifest, emit "done", and render the idle UI before the
|
|
76
|
+
# sync ever starts. By scheduling immediately we ensure _sync_task is
|
|
77
|
+
# populated by the time the frontend serves its first SSE request.
|
|
78
|
+
if os.environ.get("QBRAID_CLOUD"):
|
|
79
|
+
try:
|
|
80
|
+
from tornado.ioloop import IOLoop
|
|
81
|
+
from .handlers.file_sync import _start_sync
|
|
82
|
+
|
|
83
|
+
def _auto_sync():
|
|
84
|
+
_start_sync()
|
|
85
|
+
server_app.log.info("qbraid_lab: auto-triggered file sync (cloud mode)")
|
|
86
|
+
|
|
87
|
+
IOLoop.current().add_callback(_auto_sync)
|
|
88
|
+
except ImportError:
|
|
89
|
+
server_app.log.debug("qbraid_lab: file sync module not available, skipping auto-sync")
|
|
90
|
+
|
|
91
|
+
server_app.log.info("qbraid_lab server extension loaded")
|
qbraid_lab/_version.py
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"""Handlers for qBraid Lab server extension.
|
|
2
|
+
|
|
3
|
+
This module consolidates all server handlers:
|
|
4
|
+
- Auth: qbraid-config, qiskit-config, server-ready, validate-config, disk-usage
|
|
5
|
+
- Devices: list devices, get device by QRN
|
|
6
|
+
- Jobs: list/create/delete jobs, get job details, results, program, cancel
|
|
7
|
+
- Environments: install, list, manage environments (from lab-environment-manager)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from jupyter_server.utils import url_path_join
|
|
13
|
+
|
|
14
|
+
from .auth import (
|
|
15
|
+
DiskUsageHandler,
|
|
16
|
+
PythonInfoHandler,
|
|
17
|
+
QiskitConfigHandler,
|
|
18
|
+
ServerReadyHandler,
|
|
19
|
+
UserConfigHandler,
|
|
20
|
+
UserIdentityHandler,
|
|
21
|
+
UserVerifyHandler,
|
|
22
|
+
UserContextHandler,
|
|
23
|
+
OnboardingProfileHandler,
|
|
24
|
+
ValidateConfigHandler,
|
|
25
|
+
)
|
|
26
|
+
from .ibm_proxy import IBMProxyHandler
|
|
27
|
+
from .devices import DeviceHandler, DevicesHandler, ProvidersHandler
|
|
28
|
+
from .jobs import (
|
|
29
|
+
JobCancelHandler,
|
|
30
|
+
JobHandler,
|
|
31
|
+
JobProgramHandler,
|
|
32
|
+
JobProgramAsciiHandler,
|
|
33
|
+
JobResultHandler,
|
|
34
|
+
JobsHandler,
|
|
35
|
+
JobStatusesHandler,
|
|
36
|
+
)
|
|
37
|
+
from .group_jobs import (
|
|
38
|
+
GroupJobsListHandler,
|
|
39
|
+
GroupJobHandler,
|
|
40
|
+
GroupJobChildrenHandler,
|
|
41
|
+
GroupJobCancelHandler,
|
|
42
|
+
)
|
|
43
|
+
from .credits import CreditsHandler
|
|
44
|
+
from .environments.handlers import setup_handlers as setup_environment_handlers
|
|
45
|
+
from .dashboard import (
|
|
46
|
+
DashboardSessionsHandler, SessionKillHandler, SessionRenameHandler,
|
|
47
|
+
SessionRegisterHandler, SessionDeregisterHandler, SessionDiscoverHandler, SessionDiscoverAllHandler,
|
|
48
|
+
DirectoryBrowseHandler, TranscriptStreamHandler, TerminalInputHandler,
|
|
49
|
+
OpenCodeSessionsHandler, OpenCodeStatusHandler,
|
|
50
|
+
OpenCodeEventStreamHandler, CodexTranscriptStreamHandler,
|
|
51
|
+
ClaudeSessionBrowserHandler,
|
|
52
|
+
)
|
|
53
|
+
from .dashboard_agents import (
|
|
54
|
+
AgentLaunchHandler, AgentResumeHandler, AgentSendHandler,
|
|
55
|
+
AgentReadHandler, AgentSearchHandler, AgentUpdateHandler,
|
|
56
|
+
PermissionApproveHandler, PermissionDenyHandler,
|
|
57
|
+
PermissionPendingHandler, PermissionWatchHandler,
|
|
58
|
+
MessageSendHandler, MessageInboxHandler, MessageMarkReadHandler,
|
|
59
|
+
RemoteLaunchHandler, RemoteListHandler, RemoteSendHandler,
|
|
60
|
+
RemoteReadHandler, ComputeServersHandler,
|
|
61
|
+
GitHubReposHandler,
|
|
62
|
+
)
|
|
63
|
+
from .dashboard_ws import DashboardWebSocketHandler, _install_notify_hook
|
|
64
|
+
from .projects import ProjectListHandler, ProjectBoardHandler
|
|
65
|
+
from .hub_projects import (
|
|
66
|
+
GetProjectStreamHandler,
|
|
67
|
+
HubProjectDetailHandler,
|
|
68
|
+
HubProjectsListHandler,
|
|
69
|
+
)
|
|
70
|
+
from .file_sync import FileSyncStreamHandler, FileSyncTriggerHandler
|
|
71
|
+
from .vault import VaultHandler
|
|
72
|
+
from .ssh import SSHKeyHandler
|
|
73
|
+
from .connected_accounts import (
|
|
74
|
+
CLICheckHandler,
|
|
75
|
+
CLISubmitCodeHandler,
|
|
76
|
+
ClaudeInitiateHandler,
|
|
77
|
+
CodexInitiateHandler,
|
|
78
|
+
ConnectedAccountsStatusHandler,
|
|
79
|
+
DisconnectHandler,
|
|
80
|
+
GitHubDeviceCodeHandler,
|
|
81
|
+
GitHubPollHandler,
|
|
82
|
+
SlackInitiateHandler,
|
|
83
|
+
)
|
|
84
|
+
from .slack_callback import SlackCallbackHandler
|
|
85
|
+
from .settings import SettingsHandler
|
|
86
|
+
from .tours import ToursHandler, TourHandler
|
|
87
|
+
from .published_tours import PublishedToursHandler, PublishedTourHandler
|
|
88
|
+
from .slack_notify import SlackNotifyHandler
|
|
89
|
+
from .slack_inbox import SlackInboxHandler
|
|
90
|
+
from .compute import ComputeUsageHandler, ComputeSessionHandler, ComputeShutdownHandler
|
|
91
|
+
from .cloud_jobs import CloudJobsIBMHandler, CloudJobsIBMTaskHandler, CloudJobsIBMBackendsHandler, CloudJobsAWSHandler, CloudJobsAWSTaskHandler, InstallProviderHandler
|
|
92
|
+
from .version import VersionHandler
|
|
93
|
+
from .system_metrics import SystemMetricsHandler
|
|
94
|
+
from .notebook_fetch import FetchNotebookHandler
|
|
95
|
+
from .package_check import CheckPackagesHandler
|
|
96
|
+
|
|
97
|
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
# Auth handlers
|
|
101
|
+
"UserConfigHandler",
|
|
102
|
+
"QiskitConfigHandler",
|
|
103
|
+
"ServerReadyHandler",
|
|
104
|
+
"ValidateConfigHandler",
|
|
105
|
+
"DiskUsageHandler",
|
|
106
|
+
"PythonInfoHandler",
|
|
107
|
+
# Device handlers
|
|
108
|
+
"DevicesHandler",
|
|
109
|
+
"DeviceHandler",
|
|
110
|
+
"ProvidersHandler",
|
|
111
|
+
# Job handlers
|
|
112
|
+
"JobsHandler",
|
|
113
|
+
"JobStatusesHandler",
|
|
114
|
+
"JobHandler",
|
|
115
|
+
"JobResultHandler",
|
|
116
|
+
"JobProgramHandler",
|
|
117
|
+
"JobCancelHandler",
|
|
118
|
+
# Group job handlers
|
|
119
|
+
"GroupJobsListHandler",
|
|
120
|
+
"GroupJobHandler",
|
|
121
|
+
"GroupJobChildrenHandler",
|
|
122
|
+
"GroupJobCancelHandler",
|
|
123
|
+
# Credits handler
|
|
124
|
+
"CreditsHandler",
|
|
125
|
+
# Proxy handlers
|
|
126
|
+
"IBMProxyHandler",
|
|
127
|
+
"UserVerifyHandler",
|
|
128
|
+
"UserIdentityHandler",
|
|
129
|
+
"UserContextHandler",
|
|
130
|
+
"OnboardingProfileHandler",
|
|
131
|
+
# Environment handlers (submodule)
|
|
132
|
+
"setup_environment_handlers",
|
|
133
|
+
# File sync handlers
|
|
134
|
+
"FileSyncStreamHandler",
|
|
135
|
+
"FileSyncTriggerHandler",
|
|
136
|
+
# Dashboard handlers
|
|
137
|
+
"DashboardWebSocketHandler",
|
|
138
|
+
"DashboardSessionsHandler",
|
|
139
|
+
"SessionRegisterHandler",
|
|
140
|
+
"SessionDeregisterHandler",
|
|
141
|
+
"SessionDiscoverHandler",
|
|
142
|
+
"SessionDiscoverAllHandler",
|
|
143
|
+
"SessionKillHandler",
|
|
144
|
+
"SessionRenameHandler",
|
|
145
|
+
"DirectoryBrowseHandler",
|
|
146
|
+
# Agent handlers
|
|
147
|
+
"AgentLaunchHandler",
|
|
148
|
+
"AgentResumeHandler",
|
|
149
|
+
"AgentSendHandler",
|
|
150
|
+
"AgentReadHandler",
|
|
151
|
+
"AgentSearchHandler",
|
|
152
|
+
"AgentUpdateHandler",
|
|
153
|
+
"PermissionApproveHandler",
|
|
154
|
+
"PermissionDenyHandler",
|
|
155
|
+
"PermissionPendingHandler",
|
|
156
|
+
"PermissionWatchHandler",
|
|
157
|
+
"MessageSendHandler",
|
|
158
|
+
"MessageInboxHandler",
|
|
159
|
+
"MessageMarkReadHandler",
|
|
160
|
+
"RemoteLaunchHandler",
|
|
161
|
+
"RemoteListHandler",
|
|
162
|
+
"RemoteSendHandler",
|
|
163
|
+
"RemoteReadHandler",
|
|
164
|
+
"ComputeServersHandler",
|
|
165
|
+
"GitHubReposHandler",
|
|
166
|
+
# Vault handler
|
|
167
|
+
"VaultHandler",
|
|
168
|
+
# SSH key handler
|
|
169
|
+
"SSHKeyHandler",
|
|
170
|
+
# Connected accounts handlers
|
|
171
|
+
"ConnectedAccountsStatusHandler",
|
|
172
|
+
"GitHubDeviceCodeHandler",
|
|
173
|
+
"GitHubPollHandler",
|
|
174
|
+
"ClaudeInitiateHandler",
|
|
175
|
+
"CodexInitiateHandler",
|
|
176
|
+
"CLICheckHandler",
|
|
177
|
+
"CLISubmitCodeHandler",
|
|
178
|
+
"DisconnectHandler",
|
|
179
|
+
"SlackInitiateHandler",
|
|
180
|
+
# Slack callback handler
|
|
181
|
+
"SlackCallbackHandler",
|
|
182
|
+
# Notification handlers
|
|
183
|
+
"SlackNotifyHandler",
|
|
184
|
+
# Settings handler
|
|
185
|
+
"SettingsHandler",
|
|
186
|
+
# Tours handlers
|
|
187
|
+
"ToursHandler",
|
|
188
|
+
"TourHandler",
|
|
189
|
+
# Compute handler
|
|
190
|
+
"ComputeUsageHandler",
|
|
191
|
+
# Setup function
|
|
192
|
+
"setup_handlers",
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def setup_handlers(web_app):
|
|
197
|
+
"""Setup the server extension handlers."""
|
|
198
|
+
host_pattern = ".*$"
|
|
199
|
+
base_url = web_app.settings["base_url"]
|
|
200
|
+
|
|
201
|
+
# Auth routes (compatible with lab-authentication-server paths)
|
|
202
|
+
auth_prefix = "qbraid-authentication-server"
|
|
203
|
+
auth_handlers = [
|
|
204
|
+
(url_path_join(base_url, auth_prefix, "qbraid-config"), UserConfigHandler),
|
|
205
|
+
(url_path_join(base_url, auth_prefix, "qiskit-config"), QiskitConfigHandler),
|
|
206
|
+
(url_path_join(base_url, auth_prefix, "server-ready"), ServerReadyHandler),
|
|
207
|
+
(url_path_join(base_url, auth_prefix, "validate-config"), ValidateConfigHandler),
|
|
208
|
+
(url_path_join(base_url, auth_prefix, "qbraid-disk-usage"), DiskUsageHandler),
|
|
209
|
+
(url_path_join(base_url, auth_prefix, "python-info"), PythonInfoHandler),
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
# Devices routes
|
|
213
|
+
# Note: QRN contains colons (e.g., "ibm:ibm:qpu:marrakesh"), use regex to capture
|
|
214
|
+
devices_handlers = [
|
|
215
|
+
(url_path_join(base_url, "qbraid-lab", "devices"), DevicesHandler),
|
|
216
|
+
(url_path_join(base_url, "qbraid-lab", r"devices/(.+)"), DeviceHandler),
|
|
217
|
+
(url_path_join(base_url, "qbraid-lab", "providers"), ProvidersHandler),
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
# Jobs routes
|
|
221
|
+
# Jobs QRN also contains colons
|
|
222
|
+
# Group routes MUST come before the catch-all jobs/([^/]+) to avoid route conflicts
|
|
223
|
+
jobs_handlers = [
|
|
224
|
+
(url_path_join(base_url, "qbraid-lab", "jobs"), JobsHandler),
|
|
225
|
+
(url_path_join(base_url, "qbraid-lab", "jobs", "statuses"), JobStatusesHandler),
|
|
226
|
+
(url_path_join(base_url, "qbraid-lab", "jobs", "group"), GroupJobsListHandler),
|
|
227
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/group/([^/]+)/jobs"), GroupJobChildrenHandler),
|
|
228
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/group/([^/]+)/cancel"), GroupJobCancelHandler),
|
|
229
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/group/([^/]+)"), GroupJobHandler),
|
|
230
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/result"), JobResultHandler),
|
|
231
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/program"), JobProgramHandler),
|
|
232
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/program/ascii"), JobProgramAsciiHandler),
|
|
233
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/cancel"), JobCancelHandler),
|
|
234
|
+
(url_path_join(base_url, "qbraid-lab", r"jobs/(?!group(?:/|$))([^/]+)"), JobHandler),
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
credits_handlers = [
|
|
238
|
+
(url_path_join(base_url, "qbraid-lab", "credits"), CreditsHandler),
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
# File sync routes (SSE stream + manual trigger)
|
|
242
|
+
sync_handlers = [
|
|
243
|
+
(url_path_join(base_url, "qbraid-lab", "sync-stream"), FileSyncStreamHandler),
|
|
244
|
+
(url_path_join(base_url, "qbraid-lab", "sync-trigger"), FileSyncTriggerHandler),
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
# Proxy handlers (route external API calls through server)
|
|
248
|
+
proxy_handlers = [
|
|
249
|
+
(url_path_join(base_url, "qbraid-lab", "ibm-proxy"), IBMProxyHandler),
|
|
250
|
+
(url_path_join(base_url, "qbraid-lab", "users", "verify"), UserVerifyHandler),
|
|
251
|
+
(url_path_join(base_url, "qbraid-lab", "users", "context"), UserContextHandler),
|
|
252
|
+
(url_path_join(base_url, "qbraid-lab", "users", "onboarding-profile"), OnboardingProfileHandler),
|
|
253
|
+
(url_path_join(base_url, "qbraid-lab", "identity"), UserIdentityHandler),
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
# Dashboard routes
|
|
257
|
+
dashboard_handlers = [
|
|
258
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "ws"), DashboardWebSocketHandler),
|
|
259
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "sessions"), DashboardSessionsHandler),
|
|
260
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "register"), SessionRegisterHandler),
|
|
261
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "deregister"), SessionDeregisterHandler),
|
|
262
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "discover"), SessionDiscoverHandler),
|
|
263
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "discover-all"), SessionDiscoverAllHandler),
|
|
264
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "kill"), SessionKillHandler),
|
|
265
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "rename"), SessionRenameHandler),
|
|
266
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "browse"), DirectoryBrowseHandler),
|
|
267
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "transcript", "stream"), TranscriptStreamHandler),
|
|
268
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "terminal", "input"), TerminalInputHandler),
|
|
269
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "opencode", "sessions"), OpenCodeSessionsHandler),
|
|
270
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "opencode", "status"), OpenCodeStatusHandler),
|
|
271
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "opencode", "events"), OpenCodeEventStreamHandler),
|
|
272
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "codex", "stream"), CodexTranscriptStreamHandler),
|
|
273
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "claude", "sessions"), ClaudeSessionBrowserHandler),
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
# Agent lifecycle, permissions, messaging, GitHub
|
|
277
|
+
agent_handlers = [
|
|
278
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "launch"), AgentLaunchHandler),
|
|
279
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "resume"), AgentResumeHandler),
|
|
280
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "send"), AgentSendHandler),
|
|
281
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "read"), AgentReadHandler),
|
|
282
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "search"), AgentSearchHandler),
|
|
283
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "update"), AgentUpdateHandler),
|
|
284
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "approve"), PermissionApproveHandler),
|
|
285
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "deny"), PermissionDenyHandler),
|
|
286
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "permissions"), PermissionPendingHandler),
|
|
287
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "permissions", "watch"), PermissionWatchHandler),
|
|
288
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "messages", "send"), MessageSendHandler),
|
|
289
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "messages", "inbox"), MessageInboxHandler),
|
|
290
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "messages", "mark-read"), MessageMarkReadHandler),
|
|
291
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "launch"), RemoteLaunchHandler),
|
|
292
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "list"), RemoteListHandler),
|
|
293
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "send"), RemoteSendHandler),
|
|
294
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "read"), RemoteReadHandler),
|
|
295
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "servers"), ComputeServersHandler),
|
|
296
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "github", "repos"), GitHubReposHandler),
|
|
297
|
+
# Project boards
|
|
298
|
+
(url_path_join(base_url, "qbraid-lab", "dashboard", "projects"), ProjectListHandler),
|
|
299
|
+
(url_path_join(base_url, "qbraid-lab", r"dashboard/projects/([^/]+)"), ProjectBoardHandler),
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
# Vault route
|
|
303
|
+
vault_handlers = [
|
|
304
|
+
(url_path_join(base_url, "qbraid-lab", "vault"), VaultHandler),
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
# SSH key route
|
|
308
|
+
ssh_handlers = [
|
|
309
|
+
(url_path_join(base_url, "qbraid-lab", "ssh-keys"), SSHKeyHandler),
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
# Connected accounts routes
|
|
313
|
+
ca_prefix = url_path_join(base_url, "qbraid-lab", "connected-accounts")
|
|
314
|
+
connected_accounts_handlers = [
|
|
315
|
+
(url_path_join(ca_prefix, "status"), ConnectedAccountsStatusHandler),
|
|
316
|
+
(url_path_join(ca_prefix, "github", "device-code"), GitHubDeviceCodeHandler),
|
|
317
|
+
(url_path_join(ca_prefix, "github", "poll"), GitHubPollHandler),
|
|
318
|
+
(url_path_join(ca_prefix, "claude", "initiate"), ClaudeInitiateHandler),
|
|
319
|
+
(url_path_join(ca_prefix, "codex", "initiate"), CodexInitiateHandler),
|
|
320
|
+
(url_path_join(ca_prefix, "slack", "initiate"), SlackInitiateHandler),
|
|
321
|
+
(url_path_join(ca_prefix, "cli-submit-code"), CLISubmitCodeHandler),
|
|
322
|
+
(url_path_join(ca_prefix, "cli-check"), CLICheckHandler),
|
|
323
|
+
(url_path_join(ca_prefix, "disconnect"), DisconnectHandler),
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
# Slack OAuth callback (unauthenticated — Slack redirects browser here)
|
|
327
|
+
slack_callback_handlers = [
|
|
328
|
+
(url_path_join(base_url, "qbraid-lab", "slack-callback"), SlackCallbackHandler),
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
# Notification routes
|
|
332
|
+
notification_handlers = [
|
|
333
|
+
(url_path_join(base_url, "qbraid-lab", "notifications", "slack"), SlackNotifyHandler),
|
|
334
|
+
(url_path_join(base_url, "qbraid-lab", "slack-message"), SlackInboxHandler),
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
# Settings route
|
|
338
|
+
settings_handlers = [
|
|
339
|
+
(url_path_join(base_url, "qbraid-lab", "settings"), SettingsHandler),
|
|
340
|
+
(url_path_join(base_url, "qbraid-lab", "version"), VersionHandler),
|
|
341
|
+
(url_path_join(base_url, "qbraid-lab", "check-packages"), CheckPackagesHandler),
|
|
342
|
+
]
|
|
343
|
+
|
|
344
|
+
# Compute usage route
|
|
345
|
+
compute_handlers = [
|
|
346
|
+
(url_path_join(base_url, "qbraid-lab", "compute", "usage"), ComputeUsageHandler),
|
|
347
|
+
(url_path_join(base_url, "qbraid-lab", "compute", "session"), ComputeSessionHandler),
|
|
348
|
+
(url_path_join(base_url, "qbraid-lab", "compute", "shutdown"), ComputeShutdownHandler),
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
# Tours routes (local)
|
|
352
|
+
tours_handlers = [
|
|
353
|
+
(url_path_join(base_url, "qbraid-lab", "tours"), ToursHandler),
|
|
354
|
+
(url_path_join(base_url, "qbraid-lab", r"tours/([^/]+)"), TourHandler),
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
# Published tours routes (cloud API proxy)
|
|
358
|
+
published_tours_handlers = [
|
|
359
|
+
(url_path_join(base_url, "qbraid-lab", "published-tours"), PublishedToursHandler),
|
|
360
|
+
(url_path_join(base_url, "qbraid-lab", r"published-tours/([^/]+)"), PublishedTourHandler),
|
|
361
|
+
]
|
|
362
|
+
|
|
363
|
+
# Cloud provider direct job access
|
|
364
|
+
cloud_jobs_handlers = [
|
|
365
|
+
(url_path_join(base_url, "qbraid-lab", "cloud-jobs", "ibm"), CloudJobsIBMHandler),
|
|
366
|
+
(url_path_join(base_url, "qbraid-lab", "cloud-jobs", "ibm", "task"), CloudJobsIBMTaskHandler),
|
|
367
|
+
(url_path_join(base_url, "qbraid-lab", "cloud-jobs", "ibm", "backends"), CloudJobsIBMBackendsHandler),
|
|
368
|
+
(url_path_join(base_url, "qbraid-lab", "cloud-jobs", "aws"), CloudJobsAWSHandler),
|
|
369
|
+
(url_path_join(base_url, "qbraid-lab", "cloud-jobs", "aws", "task"), CloudJobsAWSTaskHandler),
|
|
370
|
+
(url_path_join(base_url, "qbraid-lab", "cloud-jobs", "install"), InstallProviderHandler),
|
|
371
|
+
]
|
|
372
|
+
|
|
373
|
+
# Hub projects (Explore feature): list/detail + SSE install stream
|
|
374
|
+
hub_projects_handlers = [
|
|
375
|
+
(url_path_join(base_url, "qbraid-lab", "hub-projects"), HubProjectsListHandler),
|
|
376
|
+
(url_path_join(base_url, "qbraid-lab", r"hub-projects/([^/]+)"), HubProjectDetailHandler),
|
|
377
|
+
(url_path_join(base_url, "qbraid-lab", r"get-project/([^/]+)"), GetProjectStreamHandler),
|
|
378
|
+
]
|
|
379
|
+
|
|
380
|
+
# Fetch individual notebooks from GitHub repos
|
|
381
|
+
notebook_fetch_handlers = [
|
|
382
|
+
(url_path_join(base_url, "qbraid-lab", "fetch-notebook"), FetchNotebookHandler),
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
# System metrics (for analytics / mining detection)
|
|
386
|
+
metrics_handlers = [
|
|
387
|
+
(url_path_join(base_url, "qbraid-lab", "system-metrics"), SystemMetricsHandler),
|
|
388
|
+
]
|
|
389
|
+
|
|
390
|
+
all_handlers = auth_handlers + devices_handlers + jobs_handlers + credits_handlers + sync_handlers + proxy_handlers + dashboard_handlers + agent_handlers + vault_handlers + ssh_handlers + connected_accounts_handlers + slack_callback_handlers + notification_handlers + settings_handlers + compute_handlers + tours_handlers + published_tours_handlers + cloud_jobs_handlers + hub_projects_handlers + notebook_fetch_handlers + metrics_handlers
|
|
391
|
+
|
|
392
|
+
web_app.add_handlers(host_pattern, all_handlers)
|
|
393
|
+
|
|
394
|
+
# Wire up WebSocket push notifications for dashboard registry changes
|
|
395
|
+
_install_notify_hook()
|
|
396
|
+
|
|
397
|
+
# Environment handlers (from lab-environment-manager)
|
|
398
|
+
# These use a separate setup function with url_path prefix
|
|
399
|
+
setup_environment_handlers(web_app, "jupyter-environment-manager")
|