labgate 0.5.34 → 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.
- package/README.md +1 -0
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -1
- package/dist/lib/test/integration-harness.js +16 -2
- package/dist/lib/test/integration-harness.js.map +1 -1
- package/dist/lib/ui.d.ts +1 -0
- package/dist/lib/ui.html +115 -31
- package/dist/lib/ui.js +5 -1
- package/dist/lib/ui.js.map +1 -1
- package/dist/lib/web-terminal.js +2 -1
- package/dist/lib/web-terminal.js.map +1 -1
- 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
|
-
<
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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="
|
|
6899
|
-
<script src="
|
|
6900
|
-
<script src="
|
|
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
|
|
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
|
|
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
|
-
|
|
9618
|
+
wsPath += '&afterSeq=' + encodeURIComponent(String(afterSeq));
|
|
9539
9619
|
} else if (disableLegacyReplay) {
|
|
9540
|
-
|
|
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;
|
|
@@ -12966,7 +13047,6 @@ function pollUiUpdateStatus() {
|
|
|
12966
13047
|
setUiUpdateStatus('Update complete (' + next + '). Restart `labgate ui`, then reload this page.', 'success');
|
|
12967
13048
|
showToast('Update complete. Restart `labgate ui`.', 'success');
|
|
12968
13049
|
setUiUpdateButtonsState({ showApply: false, disableCheck: false, disableApply: false });
|
|
12969
|
-
checkUiVersion(true);
|
|
12970
13050
|
return;
|
|
12971
13051
|
}
|
|
12972
13052
|
if (status === 'error') {
|
|
@@ -15194,7 +15274,7 @@ function handleResultsChangedEvent() {
|
|
|
15194
15274
|
function connectSSE() {
|
|
15195
15275
|
if (evtSource) return;
|
|
15196
15276
|
try {
|
|
15197
|
-
evtSource = new EventSource('/api/events');
|
|
15277
|
+
evtSource = new EventSource(resolveLabgateUiUrl('/api/events'));
|
|
15198
15278
|
evtSource.addEventListener('sessions', function(e) {
|
|
15199
15279
|
try {
|
|
15200
15280
|
var d = JSON.parse(e.data);
|
|
@@ -15656,7 +15736,11 @@ function loadSlurmJobs() {
|
|
|
15656
15736
|
// Session-aware filtering
|
|
15657
15737
|
if (slurmScope === 'mine') {
|
|
15658
15738
|
var sid = getAttachedWebTerminalId();
|
|
15659
|
-
if (sid)
|
|
15739
|
+
if (!sid) {
|
|
15740
|
+
el.innerHTML = '<div class="empty-state" style="padding:12px">Attach to a terminal session to view jobs for this session.</div>';
|
|
15741
|
+
return;
|
|
15742
|
+
}
|
|
15743
|
+
url += '&session_id=' + encodeURIComponent(sid);
|
|
15660
15744
|
}
|
|
15661
15745
|
|
|
15662
15746
|
fetch(url).then(function(r) { return r.json(); }).then(function(data) {
|
package/dist/lib/ui.js
CHANGED
|
@@ -5933,8 +5933,12 @@ function startUI(optsOrPort = {}, standaloneArg = true) {
|
|
|
5933
5933
|
const prewarmImageOnStartup = options.prewarmImageOnStartup === true;
|
|
5934
5934
|
const requestedPort = tcpPort ?? 0;
|
|
5935
5935
|
const maxPort = requestedPort + 3;
|
|
5936
|
+
const requestedShortCode = typeof options.shortCode === 'string' ? options.shortCode.trim() : '';
|
|
5937
|
+
if (requestedShortCode && !/^[A-Za-z0-9_-]{12}$/.test(requestedShortCode)) {
|
|
5938
|
+
throw new Error('Invalid shortCode: expected exactly 12 characters matching [A-Za-z0-9_-].');
|
|
5939
|
+
}
|
|
5936
5940
|
const uiAccessToken = useTcp ? (0, crypto_1.randomBytes)(24).toString('hex') : '';
|
|
5937
|
-
const uiShortCode = useTcp ? (0, crypto_1.randomBytes)(9).toString('base64url') : '';
|
|
5941
|
+
const uiShortCode = useTcp ? (requestedShortCode || (0, crypto_1.randomBytes)(9).toString('base64url')) : '';
|
|
5938
5942
|
let listenPort = requestedPort;
|
|
5939
5943
|
let started = false;
|
|
5940
5944
|
let dashboardQuickLink = '';
|