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.
Files changed (130) hide show
  1. qbraid_lab/BUILD_MANIFEST.json +25 -0
  2. qbraid_lab/__init__.py +91 -0
  3. qbraid_lab/_version.py +4 -0
  4. qbraid_lab/handlers/__init__.py +399 -0
  5. qbraid_lab/handlers/auth.py +638 -0
  6. qbraid_lab/handlers/cloud_jobs.py +570 -0
  7. qbraid_lab/handlers/compute.py +295 -0
  8. qbraid_lab/handlers/connected_accounts.py +1144 -0
  9. qbraid_lab/handlers/credits.py +50 -0
  10. qbraid_lab/handlers/dashboard.py +806 -0
  11. qbraid_lab/handlers/dashboard_agents.py +760 -0
  12. qbraid_lab/handlers/dashboard_ws.py +726 -0
  13. qbraid_lab/handlers/devices.py +144 -0
  14. qbraid_lab/handlers/environments/__init__.py +14 -0
  15. qbraid_lab/handlers/environments/_version.py +4 -0
  16. qbraid_lab/handlers/environments/config_logger.py +39 -0
  17. qbraid_lab/handlers/environments/configure.py +144 -0
  18. qbraid_lab/handlers/environments/devices.py +57 -0
  19. qbraid_lab/handlers/environments/envs_create.py +237 -0
  20. qbraid_lab/handlers/environments/envs_delete.py +85 -0
  21. qbraid_lab/handlers/environments/envs_groups.py +245 -0
  22. qbraid_lab/handlers/environments/envs_install.py +280 -0
  23. qbraid_lab/handlers/environments/envs_install_stream.py +181 -0
  24. qbraid_lab/handlers/environments/envs_list.py +441 -0
  25. qbraid_lab/handlers/environments/envs_pin.py +106 -0
  26. qbraid_lab/handlers/environments/envs_pkgs_stream.py +246 -0
  27. qbraid_lab/handlers/environments/envs_remove.py +56 -0
  28. qbraid_lab/handlers/environments/envs_share_codes.py +302 -0
  29. qbraid_lab/handlers/environments/envs_share_publish.py +834 -0
  30. qbraid_lab/handlers/environments/envs_state.py +55 -0
  31. qbraid_lab/handlers/environments/envs_sync.py +207 -0
  32. qbraid_lab/handlers/environments/envs_update.py +236 -0
  33. qbraid_lab/handlers/environments/envs_upload_stream.py +167 -0
  34. qbraid_lab/handlers/environments/envs_validation.py +413 -0
  35. qbraid_lab/handlers/environments/handlers.py +230 -0
  36. qbraid_lab/handlers/environments/kernels.py +409 -0
  37. qbraid_lab/handlers/environments/logos.py +98 -0
  38. qbraid_lab/handlers/environments/server_version.py +25 -0
  39. qbraid_lab/handlers/environments/user_identity.py +136 -0
  40. qbraid_lab/handlers/environments/version_info.py +61 -0
  41. qbraid_lab/handlers/file_sync.py +862 -0
  42. qbraid_lab/handlers/group_jobs.py +190 -0
  43. qbraid_lab/handlers/hub_projects.py +221 -0
  44. qbraid_lab/handlers/ibm_proxy.py +110 -0
  45. qbraid_lab/handlers/jobs.py +391 -0
  46. qbraid_lab/handlers/notebook_fetch.py +110 -0
  47. qbraid_lab/handlers/package_check.py +78 -0
  48. qbraid_lab/handlers/projects.py +139 -0
  49. qbraid_lab/handlers/published_tours.py +110 -0
  50. qbraid_lab/handlers/settings.py +99 -0
  51. qbraid_lab/handlers/slack_callback.py +212 -0
  52. qbraid_lab/handlers/slack_inbox.py +125 -0
  53. qbraid_lab/handlers/slack_notify.py +63 -0
  54. qbraid_lab/handlers/ssh.py +183 -0
  55. qbraid_lab/handlers/sync_progress.py +134 -0
  56. qbraid_lab/handlers/system_metrics.py +253 -0
  57. qbraid_lab/handlers/tours.py +143 -0
  58. qbraid_lab/handlers/vault.py +539 -0
  59. qbraid_lab/handlers/version.py +162 -0
  60. qbraid_lex-0.1.0.data/data/etc/jupyter/jupyter_server_config.d/qbraid_lab.json +7 -0
  61. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/install.json +5 -0
  62. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/package.json +179 -0
  63. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1148.c0e537701d37a843.js +1 -0
  64. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1266.dfd6cfbcb9936714.js +8702 -0
  65. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1293.9c60f7db262e1ea0.js +1 -0
  66. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1558.2ddb54bb8163a13a.js +1 -0
  67. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1602.4f04b36108161716.js +1 -0
  68. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1618.54ee118032b2da06.js +1 -0
  69. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1626.4df0f5b8cbc47e10.js +1 -0
  70. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1668.5b2796c333ffda93.js +1 -0
  71. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/1911.639694ec05d995eb.js +1 -0
  72. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2075.551792ee4078712c.js +1 -0
  73. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2362.f701e89c51fa791f.js +1 -0
  74. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2622.a1a1711bb6ac46ed.js +1 -0
  75. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/291.dbb5eae29b61efb7.js +1 -0
  76. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2959.671fe285301aed42.js +5 -0
  77. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/2994.045f70ffaf4af1a1.js +1 -0
  78. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3073.ac823393693928b1.js +1 -0
  79. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3095.4cf0c5b87984fd9a.js +1 -0
  80. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3217.69246d42552c16d3.js +1 -0
  81. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3312.191245c77ccf4288.js +1 -0
  82. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3674.a7eb2a4033e0f98b.js +1 -0
  83. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3815.9b53d46f9e0bf214.js +1 -0
  84. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/39.66b9394bab234507.js +1 -0
  85. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/3999.757f09f2ba409901.js +1 -0
  86. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4044.182ed6cacfbef7d4.js +1 -0
  87. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4231.eff465efa954bce0.js +5 -0
  88. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4292.0d02c4632454d266.js +1 -0
  89. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4318.69b0a174f3b08b02.js +1 -0
  90. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4403.7c41ea532916f64a.js +1 -0
  91. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/4417.69123f1f9e129c3c.js +1 -0
  92. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/455.3e317ac8505962e8.js +1 -0
  93. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5045.48f8830cc9d7b2f5.js +1 -0
  94. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5098.15e63f55c8470d7f.js +1 -0
  95. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5340.256d1ae16d9266ff.js +5 -0
  96. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5598.18c597df8d250a3e.js +1 -0
  97. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5651.bc0f2804b6498974.js +6 -0
  98. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5655.d26ec38ef81ce240.js +3 -0
  99. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5855.98a5b76285470d99.js +1 -0
  100. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/5909.bf8f484653d6ba57.js +1 -0
  101. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/613.467b4932fe93f76d.js +1 -0
  102. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6237.22a86cfc7d8aa6af.js +1 -0
  103. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6380.6cd6ac3de357a8ab.js +1 -0
  104. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6429.6c3751714e655e65.js +625 -0
  105. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6784.b69af43504a479bb.js +1 -0
  106. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6966.649d4be7d919fdcc.js +1 -0
  107. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/6997.53fdc7b2456a0233.js +345 -0
  108. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7075.f9807c0fbe92c422.js +1 -0
  109. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7584.69e2234382e1403e.js +1 -0
  110. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7891.fa6de2f3473f76e6.js +1 -0
  111. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/7997.e25594fc52ef8a03.js +1 -0
  112. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8032.efa743bcb71402e6.js +6 -0
  113. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8088.26975831219793ab.js +1 -0
  114. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8290.803f907931099bd0.js +1 -0
  115. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8395.ff9ed5e20b63f5aa.js +1 -0
  116. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8728.5ad4a9c7d79c1444.js +1 -0
  117. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8992.595781f9fb27224a.js +1 -0
  118. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/8997.8eb08536f6f7634e.js +1 -0
  119. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9076.9e3f34b75ce450c6.js +3 -0
  120. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9153.6ab5d3049825e584.js +11 -0
  121. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9245.8d8c828a200c0682.js +1 -0
  122. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9531.6e05c1e58a761843.js +1 -0
  123. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/9586.e5871bb54c2e8973.js +2 -0
  124. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/remoteEntry.218b554777ff60c7.js +6 -0
  125. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/style.js +4 -0
  126. qbraid_lex-0.1.0.data/data/share/jupyter/labextensions/@qbraid/lab/static/third-party-licenses.json +418 -0
  127. qbraid_lex-0.1.0.dist-info/METADATA +192 -0
  128. qbraid_lex-0.1.0.dist-info/RECORD +130 -0
  129. qbraid_lex-0.1.0.dist-info/WHEEL +4 -0
  130. 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,4 @@
1
+ # This file is auto-generated by Hatchling. As such, do not:
2
+ # - modify
3
+ # - track in version control e.g. be sure to add to .gitignore
4
+ __version__ = VERSION = '0.1.0'
@@ -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")