labgate 0.5.35 → 0.5.36

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 (2) hide show
  1. package/dist/lib/ui.html +110 -29
  2. package/package.json +1 -1
package/dist/lib/ui.html CHANGED
@@ -4,54 +4,67 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>LabGate Settings</title>
7
- <link rel="stylesheet" href="/vendor/xterm/xterm.css">
7
+ <script>
8
+ (function canonicalizeUiPathWithTrailingSlash() {
9
+ try {
10
+ var pathname = String(window.location.pathname || '/');
11
+ if (!pathname || pathname === '/' || pathname.endsWith('/')) return;
12
+ var tail = pathname.slice(pathname.lastIndexOf('/') + 1);
13
+ if (!tail || tail.indexOf('.') !== -1) return;
14
+ window.location.replace(pathname + '/' + window.location.search + window.location.hash);
15
+ } catch (_err) {
16
+ // Best effort: continue without redirect.
17
+ }
18
+ })();
19
+ </script>
20
+ <link rel="stylesheet" href="vendor/xterm/xterm.css">
8
21
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='20' fill='%23374151'/><text x='50' y='68' font-size='52' font-family='system-ui' font-weight='700' fill='white' text-anchor='middle'>L</text></svg>">
9
22
  <style>
10
23
  @font-face {
11
24
  font-family: 'Geist';
12
- src: url('/fonts/Geist-Regular.woff2') format('woff2');
25
+ src: url('fonts/Geist-Regular.woff2') format('woff2');
13
26
  font-weight: 400;
14
27
  font-style: normal;
15
28
  font-display: swap;
16
29
  }
17
30
  @font-face {
18
31
  font-family: 'Geist';
19
- src: url('/fonts/Geist-Medium.woff2') format('woff2');
32
+ src: url('fonts/Geist-Medium.woff2') format('woff2');
20
33
  font-weight: 500;
21
34
  font-style: normal;
22
35
  font-display: swap;
23
36
  }
24
37
  @font-face {
25
38
  font-family: 'Geist';
26
- src: url('/fonts/Geist-SemiBold.woff2') format('woff2');
39
+ src: url('fonts/Geist-SemiBold.woff2') format('woff2');
27
40
  font-weight: 600;
28
41
  font-style: normal;
29
42
  font-display: swap;
30
43
  }
31
44
  @font-face {
32
45
  font-family: 'Geist';
33
- src: url('/fonts/Geist-Bold.woff2') format('woff2');
46
+ src: url('fonts/Geist-Bold.woff2') format('woff2');
34
47
  font-weight: 700;
35
48
  font-style: normal;
36
49
  font-display: swap;
37
50
  }
38
51
  @font-face {
39
52
  font-family: 'GeistMono';
40
- src: url('/fonts/GeistMono-Regular.woff2') format('woff2');
53
+ src: url('fonts/GeistMono-Regular.woff2') format('woff2');
41
54
  font-weight: 400;
42
55
  font-style: normal;
43
56
  font-display: swap;
44
57
  }
45
58
  @font-face {
46
59
  font-family: 'GeistMono';
47
- src: url('/fonts/GeistMono-Medium.woff2') format('woff2');
60
+ src: url('fonts/GeistMono-Medium.woff2') format('woff2');
48
61
  font-weight: 500;
49
62
  font-style: normal;
50
63
  font-display: swap;
51
64
  }
52
65
  @font-face {
53
66
  font-family: 'GeistPixelSquare';
54
- src: url('/fonts/GeistPixel-Square.woff2') format('woff2');
67
+ src: url('fonts/GeistPixel-Square.woff2') format('woff2');
55
68
  font-weight: 400;
56
69
  font-style: normal;
57
70
  font-display: swap;
@@ -6895,9 +6908,9 @@
6895
6908
  </div>
6896
6909
  </div>
6897
6910
 
6898
- <script src="/vendor/xterm/xterm.js"></script>
6899
- <script src="/vendor/xterm/addon-fit.js"></script>
6900
- <script src="/vendor/xterm/addon-web-links.js"></script>
6911
+ <script src="vendor/xterm/xterm.js"></script>
6912
+ <script src="vendor/xterm/addon-fit.js"></script>
6913
+ <script src="vendor/xterm/addon-web-links.js"></script>
6901
6914
  <script>
6902
6915
  var config = {};
6903
6916
  var originalConfig = '';
@@ -6966,6 +6979,76 @@ var autoCopySelectionState = {
6966
6979
  lastToastAt: 0
6967
6980
  };
6968
6981
 
6982
+ function getLabgateUiBasePath() {
6983
+ var pathname = String(window.location.pathname || '/');
6984
+ if (!pathname) return '/';
6985
+ if (pathname.charAt(0) !== '/') pathname = '/' + pathname;
6986
+ if (pathname === '/') return '/';
6987
+ if (pathname.endsWith('/')) return pathname;
6988
+ var tail = pathname.slice(pathname.lastIndexOf('/') + 1);
6989
+ if (tail && tail.indexOf('.') === -1) {
6990
+ return pathname + '/';
6991
+ }
6992
+ var slashIdx = pathname.lastIndexOf('/');
6993
+ return slashIdx >= 0 ? pathname.slice(0, slashIdx + 1) : '/';
6994
+ }
6995
+
6996
+ var LABGATE_UI_BASE_PATH = getLabgateUiBasePath();
6997
+
6998
+ function shouldPrefixLabgateUrl(url) {
6999
+ return /^\/(?:api|vendor|fonts)(?:\/|\?|$)/.test(url);
7000
+ }
7001
+
7002
+ function applyLabgateUiBasePath(rawPath) {
7003
+ if (!shouldPrefixLabgateUrl(rawPath)) return rawPath;
7004
+ if (LABGATE_UI_BASE_PATH === '/') return rawPath;
7005
+ return LABGATE_UI_BASE_PATH + rawPath.slice(1);
7006
+ }
7007
+
7008
+ function resolveLabgateUiUrl(url) {
7009
+ var raw = String(url || '');
7010
+ if (!raw) return raw;
7011
+ if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(raw)) {
7012
+ try {
7013
+ var parsed = new URL(raw);
7014
+ if (parsed.origin !== window.location.origin) return raw;
7015
+ var rewrittenPath = applyLabgateUiBasePath(parsed.pathname);
7016
+ if (rewrittenPath === parsed.pathname) return raw;
7017
+ parsed.pathname = rewrittenPath;
7018
+ return parsed.toString();
7019
+ } catch (_err) {
7020
+ return raw;
7021
+ }
7022
+ }
7023
+ return applyLabgateUiBasePath(raw);
7024
+ }
7025
+
7026
+ function buildLabgateWebSocketUrl(pathWithQuery) {
7027
+ var resolved = resolveLabgateUiUrl(pathWithQuery);
7028
+ var wsUrl = new URL(resolved, window.location.origin);
7029
+ wsUrl.protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
7030
+ return wsUrl.toString();
7031
+ }
7032
+
7033
+ if (typeof window.fetch === 'function') {
7034
+ var nativeFetch = window.fetch.bind(window);
7035
+ window.fetch = function(input, init) {
7036
+ if (typeof input === 'string') {
7037
+ return nativeFetch(resolveLabgateUiUrl(input), init);
7038
+ }
7039
+ if (typeof URL !== 'undefined' && input instanceof URL) {
7040
+ return nativeFetch(new URL(resolveLabgateUiUrl(String(input))), init);
7041
+ }
7042
+ if (typeof Request !== 'undefined' && input instanceof Request) {
7043
+ var resolvedUrl = resolveLabgateUiUrl(input.url);
7044
+ if (resolvedUrl !== input.url) {
7045
+ return nativeFetch(new Request(resolvedUrl, input), init);
7046
+ }
7047
+ }
7048
+ return nativeFetch(input, init);
7049
+ };
7050
+ }
7051
+
6969
7052
  function getCachedClaudeEmail() {
6970
7053
  try {
6971
7054
  return (localStorage.getItem(CLAUDE_EMAIL_CACHE_KEY) || '').trim();
@@ -7687,8 +7770,8 @@ function renderImageWidget(data, container) {
7687
7770
  return;
7688
7771
  }
7689
7772
  var img = document.createElement('img');
7690
- img.src = '/api/display/file?path=' + encodeURIComponent(path)
7691
- + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || '');
7773
+ img.src = resolveLabgateUiUrl('/api/display/file?path=' + encodeURIComponent(path)
7774
+ + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || ''));
7692
7775
  img.alt = data.alt || '';
7693
7776
  img.style.maxWidth = '100%';
7694
7777
  img.style.borderRadius = '6px';
@@ -7718,8 +7801,8 @@ function renderPdfWidget(data, container, opts) {
7718
7801
  pdfDiv.className = 'widget-pdf';
7719
7802
  container.appendChild(pdfDiv);
7720
7803
 
7721
- var pdfUrl = '/api/display/file?path=' + encodeURIComponent(path)
7722
- + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || '');
7804
+ var pdfUrl = resolveLabgateUiUrl('/api/display/file?path=' + encodeURIComponent(path)
7805
+ + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || ''));
7723
7806
 
7724
7807
  // Use browser's built-in PDF viewer via iframe — zero dependencies, works everywhere
7725
7808
  var iframe = document.createElement('iframe');
@@ -7877,8 +7960,8 @@ function renderMoleculeWidget(data, container, opts) {
7877
7960
  if (pdbId) {
7878
7961
  viewer.loadPdb(pdbId.toUpperCase());
7879
7962
  } else if (path) {
7880
- var fileUrl = '/api/display/file?path=' + encodeURIComponent(path)
7881
- + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || '');
7963
+ var fileUrl = resolveLabgateUiUrl('/api/display/file?path=' + encodeURIComponent(path)
7964
+ + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || ''));
7882
7965
  viewer.loadStructureFromUrl(fileUrl, 'pdb');
7883
7966
  } else if (pdbData) {
7884
7967
  viewer.loadStructureFromData(pdbData, 'pdb');
@@ -7954,8 +8037,8 @@ function renderFilePreviewWidget(data, container) {
7954
8037
 
7955
8038
  container.innerHTML = '<div class="widget-placeholder">Loading file preview...</div>';
7956
8039
 
7957
- var url = '/api/display/file?path=' + encodeURIComponent(path)
7958
- + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || '');
8040
+ var url = resolveLabgateUiUrl('/api/display/file?path=' + encodeURIComponent(path)
8041
+ + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || ''));
7959
8042
 
7960
8043
  fetch(url)
7961
8044
  .then(function(resp) {
@@ -8222,7 +8305,7 @@ function renderTracksWidget(data, container, opts) {
8222
8305
  tracksDiv.innerHTML = '';
8223
8306
  var processedTracks = trackList.map(function(t) {
8224
8307
  var track = Object.assign({}, t);
8225
- if (track.path && !track.url) { track.url = '/api/display/file?path=' + encodeURIComponent(track.path) + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || ''); delete track.path; }
8308
+ if (track.path && !track.url) { track.url = resolveLabgateUiUrl('/api/display/file?path=' + encodeURIComponent(track.path) + '&writeToken=' + encodeURIComponent(window.LABGATE_WRITE_TOKEN || '')); delete track.path; }
8226
8309
  if (!track.type) track.type = 'annotation';
8227
8310
  if (!track.displayMode) track.displayMode = 'EXPANDED';
8228
8311
  return track;
@@ -8381,10 +8464,9 @@ function submitClaudeHeadlessPrompt(promptText) {
8381
8464
  webTerm.headlessRunning = true;
8382
8465
  updateTerminalInputAvailability();
8383
8466
 
8384
- var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
8385
- var wsUrl = proto + '//' + location.host
8386
- + '/api/claude/ws?id=' + encodeURIComponent(sessionId)
8467
+ var wsPath = '/api/claude/ws?id=' + encodeURIComponent(sessionId)
8387
8468
  + '&writeToken=' + encodeURIComponent(LABGATE_WRITE_TOKEN);
8469
+ var wsUrl = buildLabgateWebSocketUrl(wsPath);
8388
8470
  var socket = new WebSocket(wsUrl);
8389
8471
  webTerm.headlessSocket = socket;
8390
8472
 
@@ -9530,15 +9612,14 @@ function openWebTerminalSocket(id, opts) {
9530
9612
  var afterSeq = Number.isFinite(options.afterSeq) ? Math.max(0, Math.floor(options.afterSeq)) : null;
9531
9613
  var disableLegacyReplay = !!options.disableLegacyReplay;
9532
9614
  var attachNonce = String(options.attachNonce || '');
9533
- var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
9534
- var wsUrl = proto + '//' + location.host
9535
- + '/api/terminal/ws?id=' + encodeURIComponent(id)
9615
+ var wsPath = '/api/terminal/ws?id=' + encodeURIComponent(id)
9536
9616
  + '&writeToken=' + encodeURIComponent(LABGATE_WRITE_TOKEN);
9537
9617
  if (afterSeq !== null) {
9538
- wsUrl += '&afterSeq=' + encodeURIComponent(String(afterSeq));
9618
+ wsPath += '&afterSeq=' + encodeURIComponent(String(afterSeq));
9539
9619
  } else if (disableLegacyReplay) {
9540
- wsUrl += '&replay=0';
9620
+ wsPath += '&replay=0';
9541
9621
  }
9622
+ var wsUrl = buildLabgateWebSocketUrl(wsPath);
9542
9623
 
9543
9624
  var socket = new WebSocket(wsUrl);
9544
9625
  webTerm.socket = socket;
@@ -15193,7 +15274,7 @@ function handleResultsChangedEvent() {
15193
15274
  function connectSSE() {
15194
15275
  if (evtSource) return;
15195
15276
  try {
15196
- evtSource = new EventSource('/api/events');
15277
+ evtSource = new EventSource(resolveLabgateUiUrl('/api/events'));
15197
15278
  evtSource.addEventListener('sessions', function(e) {
15198
15279
  try {
15199
15280
  var d = JSON.parse(e.data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "labgate",
3
- "version": "0.5.35",
3
+ "version": "0.5.36",
4
4
  "description": "Secure HPC wrapper for AI coding agents (Claude-first): policy controls, Apptainer sandboxes, and SLURM workflows — https://labgate.dev",
5
5
  "homepage": "https://labgate.dev",
6
6
  "keywords": [