From 30fbd8b53246073672734b581785d57e13094b32 Mon Sep 17 00:00:00 2001 From: namelessman Date: Tue, 23 Jun 2026 14:16:42 +1200 Subject: [PATCH 01/24] Add OpenClaw proxy handling and redirection to Caddyfile --- src/labnow-open-etc/Caddyfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index 417a925..e955cc8 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -61,6 +61,15 @@ redir @path_user_rshiny_no_slash {path}/ 308 # Redirect paths ending in "rshiny" to "rshiny/" + @path_user_openclaw path_regexp ^{$URL_PREFIX:}openclaw/.*$ + handle @path_user_openclaw { # Proxy requests to openclaw, strip prefix + uri strip_prefix {$URL_PREFIX:}openclaw + reverse_proxy localhost:18789 + } + @path_user_openclaw_no_slash path_regexp ^{$URL_PREFIX:}openclaw$ + redir @path_user_openclaw_no_slash {path}/ 308 # Redirect paths ending in "openclaw" to "openclaw/" + + handle /* { # Proxy all other requests to a default backend server on port 8888 for jupyter reverse_proxy localhost:8888 } From a3e7b7cafa6ca6a11612dbe01cfc8a413eb4ea99 Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 09:16:44 +1200 Subject: [PATCH 02/24] Add superviord to OpenClaw --- src/labnow-open.Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 010eea6..f06faa5 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -41,6 +41,9 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ + # create start-supervisord.sh for JupyterHub spawner compatibility + && printf '#!/bin/bash\n[ $BASH ] && [ -f /etc/profile ] && [ -z $ENTER_PROFILE ] && . /etc/profile\nDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh \ + && chmod +x /usr/local/bin/start-supervisord.sh \ # cleanup of any temporary or cache files to keep the image size down && rm -rf /opt/conda/share/jupyter/lab/staging \ && source /opt/utils/script-utils.sh && install__clean From 96666c69d73299682730dd5d8b00b9a7a9ea015f Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 10:05:57 +1200 Subject: [PATCH 03/24] Add health check --- src/labnow-open-etc/healthcheck-server.py | 18 ++++++++++++++++++ src/labnow-open.Dockerfile | 2 ++ 2 files changed, 20 insertions(+) create mode 100755 src/labnow-open-etc/healthcheck-server.py diff --git a/src/labnow-open-etc/healthcheck-server.py b/src/labnow-open-etc/healthcheck-server.py new file mode 100755 index 0000000..898db22 --- /dev/null +++ b/src/labnow-open-etc/healthcheck-server.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +"""Simple HTTP health check server for JupyterHub spawner compatibility.""" +import http.server +import json + +class HealthHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write(json.dumps({"status": "ok"}).encode()) + + def log_message(self, format, *args): + pass # Suppress logs + +if __name__ == "__main__": + server = http.server.HTTPServer(("0.0.0.0", 8888), HealthHandler) + server.serve_forever() diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index f06faa5..5b760e2 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -27,6 +27,7 @@ ARG PROFILE_LOCALIZE="aliyun-pub" ENV PROFILE_LOCALIZE=${PROFILE_LOCALIZE} COPY --from=builder /opt/labnow-open/ /opt/labnow-open/ +COPY ./src/labnow-open-etc/healthcheck-server.py /usr/local/bin/healthcheck-server.py RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ # handle control scripts and extensions @@ -40,6 +41,7 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\n" >> /etc/supervisord/supervisord.conf || true) \ + && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautorestart=true\n' >> /etc/supervisord/supervisord.conf) \ && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ # create start-supervisord.sh for JupyterHub spawner compatibility && printf '#!/bin/bash\n[ $BASH ] && [ -f /etc/profile ] && [ -z $ENTER_PROFILE ] && . /etc/profile\nDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh \ From fdb1c613dbec415773ef6dc0a29058756078d83d Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 10:35:30 +1200 Subject: [PATCH 04/24] Add script to start Caddy if OpenClaw is present --- src/labnow-open.Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 5b760e2..c43a82a 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -33,6 +33,7 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ # handle control scripts and extensions && (type supervisord || (source /opt/utils/script-setup-sys.sh && setup_supervisord && echo "Supervisord installed")) \ && (type caddy || (source /opt/utils/script-setup-net.sh && setup_caddy && echo "Caddy installed")) \ + && (type openclaw && type caddy && printf '#!/bin/bash\ncaddy run --config /etc/caddy/Caddyfile\n' > /usr/local/bin/start-caddy.sh && chmod +x /usr/local/bin/start-caddy.sh) \ && mkdir -pv /etc/supervisord && ln -sf /opt/labnow-open/etc/supervisord.conf /etc/supervisord/ \ && mkdir -pv /etc/caddy && ln -sf /opt/labnow-open/etc/Caddyfile /etc/caddy/ \ && (type jupyter && echo '{"ServerApp":{"ip":"0.0.0.0","port":8888,"root_dir":"/root","default_url":"/home","token":"","password":"","allow_root":true,"allow_origin":"*","open_browser":false}}' > /opt/conda/etc/jupyter/jupyter_server_config.json || true) \ From f1d64b234ae10687e83f1afc0db70f83a9733938 Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 10:45:31 +1200 Subject: [PATCH 05/24] Update supervisord configuration for OpenClaw and healthcheck to enable autostart --- src/labnow-open.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index c43a82a..9014c6d 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -41,8 +41,8 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type code-server && printf "[program:vscode]\ncommand=/usr/local/bin/start-code-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ - && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\n" >> /etc/supervisord/supervisord.conf || true) \ - && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautorestart=true\n' >> /etc/supervisord/supervisord.conf) \ + && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ + && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautostart=true\nautorestart=true\n' >> /etc/supervisord/supervisord.conf) \ && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ # create start-supervisord.sh for JupyterHub spawner compatibility && printf '#!/bin/bash\n[ $BASH ] && [ -f /etc/profile ] && [ -z $ENTER_PROFILE ] && . /etc/profile\nDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh \ From 4591dca6095bacad0a2b4d61ccde8ccb93a083bd Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 10:52:59 +1200 Subject: [PATCH 06/24] Update OpenClaw supervisord configuration to allow unconfigured gateway --- src/labnow-open.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 9014c6d..75b85b1 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -41,7 +41,7 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type code-server && printf "[program:vscode]\ncommand=/usr/local/bin/start-code-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ - && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ + && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh gateway --allow-unconfigured\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautostart=true\nautorestart=true\n' >> /etc/supervisord/supervisord.conf) \ && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ # create start-supervisord.sh for JupyterHub spawner compatibility From 4ce5a0bbb2c0f81831c5749e8c2a4ef60713250f Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 10:54:59 +1200 Subject: [PATCH 07/24] Update supervisord configuration to allow healthcheck autostart --- src/labnow-open.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 75b85b1..4f1d5ca 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -42,7 +42,7 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh gateway --allow-unconfigured\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ - && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautostart=true\nautorestart=true\n' >> /etc/supervisord/supervisord.conf) \ + && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautostart=true\nautorestart=true\n' >> /etc/supervisord/supervisord.conf || true) \ && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ # create start-supervisord.sh for JupyterHub spawner compatibility && printf '#!/bin/bash\n[ $BASH ] && [ -f /etc/profile ] && [ -z $ENTER_PROFILE ] && . /etc/profile\nDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh \ From 317458606acc9330f4341ca876bfcc87b76d43e9 Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 11:01:53 +1200 Subject: [PATCH 08/24] Fix Dockerfile to ensure start-caddy.sh script creation is resilient to errors --- src/labnow-open.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 4f1d5ca..34df56b 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -33,7 +33,7 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ # handle control scripts and extensions && (type supervisord || (source /opt/utils/script-setup-sys.sh && setup_supervisord && echo "Supervisord installed")) \ && (type caddy || (source /opt/utils/script-setup-net.sh && setup_caddy && echo "Caddy installed")) \ - && (type openclaw && type caddy && printf '#!/bin/bash\ncaddy run --config /etc/caddy/Caddyfile\n' > /usr/local/bin/start-caddy.sh && chmod +x /usr/local/bin/start-caddy.sh) \ + && (type openclaw && type caddy && printf '#!/bin/bash\ncaddy run --config /etc/caddy/Caddyfile\n' > /usr/local/bin/start-caddy.sh && chmod +x /usr/local/bin/start-caddy.sh || true) \ && mkdir -pv /etc/supervisord && ln -sf /opt/labnow-open/etc/supervisord.conf /etc/supervisord/ \ && mkdir -pv /etc/caddy && ln -sf /opt/labnow-open/etc/Caddyfile /etc/caddy/ \ && (type jupyter && echo '{"ServerApp":{"ip":"0.0.0.0","port":8888,"root_dir":"/root","default_url":"/home","token":"","password":"","allow_root":true,"allow_origin":"*","open_browser":false}}' > /opt/conda/etc/jupyter/jupyter_server_config.json || true) \ From f32632948786205022326e336d1382f299388af5 Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 12:53:32 +1200 Subject: [PATCH 09/24] Add OpenClaw and Health Check to program definitions --- src/labnow-open-web/src/App.jsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/labnow-open-web/src/App.jsx b/src/labnow-open-web/src/App.jsx index dea475c..1bb93db 100644 --- a/src/labnow-open-web/src/App.jsx +++ b/src/labnow-open-web/src/App.jsx @@ -34,6 +34,7 @@ import { buildHomePath, buildWorkspacePath } from "./utils/runtimeBase"; const PROGRAM_DEFINITIONS = { caddy: { displayName: "Caddy Server", hidden: true }, + healthcheck: { displayName: "Health Check", hidden: true }, jupyter: { displayName: "JupyterLab", link: "/lab", @@ -62,6 +63,13 @@ const PROGRAM_DEFINITIONS = { description: "Run Shiny applications for interactive dashboards. Useful for sharing data apps with your team.", }, + openclaw: { + displayName: "OpenClaw", + link: "/openclaw", + logo: "logo-openclaw.svg", + description: + "OpenClaw AI Agent platform. Build and deploy intelligent agents with natural language.", + }, }; function NotificationBar({ notice, onClose }) { From 47e0f745684cf1d9220db1d1c833c842ffb2781c Mon Sep 17 00:00:00 2001 From: namelessman Date: Thu, 25 Jun 2026 13:22:24 +1200 Subject: [PATCH 10/24] Update OpenClaw path handling in Caddyfile to support optional trailing slash --- src/labnow-open-etc/Caddyfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index e955cc8..ea53d62 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -61,13 +61,11 @@ redir @path_user_rshiny_no_slash {path}/ 308 # Redirect paths ending in "rshiny" to "rshiny/" - @path_user_openclaw path_regexp ^{$URL_PREFIX:}openclaw/.*$ + @path_user_openclaw path_regexp ^{$URL_PREFIX:}openclaw(/.*)?$ handle @path_user_openclaw { # Proxy requests to openclaw, strip prefix uri strip_prefix {$URL_PREFIX:}openclaw reverse_proxy localhost:18789 } - @path_user_openclaw_no_slash path_regexp ^{$URL_PREFIX:}openclaw$ - redir @path_user_openclaw_no_slash {path}/ 308 # Redirect paths ending in "openclaw" to "openclaw/" handle /* { # Proxy all other requests to a default backend server on port 8888 for jupyter From 4765c6949bd3c837f2b0d092c4008921237eef5e Mon Sep 17 00:00:00 2001 From: namelessman Date: Fri, 26 Jun 2026 16:43:32 +1200 Subject: [PATCH 11/24] Update OpenClaw link in PROGRAM_DEFINITIONS to include trailing slash --- src/labnow-open-web/src/App.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labnow-open-web/src/App.jsx b/src/labnow-open-web/src/App.jsx index 1bb93db..d03632a 100644 --- a/src/labnow-open-web/src/App.jsx +++ b/src/labnow-open-web/src/App.jsx @@ -65,7 +65,7 @@ const PROGRAM_DEFINITIONS = { }, openclaw: { displayName: "OpenClaw", - link: "/openclaw", + link: "/openclaw/", logo: "logo-openclaw.svg", description: "OpenClaw AI Agent platform. Build and deploy intelligent agents with natural language.", From 8f319db418b788ddfda64eb6c3c59b57b0154e1b Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Mon, 29 Jun 2026 13:40:42 +0800 Subject: [PATCH 12/24] update dependabot --- .github/dependabot.yml | 12 +----------- src/labnow-open.Dockerfile | 3 ++- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 65aaf4e..7f5b0ca 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,18 +21,8 @@ updates: patterns: - "*" - - package-ecosystem: "pip" - directory: "/src" - schedule: - interval: "weekly" - day: "sunday" - groups: - all-pip: - patterns: - - "*" - - package-ecosystem: "npm" - directory: "/admin" + directory: "/src/labnow-open-web" schedule: interval: "weekly" day: "sunday" diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 34df56b..f2cb24e 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -41,9 +41,10 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type code-server && printf "[program:vscode]\ncommand=/usr/local/bin/start-code-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ + && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh gateway --allow-unconfigured\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ + ## TODO: consider optimize this option && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautostart=true\nautorestart=true\n' >> /etc/supervisord/supervisord.conf || true) \ - && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ # create start-supervisord.sh for JupyterHub spawner compatibility && printf '#!/bin/bash\n[ $BASH ] && [ -f /etc/profile ] && [ -z $ENTER_PROFILE ] && . /etc/profile\nDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh \ && chmod +x /usr/local/bin/start-supervisord.sh \ From 9ac602ffaca82f9928980605e58167dcaed5d150 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Mon, 29 Jun 2026 14:01:12 +0800 Subject: [PATCH 13/24] updates --- src/labnow-open-etc/healthcheck-server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/labnow-open-etc/healthcheck-server.py b/src/labnow-open-etc/healthcheck-server.py index 898db22..e94753f 100755 --- a/src/labnow-open-etc/healthcheck-server.py +++ b/src/labnow-open-etc/healthcheck-server.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Simple HTTP health check server for JupyterHub spawner compatibility.""" +"""Simple HTTP health check server for spawner compatibility.""" import http.server import json From 2dd4da96a83ee9bd7b19e48f3efe36d2d77feae5 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Mon, 6 Jul 2026 20:55:55 +0800 Subject: [PATCH 14/24] debug supervisrod and spwaner check --- src/labnow-open-etc/Caddyfile | 3 ++- src/labnow-open-etc/healthcheck-server.py | 18 ------------------ src/labnow-open.Dockerfile | 4 +--- 3 files changed, 3 insertions(+), 22 deletions(-) delete mode 100755 src/labnow-open-etc/healthcheck-server.py diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index ea53d62..ae204fa 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -69,6 +69,7 @@ handle /* { # Proxy all other requests to a default backend server on port 8888 for jupyter - reverse_proxy localhost:8888 + reverse_proxy localhost:8888 localhost:9001 + lb_policy first } } diff --git a/src/labnow-open-etc/healthcheck-server.py b/src/labnow-open-etc/healthcheck-server.py deleted file mode 100755 index e94753f..0000000 --- a/src/labnow-open-etc/healthcheck-server.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -"""Simple HTTP health check server for spawner compatibility.""" -import http.server -import json - -class HealthHandler(http.server.BaseHTTPRequestHandler): - def do_GET(self): - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - self.wfile.write(json.dumps({"status": "ok"}).encode()) - - def log_message(self, format, *args): - pass # Suppress logs - -if __name__ == "__main__": - server = http.server.HTTPServer(("0.0.0.0", 8888), HealthHandler) - server.serve_forever() diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index f2cb24e..8b3acdf 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -42,9 +42,7 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ - && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh gateway --allow-unconfigured\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ - ## TODO: consider optimize this option - && (type openclaw && printf '[program:healthcheck]\ncommand=/usr/local/bin/healthcheck-server.py\nautostart=true\nautorestart=true\n' >> /etc/supervisord/supervisord.conf || true) \ + && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\n" >> /etc/supervisord/supervisord.conf || true) \ # create start-supervisord.sh for JupyterHub spawner compatibility && printf '#!/bin/bash\n[ $BASH ] && [ -f /etc/profile ] && [ -z $ENTER_PROFILE ] && . /etc/profile\nDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh \ && chmod +x /usr/local/bin/start-supervisord.sh \ From 112e770dfdd15c263c56e227d532d9290657989f Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Mon, 6 Jul 2026 21:06:34 +0800 Subject: [PATCH 15/24] remove healthcheck --- src/labnow-open-web/src/App.jsx | 1 - src/labnow-open.Dockerfile | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/labnow-open-web/src/App.jsx b/src/labnow-open-web/src/App.jsx index d03632a..deebe26 100644 --- a/src/labnow-open-web/src/App.jsx +++ b/src/labnow-open-web/src/App.jsx @@ -34,7 +34,6 @@ import { buildHomePath, buildWorkspacePath } from "./utils/runtimeBase"; const PROGRAM_DEFINITIONS = { caddy: { displayName: "Caddy Server", hidden: true }, - healthcheck: { displayName: "Health Check", hidden: true }, jupyter: { displayName: "JupyterLab", link: "/lab", diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 8b3acdf..cf2e1d4 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -27,15 +27,19 @@ ARG PROFILE_LOCALIZE="aliyun-pub" ENV PROFILE_LOCALIZE=${PROFILE_LOCALIZE} COPY --from=builder /opt/labnow-open/ /opt/labnow-open/ -COPY ./src/labnow-open-etc/healthcheck-server.py /usr/local/bin/healthcheck-server.py RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ - # handle control scripts and extensions + ## handle control scripts and extensions && (type supervisord || (source /opt/utils/script-setup-sys.sh && setup_supervisord && echo "Supervisord installed")) \ && (type caddy || (source /opt/utils/script-setup-net.sh && setup_caddy && echo "Caddy installed")) \ - && (type openclaw && type caddy && printf '#!/bin/bash\ncaddy run --config /etc/caddy/Caddyfile\n' > /usr/local/bin/start-caddy.sh && chmod +x /usr/local/bin/start-caddy.sh || true) \ && mkdir -pv /etc/supervisord && ln -sf /opt/labnow-open/etc/supervisord.conf /etc/supervisord/ \ && mkdir -pv /etc/caddy && ln -sf /opt/labnow-open/etc/Caddyfile /etc/caddy/ \ + ## create start-supervisord.sh and start-caddy.sh scripts if not existing, and make them executable + && ([ ! -f /usr/local/bin/start-supervisord.sh ] && printf '#!/bin/bash\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh || true ) \ + && ([ ! -f /usr/local/bin/start-caddy.sh ] && printf '#!/bin/bash\ncaddy run --config /etc/caddy/Caddyfile\n' > /usr/local/bin/start-caddy.sh || true ) \ + && chmod +x /usr/local/bin/start-caddy.sh \ + && chmod +x /usr/local/bin/start-supervisord.sh \ + ## components that are optional, only add them to supervisord if they exist && (type jupyter && echo '{"ServerApp":{"ip":"0.0.0.0","port":8888,"root_dir":"/root","default_url":"/home","token":"","password":"","allow_root":true,"allow_origin":"*","open_browser":false}}' > /opt/conda/etc/jupyter/jupyter_server_config.json || true) \ && (type jupyter && printf "[program:jupyter]\ncommand=/usr/local/bin/start-jupyterlab.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type code-server && printf "[program:vscode]\ncommand=/usr/local/bin/start-code-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ @@ -43,11 +47,7 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\n" >> /etc/supervisord/supervisord.conf || true) \ - # create start-supervisord.sh for JupyterHub spawner compatibility - && printf '#!/bin/bash\n[ $BASH ] && [ -f /etc/profile ] && [ -z $ENTER_PROFILE ] && . /etc/profile\nDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"\nLOG_FORMAT=json exec supervisord -c /etc/supervisord/supervisord.conf\n' > /usr/local/bin/start-supervisord.sh \ - && chmod +x /usr/local/bin/start-supervisord.sh \ # cleanup of any temporary or cache files to keep the image size down - && rm -rf /opt/conda/share/jupyter/lab/staging \ && source /opt/utils/script-utils.sh && install__clean WORKDIR $HOME_DIR From ec02a853639d796299790430b1fad7280b89bc75 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 02:45:19 +0800 Subject: [PATCH 16/24] update build --- .github/dependabot.yml | 2 + .github/workflows/build-docker.yml | 1 + src/labnow-open.Dockerfile | 2 +- tool/tool.sh | 78 ++++++++++++++++++++---------- 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7f5b0ca..cb516b9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,6 +26,8 @@ updates: schedule: interval: "weekly" day: "sunday" + cooldown: + default-days: 7 groups: all-npm: patterns: diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 469a287..4af4f06 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -17,6 +17,7 @@ concurrency: cancel-in-progress: true env: + TZ: Asia/Shanghai BUILDKIT_PROGRESS: "plain" # Full logs for CI build. REGISTRY_SRC: ${{ vars.REGISTRY_SRC || 'docker.io' }} # For BASE_NAMESPACE of images: where to pull base images from, docker.io or other source registry URL. REGISTRY_DST: ${{ vars.REGISTRY_DST || 'quay.io' }} # For tags of built images: where to push images to, docker.io or other destination registry URL. diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index cf2e1d4..3e94d02 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -1,6 +1,6 @@ # Use the existing image as the base ARG BASE_NAMESPACE="quay.io" -ARG BASE_IMG="labnow/developer:latest" +ARG BASE_IMG="developer:latest" # this ENV will be used in /opt/utils/script-localize.sh ARG PROFILE_LOCALIZE="aliyun-pub" diff --git a/tool/tool.sh b/tool/tool.sh index dfc686e..550a9ff 100644 --- a/tool/tool.sh +++ b/tool/tool.sh @@ -1,43 +1,58 @@ #!/bin/bash -set -xu +set -eux + +# If not executed in GitHub Action, run script in project root, and export the following 3 variables manually: +# export REGISTRY_SRC='quay.io' # For BASE_NAMESPACE of images: where to pull base images from, docker.io or other source registry URL. +# export REGISTRY_DST='quay.io' # For tags of built images: where to push images to, docker.io or other destination registry URL. +# export CI_PROJECT_NAME='LabNow/lab-dev' CI_PROJECT_NAME=${CI_PROJECT_NAME:-$GITHUB_REPOSITORY} CI_PROJECT_BRANCH=${GITHUB_HEAD_REF:-"main"} CI_PROJECT_SPACE=$(echo "${CI_PROJECT_BRANCH}" | cut -f1 -d'/') -if [ "${CI_PROJECT_BRANCH}" = "main" ] ; then - # If on the main branch, docker images namespace will be same as CI_PROJECT_NAME's name space - export CI_PROJECT_NAMESPACE="$(dirname ${CI_PROJECT_NAME})" ; -else - # not main branch, docker namespace = {CI_PROJECT_NAME's name space} + "-" + {1st substr before / in CI_PROJECT_SPACE} - export CI_PROJECT_NAMESPACE="$(dirname ${CI_PROJECT_NAME})0${CI_PROJECT_SPACE}" ; -fi +# If on the main branch, image namespace will be same as CI_PROJECT_NAME's name space; +# else (not main branch), image namespace = {CI_PROJECT_NAME's name space} + "0" + {1st substr before / in CI_PROJECT_SPACE}. +[ "${CI_PROJECT_BRANCH}" = "main" ] && NAMESPACE_SUFFIX="" || NAMESPACE_SUFFIX="0${CI_PROJECT_SPACE}" ; +export CI_PROJECT_NAMESPACE="$(dirname ${CI_PROJECT_NAME})${NAMESPACE_SUFFIX}" ; export IMG_NAMESPACE=$(echo "${CI_PROJECT_NAMESPACE}" | awk '{print tolower($0)}') -export IMG_PREFIX_SRC=$(echo "${REGISTRY_SRC:-"docker.io"}" | awk '{print tolower($0)}') -export IMG_PREFIX_DST=$(echo "${REGISTRY_DST:-"docker.io"}/${CI_PROJECT_NAMESPACE}" | awk '{print tolower($0)}') +export IMG_PREFIX_SRC=$(echo "${REGISTRY_SRC:-"docker.io"}/${IMG_NAMESPACE}" | awk '{print tolower($0)}') +export IMG_PREFIX_DST=$(echo "${REGISTRY_DST:-"docker.io"}/${IMG_NAMESPACE}" | awk '{print tolower($0)}') export TAG_SUFFIX="-$(git rev-parse --short HEAD)" -echo "--------> CI_PROJECT_NAMESPACE=${CI_PROJECT_NAMESPACE}" # use different namespace for dev/prd +echo "--------> CI_PROJECT_NAMESPACE=${CI_PROJECT_NAMESPACE}" +echo "--------> DOCKER_IMG_NAMESPACE=${IMG_NAMESPACE}" echo "--------> DOCKER_IMG_PREFIX_SRC=${IMG_PREFIX_SRC}" echo "--------> DOCKER_IMG_PREFIX_DST=${IMG_PREFIX_DST}" echo "--------> DOCKER_TAG_SUFFIX=${TAG_SUFFIX}" -[ ! -f /etc/docker/daemon.json ] && sudo tee /etc/docker/daemon.json > /dev/null <<< '{}' -jq '.experimental=true | ."data-root"="/mnt/docker"' /etc/docker/daemon.json > /tmp/daemon.json && sudo mv /tmp/daemon.json /etc/docker/ -( sudo service docker restart || true ) && cat /etc/docker/daemon.json && docker info build_image() { echo "$@" ; - IMG=$1; TAG=$2; FILE=$3; shift 3; VER=$(date +%Y.%m%d.%H%M)${TAG_SUFFIX}; WORKDIR="$(pwd)"; - docker build --compress --force-rm=true -t "${IMG_PREFIX_DST}/${IMG}:${TAG}" -f "$FILE" --build-arg "BASE_NAMESPACE=${IMG_PREFIX_SRC}" "$@" "${WORKDIR}" ; - docker tag "${IMG_PREFIX_DST}/${IMG}:${TAG}" "${IMG_PREFIX_DST}/${IMG}:${VER}" ; + IMG=$1; TAG=$2; FILE=$3; shift 3; VER=$(date +%Y.%m%d.%H%M)${TAG_SUFFIX}; WORKDIR="$(dirname $FILE)"; + docker build --compress --force-rm=true -t "${IMG_PREFIX_DST}/${IMG}:${TAG}" -f "$FILE" --build-arg "BASE_NAMESPACE=${IMG_PREFIX_SRC}" "$@" "${WORKDIR}" + docker tag "${IMG_PREFIX_DST}/${IMG}:${TAG}" "${IMG_PREFIX_DST}/${IMG}:${VER}" + echo "${IMG_PREFIX_DST}/${IMG}:${TAG}" +} + +build_image_no_tag() { + echo "$@" ; + IMG=$1; TAG=$2; FILE=$3; shift 3; WORKDIR="$(dirname $FILE)"; + docker build --compress --force-rm=true -t "${IMG_PREFIX_DST}/${IMG}:${TAG}" -f "$FILE" --build-arg "BASE_NAMESPACE=${IMG_PREFIX_SRC}" "$@" "${WORKDIR}" + echo "${IMG_PREFIX_DST}/${IMG}:${TAG}" +} + +alias_image() { + IMG_1=$1; TAG_1=$2; IMG_2=$3; TAG_2=$4; shift 4; VER=$(date +%Y.%m%d.%H%M)${TAG_SUFFIX}; + docker tag "${IMG_PREFIX_DST}/${IMG_1}:${TAG_1}" "${IMG_PREFIX_DST}/${IMG_2}:${TAG_2}" + docker tag "${IMG_PREFIX_DST}/${IMG_2}:${TAG_2}" "${IMG_PREFIX_DST}/${IMG_2}:${VER}" } push_image() { KEYWORD="${1:-second}"; docker image prune --force && docker images | sort; - IMAGES=$(docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}" | grep "${KEYWORD}" | awk '{print $1 ":" $2}') ; + IMAGES=$(docker images --format "{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}" | grep "${KEYWORD}" | awk '{print $1 ":" $2}') ; + [ -n "${IMAGES}" ] || { echo "!! No images matched keyword: ${KEYWORD}"; return 1; } echo "$DOCKER_REGISTRY_PASSWORD" | docker login "${REGISTRY_DST}" -u "$DOCKER_REGISTRY_USERNAME" --password-stdin ; for IMG in $(echo "${IMAGES}" | tr " " "\n") ; do @@ -48,24 +63,37 @@ push_image() { } clear_images() { - KEYWORD=${1:-'days ago\|weeks ago\|months ago\|years ago'}; # if no keyword is provided, clear all images build days ago + KEYWORD=${1:-'days ago\|weeks ago\|months ago\|years ago'}; # if no keyword is provided, clear all images built days ago IMGS_1=$(docker images | grep "${KEYWORD}" | awk '{print $1 ":" $2}') ; IMGS_2=$(docker images | grep "${KEYWORD}" | awk '{print $3}') ; for IMG in $(echo "$IMGS_1 $IMGS_2" | tr " " "\n") ; do - docker rmi "${IMG}" || true; status=$?; echo "[${status}] image removed > ${IMG}"; + docker rmi "${IMG}" ; status=$?; echo "[${status}] image removed > ${IMG}"; done docker image prune --force && docker images ; } remove_folder() { - sudo du -h -d1 "$1" || true ; - sudo rm -rf "$1" || true ; + for dir in "$@"; do + if [ -d "$dir" ]; then + echo "Removing folder: $dir" ; + sudo du -h -d1 "$dir" || true ; + sudo rm -rf "$dir" || true ; + else + echo "Warn: directory not found: $dir" ; + fi + done } free_diskspace() { - remove_folder /usr/share/dotnet - remove_folder /usr/local/lib/android - df -h + remove_folder /usr/share/dotnet ; # /usr/local/lib/android /var/lib/docker + df -h ; +} + +setup_github_actions() { + [ ! -f /etc/docker/daemon.json ] && sudo tee /etc/docker/daemon.json > /dev/null <<< '{}' ; + jq '.experimental=true | ."data-root"="/mnt/docker"' /etc/docker/daemon.json > /tmp/daemon.json && sudo mv /tmp/daemon.json /etc/docker/ ; + ( sudo service docker restart || true ) && cat /etc/docker/daemon.json && docker info ; } +[ ${GITHUB_ACTIONS:-"false"} = "true" ] && echo "Running in GitHub Actions and Setup Env: $(setup_github_actions)" || echo "Not running in GitHub Action." ; From 4bd9d647790e30b84bd09786aef85dd058c3bf39 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 02:46:58 +0800 Subject: [PATCH 17/24] debug build --- .github/workflows/build-docker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 4af4f06..07fdd69 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -60,7 +60,7 @@ jobs: - run: | source ./tool/tool.sh build_image labnow-open-dev latest ./src/labnow-open.Dockerfile \ - --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=labnow/developer:latest" + --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=developer:latest" push_image labnow-open-dev job-data-science: @@ -72,7 +72,7 @@ jobs: - run: | source ./tool/tool.sh build_image labnow-open-data-science latest ./src/labnow-open.Dockerfile \ - --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=labnow/data-science-dev:latest" + --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=data-science-dev:latest" push_image labnow-open-data-science job-openclaw: @@ -84,7 +84,7 @@ jobs: - run: | source ./tool/tool.sh build_image labnow-open-openclaw latest ./src/labnow-open.Dockerfile \ - --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=labnow/openclaw:latest" + --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=openclaw:latest" push_image labnow-open-openclaw From 2469a23936e1ceeb1f65090dd19031ef26fbe479 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 02:51:13 +0800 Subject: [PATCH 18/24] debug build --- src/labnow-open.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index 3e94d02..b82bbe6 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -10,8 +10,8 @@ ARG PROFILE_LOCALIZE="aliyun-pub" ENV PROFILE_LOCALIZE=${PROFILE_LOCALIZE} -COPY ./src/labnow-open-web /tmp/labnow-open-web -COPY ./src/labnow-open-etc /opt/labnow-open/etc +COPY ./labnow-open-web /tmp/labnow-open-web +COPY ./labnow-open-etc /opt/labnow-open/etc RUN set -eux \ && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && source /opt/utils/script-setup-core.sh && setup_node_pnpm 11 \ From b8c22b9e7c80e93a404da01c1068d3e2bbd57e0e Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 02:59:12 +0800 Subject: [PATCH 19/24] fix Caddyfile --- src/labnow-open-etc/Caddyfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index ae204fa..aa40963 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -69,7 +69,9 @@ handle /* { # Proxy all other requests to a default backend server on port 8888 for jupyter - reverse_proxy localhost:8888 localhost:9001 - lb_policy first + reverse_proxy { + to localhost:8888 localhost:9001 + lb_policy first + } } } From cc707a089d0ed7317be08222ca632185115d98d3 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 13:02:20 +0800 Subject: [PATCH 20/24] fix /api --- src/labnow-open-etc/Caddyfile | 14 +++++++++----- src/labnow-open.Dockerfile | 5 +++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index aa40963..c76cdc8 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -67,11 +67,15 @@ reverse_proxy localhost:18789 } + # respond 200 OK to /api + @path_user_api_healthcheck { + path {$URL_PREFIX:}/api + } + handle @path_user_api_healthcheck { + respond "OK" 200 + } - handle /* { # Proxy all other requests to a default backend server on port 8888 for jupyter - reverse_proxy { - to localhost:8888 localhost:9001 - lb_policy first - } + handle /* { # Proxy all other requests to a default backend server on port 8888 + reverse_proxy localhost:8888 } } diff --git a/src/labnow-open.Dockerfile b/src/labnow-open.Dockerfile index b82bbe6..b240fe6 100644 --- a/src/labnow-open.Dockerfile +++ b/src/labnow-open.Dockerfile @@ -45,8 +45,9 @@ RUN set -eux && source /opt/utils/script-localize.sh ${PROFILE_LOCALIZE} \ && (type code-server && printf "[program:vscode]\ncommand=/usr/local/bin/start-code-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || true) \ && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || true) \ - && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\n" >> /etc/supervisord/supervisord.conf || true) \ - && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\n" >> /etc/supervisord/supervisord.conf || true) \ + ## optional agent components: openclaw and hermes + && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ + && (type hermes && printf "[program:hermes]\ncommand=/usr/local/bin/start-hermes.sh\nautostart=true\n" >> /etc/supervisord/supervisord.conf || true) \ # cleanup of any temporary or cache files to keep the image size down && source /opt/utils/script-utils.sh && install__clean From 345311a975a307ed50a92ef3d592a49051c6f718 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 13:27:54 +0800 Subject: [PATCH 21/24] fix healthcheck api --- src/labnow-open-etc/Caddyfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index c76cdc8..aeefddd 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -67,9 +67,9 @@ reverse_proxy localhost:18789 } - # respond 200 OK to /api + # respond 200 OK to /api for healthcheck @path_user_api_healthcheck { - path {$URL_PREFIX:}/api + path {$URL_PREFIX:}api } handle @path_user_api_healthcheck { respond "OK" 200 From 9f4fbb9f81cea086078e35cac32fc3a9e396b747 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 13:49:06 +0800 Subject: [PATCH 22/24] openclaw slash prefix --- src/labnow-open-etc/Caddyfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index aeefddd..108174d 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -66,6 +66,8 @@ uri strip_prefix {$URL_PREFIX:}openclaw reverse_proxy localhost:18789 } + @path_user_openclaw_no_slash path_regexp ^{$URL_PREFIX:}openclaw$ + redir @path_user_openclaw_no_slash {path}/ 308 # Redirect paths ending in "openclaw" to "openclaw/" # respond 200 OK to /api for healthcheck @path_user_api_healthcheck { From c9b024622c6deb2e8f7ce388af33c739da29c536 Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 16:34:30 +0800 Subject: [PATCH 23/24] debug openclaw websocket --- src/labnow-open-etc/Caddyfile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index 108174d..6274e6a 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -61,13 +61,19 @@ redir @path_user_rshiny_no_slash {path}/ 308 # Redirect paths ending in "rshiny" to "rshiny/" + # redirect http requests of no-slash without `Connection: *Upgrade*` header WebSocket + @path_user_openclaw_no_slash { + path_regexp ^{$URL_PREFIX:}openclaw$ + not header Connection *Upgrade* + } + redir @path_user_openclaw_no_slash {path}/ 308 + @path_user_openclaw path_regexp ^{$URL_PREFIX:}openclaw(/.*)?$ - handle @path_user_openclaw { # Proxy requests to openclaw, strip prefix + handle @path_user_openclaw { uri strip_prefix {$URL_PREFIX:}openclaw reverse_proxy localhost:18789 } - @path_user_openclaw_no_slash path_regexp ^{$URL_PREFIX:}openclaw$ - redir @path_user_openclaw_no_slash {path}/ 308 # Redirect paths ending in "openclaw" to "openclaw/" + # respond 200 OK to /api for healthcheck @path_user_api_healthcheck { From f79913421b285b5499e1234d0fdcfe0040a43f3e Mon Sep 17 00:00:00 2001 From: Bibo Hao Date: Tue, 7 Jul 2026 19:10:49 +0800 Subject: [PATCH 24/24] update Caddyfile --- .github/workflows/build-docker.yml | 4 +- src/labnow-open-etc/Caddyfile | 91 +++++++++++++++++------------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 07fdd69..e14c021 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -32,7 +32,7 @@ env: COMMIT_SHA: ${{ github.sha }} # COMMIT_SHA="$(git rev-parse --short HEAD)" jobs: - semgrep: + job-semgrep: runs-on: ubuntu-latest permissions: {"contents": "read", "pull-requests": "write"} container: @@ -89,7 +89,7 @@ jobs: ## Sync all images in this build (listed by "names") to mirror registry. - sync_images: + job-sync-images: needs: ["job-developer", "job-data-science", "job-openclaw"] runs-on: ubuntu-latest permissions: {"contents": "read", "pull-requests": "write"} diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index 6274e6a..3b36d95 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -1,5 +1,5 @@ :80 { - log { # Enable access logging for all requests + log { output file /dev/null { roll_size 10mb roll_keep 5 @@ -8,82 +8,97 @@ format json } - # Add a global header to all responses + # Global response headers header { - -Server - Location-Base {$URL_PREFIX:} + -Server + Location-Base {$URL_PREFIX:} } - # Redirect the root path to the user home page - @root path_regexp root ^{$URL_PREFIX:}/?$ + # Redirect the application root to the home page + @root path {$URL_PREFIX:} {$URL_PREFIX:}/ redir @root {$URL_PREFIX:}home 307 - # Handle user home paths - @path_user_home path_regexp ^{$URL_PREFIX}home/.*$ + # Home + @path_user_home path {$URL_PREFIX:}home/* handle @path_user_home { uri strip_prefix {$URL_PREFIX:}home reverse_proxy localhost:9001 - # root * /opt/labnow-open/web - # file_server } - @path_user_home_no_slash path_regexp ^{$URL_PREFIX:}home$ - redir @path_user_home_no_slash {path}/ 308 + @path_user_home_no_slash path {$URL_PREFIX:}home + redir @path_user_home_no_slash {path}/ 308 - @path_user_vscode path_regexp ^{$URL_PREFIX:}vscode/.*$ - handle @path_user_vscode { # Proxy requests to vscode, strip prefix + # VS Code + @path_user_vscode path {$URL_PREFIX:}vscode/* + handle @path_user_vscode { uri strip_prefix {$URL_PREFIX:}vscode reverse_proxy localhost:9999 } - @path_user_vscode_no_slash path_regexp ^{$URL_PREFIX:}vscode$ - redir @path_user_vscode_no_slash {path}/ 308 # Redirect paths ending in "vscode" to "vscode/" + @path_user_vscode_no_slash path {$URL_PREFIX:}vscode + redir @path_user_vscode_no_slash {path}/ 308 - - @path_user_rserver path_regexp ^{$URL_PREFIX:}rserver/.*$ - handle @path_user_rserver { # Proxy requests to rserver, strip prefix + # RStudio Server + @path_user_rserver path {$URL_PREFIX:}rserver/* + handle @path_user_rserver { uri strip_prefix {$URL_PREFIX:}rserver reverse_proxy localhost:8787 { - header_up -X-Forwarded-Port + header_up -X-Forwarded-Port } } - @path_user_rserver_no_slash path_regexp ^{$URL_PREFIX:}rserver$ - redir @path_user_rserver_no_slash {path}/ 308 # Redirect paths ending in "rserver" to "rserver/" - + @path_user_rserver_no_slash path {$URL_PREFIX:}rserver + redir @path_user_rserver_no_slash {path}/ 308 - @path_user_rshiny path_regexp ^{$URL_PREFIX:}rshiny/.*$ - handle @path_user_rshiny { # Proxy requests to rshiny, strip prefix + # Shiny Server + @path_user_rshiny path {$URL_PREFIX:}rshiny/* + handle @path_user_rshiny { uri strip_prefix {$URL_PREFIX:}rshiny reverse_proxy localhost:3838 { - header_up -X-Forwarded-Port + header_up -X-Forwarded-Port } } - @path_user_rshiny_no_slash path_regexp ^{$URL_PREFIX:}rshiny$ - redir @path_user_rshiny_no_slash {path}/ 308 # Redirect paths ending in "rshiny" to "rshiny/" + @path_user_rshiny_no_slash path {$URL_PREFIX:}rshiny + redir @path_user_rshiny_no_slash {path}/ 308 - - # redirect http requests of no-slash without `Connection: *Upgrade*` header WebSocket + # OpenClaw + # Redirect non-WebSocket requests to the canonical trailing-slash URL @path_user_openclaw_no_slash { - path_regexp ^{$URL_PREFIX:}openclaw$ + path {$URL_PREFIX:}openclaw not header Connection *Upgrade* } redir @path_user_openclaw_no_slash {path}/ 308 - @path_user_openclaw path_regexp ^{$URL_PREFIX:}openclaw(/.*)?$ + @path_user_openclaw path {$URL_PREFIX:}openclaw/* handle @path_user_openclaw { uri strip_prefix {$URL_PREFIX:}openclaw reverse_proxy localhost:18789 } + # Hermes + @path_user_hermes_no_slash path {$URL_PREFIX:}hermes + redir @path_user_hermes_no_slash {path}/ 308 - # respond 200 OK to /api for healthcheck - @path_user_api_healthcheck { - path {$URL_PREFIX:}api + @path_user_hermes path {$URL_PREFIX:}hermes/* + handle @path_user_hermes { + uri strip_prefix {$URL_PREFIX:}hermes + reverse_proxy localhost:9119 } - handle @path_user_api_healthcheck { - respond "OK" 200 + + # Selkies + @path_user_selkies_no_slash path {$URL_PREFIX:}selkies + redir @path_user_selkies_no_slash {path}/ 308 + + @path_user_selkies path {$URL_PREFIX:}selkies/* + handle @path_user_selkies { + uri strip_prefix {$URL_PREFIX:}selkies + reverse_proxy localhost:8080 } - handle /* { # Proxy all other requests to a default backend server on port 8888 + # Health check + @path_user_api_healthcheck path {$URL_PREFIX:}api + respond @path_user_api_healthcheck "OK" 200 + + # Default backend + handle { reverse_proxy localhost:8888 } }