qbraid-lex 0.1.0a1__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 (128) 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 +380 -0
  5. qbraid_lab/handlers/auth.py +638 -0
  6. qbraid_lab/handlers/cloud_jobs.py +519 -0
  7. qbraid_lab/handlers/compute.py +289 -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 +233 -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 +812 -0
  42. qbraid_lab/handlers/hub_projects.py +221 -0
  43. qbraid_lab/handlers/ibm_proxy.py +110 -0
  44. qbraid_lab/handlers/jobs.py +391 -0
  45. qbraid_lab/handlers/notebook_fetch.py +110 -0
  46. qbraid_lab/handlers/projects.py +139 -0
  47. qbraid_lab/handlers/published_tours.py +110 -0
  48. qbraid_lab/handlers/settings.py +99 -0
  49. qbraid_lab/handlers/slack_callback.py +212 -0
  50. qbraid_lab/handlers/slack_inbox.py +125 -0
  51. qbraid_lab/handlers/slack_notify.py +63 -0
  52. qbraid_lab/handlers/ssh.py +126 -0
  53. qbraid_lab/handlers/sync_progress.py +134 -0
  54. qbraid_lab/handlers/system_metrics.py +253 -0
  55. qbraid_lab/handlers/tours.py +143 -0
  56. qbraid_lab/handlers/vault.py +539 -0
  57. qbraid_lab/handlers/version.py +162 -0
  58. qbraid_lex-0.1.0a1.data/data/etc/jupyter/jupyter_server_config.d/qbraid_lab.json +7 -0
  59. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/install.json +5 -0
  60. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/package.json +179 -0
  61. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1148.c0e537701d37a843.js +1 -0
  62. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1266.8eace7fff9020d12.js +8507 -0
  63. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1293.9c60f7db262e1ea0.js +1 -0
  64. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1558.2ddb54bb8163a13a.js +1 -0
  65. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1602.4f04b36108161716.js +1 -0
  66. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1618.54ee118032b2da06.js +1 -0
  67. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1626.4df0f5b8cbc47e10.js +1 -0
  68. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1668.5b2796c333ffda93.js +1 -0
  69. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/1911.639694ec05d995eb.js +1 -0
  70. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/2075.551792ee4078712c.js +1 -0
  71. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/2362.afdfcbb7c3dd0b03.js +1 -0
  72. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/2622.a1a1711bb6ac46ed.js +1 -0
  73. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/291.dbb5eae29b61efb7.js +1 -0
  74. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/2959.671fe285301aed42.js +5 -0
  75. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/2994.045f70ffaf4af1a1.js +1 -0
  76. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/3073.ac823393693928b1.js +1 -0
  77. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/3095.4cf0c5b87984fd9a.js +1 -0
  78. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/3217.69246d42552c16d3.js +1 -0
  79. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/3312.191245c77ccf4288.js +1 -0
  80. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/3674.a7eb2a4033e0f98b.js +1 -0
  81. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/3815.9b53d46f9e0bf214.js +1 -0
  82. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/39.66b9394bab234507.js +1 -0
  83. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/3999.757f09f2ba409901.js +1 -0
  84. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/4044.182ed6cacfbef7d4.js +1 -0
  85. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/4231.eff465efa954bce0.js +5 -0
  86. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/4292.0d02c4632454d266.js +1 -0
  87. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/4318.69b0a174f3b08b02.js +1 -0
  88. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/4403.7c41ea532916f64a.js +1 -0
  89. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/4417.69123f1f9e129c3c.js +1 -0
  90. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/455.3e317ac8505962e8.js +1 -0
  91. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5045.48f8830cc9d7b2f5.js +1 -0
  92. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5098.15e63f55c8470d7f.js +1 -0
  93. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5340.256d1ae16d9266ff.js +5 -0
  94. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5598.18c597df8d250a3e.js +1 -0
  95. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5651.bc0f2804b6498974.js +6 -0
  96. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5655.d26ec38ef81ce240.js +3 -0
  97. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5855.98a5b76285470d99.js +1 -0
  98. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/5909.bf8f484653d6ba57.js +1 -0
  99. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/613.467b4932fe93f76d.js +1 -0
  100. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/6237.22a86cfc7d8aa6af.js +1 -0
  101. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/6380.6cd6ac3de357a8ab.js +1 -0
  102. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/6429.6c3751714e655e65.js +625 -0
  103. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/6784.b69af43504a479bb.js +1 -0
  104. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/6966.649d4be7d919fdcc.js +1 -0
  105. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/7075.f9807c0fbe92c422.js +1 -0
  106. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/7100.af27fa7f9ad87a0d.js +286 -0
  107. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/7584.69e2234382e1403e.js +1 -0
  108. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/7891.fa6de2f3473f76e6.js +1 -0
  109. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/7997.e25594fc52ef8a03.js +1 -0
  110. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/8032.efa743bcb71402e6.js +6 -0
  111. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/8088.26975831219793ab.js +1 -0
  112. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/8290.803f907931099bd0.js +1 -0
  113. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/8395.ff9ed5e20b63f5aa.js +1 -0
  114. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/8728.5ad4a9c7d79c1444.js +1 -0
  115. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/8992.595781f9fb27224a.js +1 -0
  116. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/8997.8eb08536f6f7634e.js +1 -0
  117. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/9076.9e3f34b75ce450c6.js +3 -0
  118. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/9153.6ab5d3049825e584.js +11 -0
  119. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/9245.8d8c828a200c0682.js +1 -0
  120. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/9531.6e05c1e58a761843.js +1 -0
  121. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/9586.e5871bb54c2e8973.js +2 -0
  122. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/remoteEntry.09c4eaa49e85f998.js +6 -0
  123. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/style.js +4 -0
  124. qbraid_lex-0.1.0a1.data/data/share/jupyter/labextensions/@qbraid/lab/static/third-party-licenses.json +418 -0
  125. qbraid_lex-0.1.0a1.dist-info/METADATA +83 -0
  126. qbraid_lex-0.1.0a1.dist-info/RECORD +128 -0
  127. qbraid_lex-0.1.0a1.dist-info/WHEEL +4 -0
  128. qbraid_lex-0.1.0a1.dist-info/licenses/LICENSE +29 -0
@@ -0,0 +1,25 @@
1
+ {
2
+ "version": "0.1.0a1",
3
+ "build_type": "production",
4
+ "timestamp": "2026-05-10T15:45:09Z",
5
+ "git": {
6
+ "branch": "main",
7
+ "commit": "d857c15",
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": "d857c15 chore: rename to qbraid-lex; add publish-to-PyPI workflow (#63)",
18
+ "devices": "d857c15 chore: rename to qbraid-lex; add publish-to-PyPI workflow (#63)",
19
+ "jobs": "d857c15 chore: rename to qbraid-lex; add publish-to-PyPI workflow (#63)",
20
+ "dashboard": "d857c15 chore: rename to qbraid-lex; add publish-to-PyPI workflow (#63)",
21
+ "vault": "d857c15 chore: rename to qbraid-lex; add publish-to-PyPI workflow (#63)",
22
+ "walkthrough": "d857c15 chore: rename to qbraid-lex; add publish-to-PyPI workflow (#63)",
23
+ "sync": "d857c15 chore: rename to qbraid-lex; add publish-to-PyPI workflow (#63)"
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.0a1'
@@ -0,0 +1,380 @@
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 .credits import CreditsHandler
38
+ from .environments.handlers import setup_handlers as setup_environment_handlers
39
+ from .dashboard import (
40
+ DashboardSessionsHandler, SessionKillHandler, SessionRenameHandler,
41
+ SessionRegisterHandler, SessionDeregisterHandler, SessionDiscoverHandler, SessionDiscoverAllHandler,
42
+ DirectoryBrowseHandler, TranscriptStreamHandler, TerminalInputHandler,
43
+ OpenCodeSessionsHandler, OpenCodeStatusHandler,
44
+ OpenCodeEventStreamHandler, CodexTranscriptStreamHandler,
45
+ ClaudeSessionBrowserHandler,
46
+ )
47
+ from .dashboard_agents import (
48
+ AgentLaunchHandler, AgentResumeHandler, AgentSendHandler,
49
+ AgentReadHandler, AgentSearchHandler, AgentUpdateHandler,
50
+ PermissionApproveHandler, PermissionDenyHandler,
51
+ PermissionPendingHandler, PermissionWatchHandler,
52
+ MessageSendHandler, MessageInboxHandler, MessageMarkReadHandler,
53
+ RemoteLaunchHandler, RemoteListHandler, RemoteSendHandler,
54
+ RemoteReadHandler, ComputeServersHandler,
55
+ GitHubReposHandler,
56
+ )
57
+ from .dashboard_ws import DashboardWebSocketHandler, _install_notify_hook
58
+ from .projects import ProjectListHandler, ProjectBoardHandler
59
+ from .hub_projects import (
60
+ GetProjectStreamHandler,
61
+ HubProjectDetailHandler,
62
+ HubProjectsListHandler,
63
+ )
64
+ from .file_sync import FileSyncStreamHandler, FileSyncTriggerHandler
65
+ from .vault import VaultHandler
66
+ from .ssh import SSHKeyHandler
67
+ from .connected_accounts import (
68
+ CLICheckHandler,
69
+ CLISubmitCodeHandler,
70
+ ClaudeInitiateHandler,
71
+ CodexInitiateHandler,
72
+ ConnectedAccountsStatusHandler,
73
+ DisconnectHandler,
74
+ GitHubDeviceCodeHandler,
75
+ GitHubPollHandler,
76
+ SlackInitiateHandler,
77
+ )
78
+ from .slack_callback import SlackCallbackHandler
79
+ from .settings import SettingsHandler
80
+ from .tours import ToursHandler, TourHandler
81
+ from .published_tours import PublishedToursHandler, PublishedTourHandler
82
+ from .slack_notify import SlackNotifyHandler
83
+ from .slack_inbox import SlackInboxHandler
84
+ from .compute import ComputeUsageHandler, ComputeSessionHandler, ComputeShutdownHandler
85
+ from .cloud_jobs import CloudJobsIBMHandler, CloudJobsIBMTaskHandler, CloudJobsIBMBackendsHandler, CloudJobsAWSHandler, CloudJobsAWSTaskHandler, InstallProviderHandler
86
+ from .version import VersionHandler
87
+ from .system_metrics import SystemMetricsHandler
88
+ from .notebook_fetch import FetchNotebookHandler
89
+
90
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
91
+
92
+ __all__ = [
93
+ # Auth handlers
94
+ "UserConfigHandler",
95
+ "QiskitConfigHandler",
96
+ "ServerReadyHandler",
97
+ "ValidateConfigHandler",
98
+ "DiskUsageHandler",
99
+ "PythonInfoHandler",
100
+ # Device handlers
101
+ "DevicesHandler",
102
+ "DeviceHandler",
103
+ "ProvidersHandler",
104
+ # Job handlers
105
+ "JobsHandler",
106
+ "JobStatusesHandler",
107
+ "JobHandler",
108
+ "JobResultHandler",
109
+ "JobProgramHandler",
110
+ "JobCancelHandler",
111
+ # Credits handler
112
+ "CreditsHandler",
113
+ # Proxy handlers
114
+ "IBMProxyHandler",
115
+ "UserVerifyHandler",
116
+ "UserIdentityHandler",
117
+ "UserContextHandler",
118
+ "OnboardingProfileHandler",
119
+ # Environment handlers (submodule)
120
+ "setup_environment_handlers",
121
+ # File sync handlers
122
+ "FileSyncStreamHandler",
123
+ "FileSyncTriggerHandler",
124
+ # Dashboard handlers
125
+ "DashboardWebSocketHandler",
126
+ "DashboardSessionsHandler",
127
+ "SessionRegisterHandler",
128
+ "SessionDeregisterHandler",
129
+ "SessionDiscoverHandler",
130
+ "SessionDiscoverAllHandler",
131
+ "SessionKillHandler",
132
+ "SessionRenameHandler",
133
+ "DirectoryBrowseHandler",
134
+ # Agent handlers
135
+ "AgentLaunchHandler",
136
+ "AgentResumeHandler",
137
+ "AgentSendHandler",
138
+ "AgentReadHandler",
139
+ "AgentSearchHandler",
140
+ "AgentUpdateHandler",
141
+ "PermissionApproveHandler",
142
+ "PermissionDenyHandler",
143
+ "PermissionPendingHandler",
144
+ "PermissionWatchHandler",
145
+ "MessageSendHandler",
146
+ "MessageInboxHandler",
147
+ "MessageMarkReadHandler",
148
+ "RemoteLaunchHandler",
149
+ "RemoteListHandler",
150
+ "RemoteSendHandler",
151
+ "RemoteReadHandler",
152
+ "ComputeServersHandler",
153
+ "GitHubReposHandler",
154
+ # Vault handler
155
+ "VaultHandler",
156
+ # SSH key handler
157
+ "SSHKeyHandler",
158
+ # Connected accounts handlers
159
+ "ConnectedAccountsStatusHandler",
160
+ "GitHubDeviceCodeHandler",
161
+ "GitHubPollHandler",
162
+ "ClaudeInitiateHandler",
163
+ "CodexInitiateHandler",
164
+ "CLICheckHandler",
165
+ "CLISubmitCodeHandler",
166
+ "DisconnectHandler",
167
+ "SlackInitiateHandler",
168
+ # Slack callback handler
169
+ "SlackCallbackHandler",
170
+ # Notification handlers
171
+ "SlackNotifyHandler",
172
+ # Settings handler
173
+ "SettingsHandler",
174
+ # Tours handlers
175
+ "ToursHandler",
176
+ "TourHandler",
177
+ # Compute handler
178
+ "ComputeUsageHandler",
179
+ # Setup function
180
+ "setup_handlers",
181
+ ]
182
+
183
+
184
+ def setup_handlers(web_app):
185
+ """Setup the server extension handlers."""
186
+ host_pattern = ".*$"
187
+ base_url = web_app.settings["base_url"]
188
+
189
+ # Auth routes (compatible with lab-authentication-server paths)
190
+ auth_prefix = "qbraid-authentication-server"
191
+ auth_handlers = [
192
+ (url_path_join(base_url, auth_prefix, "qbraid-config"), UserConfigHandler),
193
+ (url_path_join(base_url, auth_prefix, "qiskit-config"), QiskitConfigHandler),
194
+ (url_path_join(base_url, auth_prefix, "server-ready"), ServerReadyHandler),
195
+ (url_path_join(base_url, auth_prefix, "validate-config"), ValidateConfigHandler),
196
+ (url_path_join(base_url, auth_prefix, "qbraid-disk-usage"), DiskUsageHandler),
197
+ (url_path_join(base_url, auth_prefix, "python-info"), PythonInfoHandler),
198
+ ]
199
+
200
+ # Devices routes
201
+ # Note: QRN contains colons (e.g., "ibm:ibm:qpu:marrakesh"), use regex to capture
202
+ devices_handlers = [
203
+ (url_path_join(base_url, "qbraid-lab", "devices"), DevicesHandler),
204
+ (url_path_join(base_url, "qbraid-lab", r"devices/(.+)"), DeviceHandler),
205
+ (url_path_join(base_url, "qbraid-lab", "providers"), ProvidersHandler),
206
+ ]
207
+
208
+ # Jobs routes
209
+ # Jobs QRN also contains colons
210
+ jobs_handlers = [
211
+ (url_path_join(base_url, "qbraid-lab", "jobs"), JobsHandler),
212
+ (url_path_join(base_url, "qbraid-lab", "jobs", "statuses"), JobStatusesHandler),
213
+ (url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/result"), JobResultHandler),
214
+ (url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/program"), JobProgramHandler),
215
+ (url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/program/ascii"), JobProgramAsciiHandler),
216
+ (url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)/cancel"), JobCancelHandler),
217
+ (url_path_join(base_url, "qbraid-lab", r"jobs/([^/]+)"), JobHandler),
218
+ ]
219
+
220
+ credits_handlers = [
221
+ (url_path_join(base_url, "qbraid-lab", "credits"), CreditsHandler),
222
+ ]
223
+
224
+ # File sync routes (SSE stream + manual trigger)
225
+ sync_handlers = [
226
+ (url_path_join(base_url, "qbraid-lab", "sync-stream"), FileSyncStreamHandler),
227
+ (url_path_join(base_url, "qbraid-lab", "sync-trigger"), FileSyncTriggerHandler),
228
+ ]
229
+
230
+ # Proxy handlers (route external API calls through server)
231
+ proxy_handlers = [
232
+ (url_path_join(base_url, "qbraid-lab", "ibm-proxy"), IBMProxyHandler),
233
+ (url_path_join(base_url, "qbraid-lab", "users", "verify"), UserVerifyHandler),
234
+ (url_path_join(base_url, "qbraid-lab", "users", "context"), UserContextHandler),
235
+ (url_path_join(base_url, "qbraid-lab", "users", "onboarding-profile"), OnboardingProfileHandler),
236
+ (url_path_join(base_url, "qbraid-lab", "identity"), UserIdentityHandler),
237
+ ]
238
+
239
+ # Dashboard routes
240
+ dashboard_handlers = [
241
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "ws"), DashboardWebSocketHandler),
242
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "sessions"), DashboardSessionsHandler),
243
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "register"), SessionRegisterHandler),
244
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "deregister"), SessionDeregisterHandler),
245
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "discover"), SessionDiscoverHandler),
246
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "discover-all"), SessionDiscoverAllHandler),
247
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "kill"), SessionKillHandler),
248
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "sessions", "rename"), SessionRenameHandler),
249
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "browse"), DirectoryBrowseHandler),
250
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "transcript", "stream"), TranscriptStreamHandler),
251
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "terminal", "input"), TerminalInputHandler),
252
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "opencode", "sessions"), OpenCodeSessionsHandler),
253
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "opencode", "status"), OpenCodeStatusHandler),
254
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "opencode", "events"), OpenCodeEventStreamHandler),
255
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "codex", "stream"), CodexTranscriptStreamHandler),
256
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "claude", "sessions"), ClaudeSessionBrowserHandler),
257
+ ]
258
+
259
+ # Agent lifecycle, permissions, messaging, GitHub
260
+ agent_handlers = [
261
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "launch"), AgentLaunchHandler),
262
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "resume"), AgentResumeHandler),
263
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "send"), AgentSendHandler),
264
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "read"), AgentReadHandler),
265
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "search"), AgentSearchHandler),
266
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "update"), AgentUpdateHandler),
267
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "approve"), PermissionApproveHandler),
268
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "deny"), PermissionDenyHandler),
269
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "permissions"), PermissionPendingHandler),
270
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "permissions", "watch"), PermissionWatchHandler),
271
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "messages", "send"), MessageSendHandler),
272
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "messages", "inbox"), MessageInboxHandler),
273
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "messages", "mark-read"), MessageMarkReadHandler),
274
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "launch"), RemoteLaunchHandler),
275
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "list"), RemoteListHandler),
276
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "send"), RemoteSendHandler),
277
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "read"), RemoteReadHandler),
278
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "agents", "remote", "servers"), ComputeServersHandler),
279
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "github", "repos"), GitHubReposHandler),
280
+ # Project boards
281
+ (url_path_join(base_url, "qbraid-lab", "dashboard", "projects"), ProjectListHandler),
282
+ (url_path_join(base_url, "qbraid-lab", r"dashboard/projects/([^/]+)"), ProjectBoardHandler),
283
+ ]
284
+
285
+ # Vault route
286
+ vault_handlers = [
287
+ (url_path_join(base_url, "qbraid-lab", "vault"), VaultHandler),
288
+ ]
289
+
290
+ # SSH key route
291
+ ssh_handlers = [
292
+ (url_path_join(base_url, "qbraid-lab", "ssh-keys"), SSHKeyHandler),
293
+ ]
294
+
295
+ # Connected accounts routes
296
+ ca_prefix = url_path_join(base_url, "qbraid-lab", "connected-accounts")
297
+ connected_accounts_handlers = [
298
+ (url_path_join(ca_prefix, "status"), ConnectedAccountsStatusHandler),
299
+ (url_path_join(ca_prefix, "github", "device-code"), GitHubDeviceCodeHandler),
300
+ (url_path_join(ca_prefix, "github", "poll"), GitHubPollHandler),
301
+ (url_path_join(ca_prefix, "claude", "initiate"), ClaudeInitiateHandler),
302
+ (url_path_join(ca_prefix, "codex", "initiate"), CodexInitiateHandler),
303
+ (url_path_join(ca_prefix, "slack", "initiate"), SlackInitiateHandler),
304
+ (url_path_join(ca_prefix, "cli-submit-code"), CLISubmitCodeHandler),
305
+ (url_path_join(ca_prefix, "cli-check"), CLICheckHandler),
306
+ (url_path_join(ca_prefix, "disconnect"), DisconnectHandler),
307
+ ]
308
+
309
+ # Slack OAuth callback (unauthenticated — Slack redirects browser here)
310
+ slack_callback_handlers = [
311
+ (url_path_join(base_url, "qbraid-lab", "slack-callback"), SlackCallbackHandler),
312
+ ]
313
+
314
+ # Notification routes
315
+ notification_handlers = [
316
+ (url_path_join(base_url, "qbraid-lab", "notifications", "slack"), SlackNotifyHandler),
317
+ (url_path_join(base_url, "qbraid-lab", "slack-message"), SlackInboxHandler),
318
+ ]
319
+
320
+ # Settings route
321
+ settings_handlers = [
322
+ (url_path_join(base_url, "qbraid-lab", "settings"), SettingsHandler),
323
+ (url_path_join(base_url, "qbraid-lab", "version"), VersionHandler),
324
+ ]
325
+
326
+ # Compute usage route
327
+ compute_handlers = [
328
+ (url_path_join(base_url, "qbraid-lab", "compute", "usage"), ComputeUsageHandler),
329
+ (url_path_join(base_url, "qbraid-lab", "compute", "session"), ComputeSessionHandler),
330
+ (url_path_join(base_url, "qbraid-lab", "compute", "shutdown"), ComputeShutdownHandler),
331
+ ]
332
+
333
+ # Tours routes (local)
334
+ tours_handlers = [
335
+ (url_path_join(base_url, "qbraid-lab", "tours"), ToursHandler),
336
+ (url_path_join(base_url, "qbraid-lab", r"tours/([^/]+)"), TourHandler),
337
+ ]
338
+
339
+ # Published tours routes (cloud API proxy)
340
+ published_tours_handlers = [
341
+ (url_path_join(base_url, "qbraid-lab", "published-tours"), PublishedToursHandler),
342
+ (url_path_join(base_url, "qbraid-lab", r"published-tours/([^/]+)"), PublishedTourHandler),
343
+ ]
344
+
345
+ # Cloud provider direct job access
346
+ cloud_jobs_handlers = [
347
+ (url_path_join(base_url, "qbraid-lab", "cloud-jobs", "ibm"), CloudJobsIBMHandler),
348
+ (url_path_join(base_url, "qbraid-lab", "cloud-jobs", "ibm", "task"), CloudJobsIBMTaskHandler),
349
+ (url_path_join(base_url, "qbraid-lab", "cloud-jobs", "ibm", "backends"), CloudJobsIBMBackendsHandler),
350
+ (url_path_join(base_url, "qbraid-lab", "cloud-jobs", "aws"), CloudJobsAWSHandler),
351
+ (url_path_join(base_url, "qbraid-lab", "cloud-jobs", "aws", "task"), CloudJobsAWSTaskHandler),
352
+ (url_path_join(base_url, "qbraid-lab", "cloud-jobs", "install"), InstallProviderHandler),
353
+ ]
354
+
355
+ # Hub projects (Explore feature): list/detail + SSE install stream
356
+ hub_projects_handlers = [
357
+ (url_path_join(base_url, "qbraid-lab", "hub-projects"), HubProjectsListHandler),
358
+ (url_path_join(base_url, "qbraid-lab", r"hub-projects/([^/]+)"), HubProjectDetailHandler),
359
+ (url_path_join(base_url, "qbraid-lab", r"get-project/([^/]+)"), GetProjectStreamHandler),
360
+ ]
361
+
362
+ # Fetch individual notebooks from GitHub repos
363
+ notebook_fetch_handlers = [
364
+ (url_path_join(base_url, "qbraid-lab", "fetch-notebook"), FetchNotebookHandler),
365
+ ]
366
+
367
+ # System metrics (for analytics / mining detection)
368
+ metrics_handlers = [
369
+ (url_path_join(base_url, "qbraid-lab", "system-metrics"), SystemMetricsHandler),
370
+ ]
371
+
372
+ 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
373
+ web_app.add_handlers(host_pattern, all_handlers)
374
+
375
+ # Wire up WebSocket push notifications for dashboard registry changes
376
+ _install_notify_hook()
377
+
378
+ # Environment handlers (from lab-environment-manager)
379
+ # These use a separate setup function with url_path prefix
380
+ setup_environment_handlers(web_app, "jupyter-environment-manager")