diff --git a/.agents/skills b/.agents/skills new file mode 120000 index 0000000..cd2ebc5 --- /dev/null +++ b/.agents/skills @@ -0,0 +1 @@ +../doc/skills \ No newline at end of file diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000..cd2ebc5 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../doc/skills \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 120000 index 0000000..be77ac8 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/.github/skills b/.github/skills new file mode 120000 index 0000000..cd2ebc5 --- /dev/null +++ b/.github/skills @@ -0,0 +1 @@ +../doc/skills \ No newline at end of file diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 7fe251c..22716f6 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -51,7 +51,8 @@ jobs: --config p/javascript \ --config p/react \ --config p/owasp-top-ten \ - --exclude-rule=dockerfile.security.missing-user.missing-user + --exclude-rule=dockerfile.security.missing-user.missing-user \ + --exclude-rule=yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag job-developer: name: "labnow-open-dev" @@ -86,10 +87,21 @@ jobs: --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=openclaw:latest" push_image labnow-open-openclaw + job-hermes: + name: "labnow-open-hermes" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: | + source ./tool/tool.sh + build_image labnow-open-hermes latest ./src/labnow-open.Dockerfile \ + --build-arg "PROFILE_LOCALIZE=default" --build-arg "BASE_IMG=hermes:latest" + push_image labnow-open-hermes + ## Sync all images in this build (listed by "names") to mirror registry. job-sync-images: - needs: ["job-developer", "job-data-science", "job-openclaw"] + needs: ["job-developer", "job-data-science", "job-openclaw", "job-hermes"] runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 diff --git a/.gitignore b/.gitignore index 531f1b1..4a49d3e 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,6 @@ dmypy.json # Yarn cache .yarn/ + +# Local project documentation +doc/dev/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e69de29 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index 9695058..58dcf63 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Visit Images on Quay.io](https://img.shields.io/badge/Quay.io-Images-green)](https://quay.io/organization/labnow) [![Visit Images on DockerHub](https://img.shields.io/badge/DockerHub-Images-green)](https://hub.docker.com/u/LabNow) -Please generously STAR★ our project or donate to us! +Please generously STAR⭐️ our project or donate to us! [![GitHub Stars](https://img.shields.io/github/stars/LabNow-ai/labnow-open.svg?label=Stars)](https://github.com/LabNow-ai/labnow-open/stargazers) Discussion and contributions are welcome: @@ -14,20 +14,39 @@ Discussion and contributions are welcome: [![Open an Issue on GitHub](https://img.shields.io/github/issues/LabNow-ai/labnow-open)](https://github.com/LabNow-ai/labnow-open/issues) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/LabNow-ai/labnow-open) +> 中文文档:[doc/README-cn.md](doc/README-cn.md) + --- -`LabNow Open` 是一个开源的容器化 AI/Data 工作空间项目,目标是让你用尽可能少的环境配置,快速得到可用的研发环境与工具门户。 +LabNow Open is an open-source, containerized AI/data science workspace. It bundles a lightweight **Web Console** that lets you manage and launch common data science services — all from a single browser tab, with minimal setup. + +## What's Included + +- **JupyterLab** — interactive notebooks and data exploration +- **VS Code** (code-server) — full-featured code editor in the browser +- **RStudio Server** — R development environment +- **Shiny Server** — interactive R web applications +- **Hermes** — gateway and dashboard for AI tool orchestration +- **OpenClaw** — AI agent platform + +All services run behind a unified **Caddy** reverse proxy on port 80, managed by **supervisord** for process lifecycle control. + +## Image Variants + +All variants are built from a single [Dockerfile](src/labnow-open.Dockerfile) and differ only in which base image they layer on top of: -项目内置了一个轻量 Web Console,用于管理和跳转常用数据科学服务,包含: +| Image Tag | Base Image | What It Contains | +|---|---|---| +| `labnow-open-dev` | `developer:latest` | Minimal dev tools + Web Console | +| `labnow-open-data-science` | `data-science-dev:latest` | Full data science stack (Python, R, Jupyter) | +| `labnow-open-hermes` | `hermes:latest` | Hermes gateway + dashboard | +| `labnow-open-openclaw` | `openclaw:latest` | OpenClaw AI agent platform | -- JupyterLab -- VS Code (code-server) -- RStudio Server -- Shiny Server +Images are published on both [Quay.io](https://quay.io/organization/labnow) and [Docker Hub](https://hub.docker.com/u/LabNow). -## 快速开始(直接使用现成镜像) +## Quick Start -如果你只想快速体验,建议直接运行镜像: +Run the full data science variant with a single command: ```bash docker run --rm -it \ @@ -36,4 +55,17 @@ docker run --rm -it \ quay.io/labnow/labnow-open-data-science:latest ``` -启动后访问:`http://localhost:8888/` +Then open **http://localhost:8888/** in your browser. The Web Console will show all available services — click any card to launch it. + +### Other Variants + +```bash +# Minimal developer image +docker run --rm -it -p 8888:80 quay.io/labnow/labnow-open-dev:latest + +# With Hermes gateway +docker run --rm -it -p 8888:80 quay.io/labnow/labnow-open-hermes:latest + +# With OpenClaw agent platform +docker run --rm -it -p 8888:80 quay.io/labnow/labnow-open-openclaw:latest +``` diff --git a/doc/README-cn.md b/doc/README-cn.md new file mode 100644 index 0000000..550b0e2 --- /dev/null +++ b/doc/README-cn.md @@ -0,0 +1,76 @@ +# LabNow Open + +[![License](https://img.shields.io/badge/License-BSD%203--Clause-green.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/LabNow-ai/labnow-open/build-docker.yml?branch=main)](https://github.com/LabNow-ai/labnow-open/actions) +[![GitHub Stars](https://img.shields.io/github/stars/LabNow-ai/labnow-open.svg?label=Stars)](https://github.com/LabNow-ai/labnow-open/stargazers) + +> English docs: [README.md](../README.md) + +--- + +`LabNow Open` 是一个开源的容器化 AI/数据科学工作空间项目。目标是让你用尽可能少的环境配置,快速得到可用的研发环境与统一工具门户。 + +项目内置一个轻量 **Web Console**(React SPA),通过卡片式界面管理和跳转常用数据科学服务。所有服务由 **Caddy** 统一反向代理,由 **supervisord** 负责进程生命周期管理。 + +## 集成的服务 + +| 服务 | 说明 | Web 路径 | +|---|---|---| +| **JupyterLab** | 交互式 Notebook 与数据探索 | `/lab/` | +| **VS Code** (code-server) | 浏览器中的完整代码编辑器 | `/vscode/` | +| **RStudio Server** | R 语言开发环境 | `/rserver/` | +| **Shiny Server** | R Shiny 交互式 Web 应用 | `/rshiny/` | +| **Hermes** | AI 工具网关与仪表盘 | `/hermes/` | +| **OpenClaw** | AI Agent 平台 | `/openclaw/` | + + +## 镜像变体 + +所有变体由同一个 [Dockerfile](../src/labnow-open.Dockerfile) 构建,区别仅在于所基于的基础镜像不同: + +| 镜像标签 | 基础镜像 | 包含内容 | +|---|---|---| +| `labnow-open-dev` | `developer:latest` | 精简开发工具 + Web Console | +| `labnow-open-data-science` | `data-science-dev:latest` | 完整数据科学栈(Python, R, Jupyter) | +| `labnow-open-hermes` | `hermes:latest` | Hermes 网关 + 仪表盘 | +| `labnow-open-openclaw` | `openclaw:latest` | OpenClaw AI Agent 平台 | + +镜像发布在 [Quay.io](https://quay.io/organization/labnow) 和 [Docker Hub](https://hub.docker.com/u/LabNow)。 + +## 快速开始 + +一条命令启动完整数据科学环境: + +```bash +docker run --rm -it \ + --name labnow-open \ + -p 8888:80 \ + quay.io/labnow/labnow-open-data-science:latest +``` + +浏览器访问 **http://localhost:8888/**,Web Console 会列出所有可用服务,点击卡片即可启动。 + +### 其他变体 + +```bash +# 精简开发者镜像 +docker run --rm -it -p 8888:80 quay.io/labnow/labnow-open-dev:latest + +# 带 Hermes 网关 +docker run --rm -it -p 8888:80 quay.io/labnow/labnow-open-hermes:latest + +# 带 OpenClaw Agent 平台 +docker run --rm -it -p 8888:80 quay.io/labnow/labnow-open-openclaw:latest +``` + + +## 开发与 CI/CD + +详细的本地开发指南与 CI/CD 流程说明见:[doc/README-dev.md](README-dev.md) + +## 参与贡献 + +欢迎提交 Issue 和 Pull Request,也可以通过以下方式参与讨论: + +- [![Join Discord Chat](https://img.shields.io/badge/Discuss_on-Discord-green)](https://discord.gg/kHUzgQxgbJ) +- [![Open an Issue](https://img.shields.io/github/issues/LabNow-ai/labnow-open)](https://github.com/LabNow-ai/labnow-open/issues) diff --git a/doc/README-dev.md b/doc/README-dev.md index 8942e4a..4459cf3 100644 --- a/doc/README-dev.md +++ b/doc/README-dev.md @@ -1,18 +1,76 @@ -# Develop with the `labnow/developer` image +# Development Guide -## Step 1: Start the developemnt environment +## Architecture -Start the development container and enter into the container. +``` +Browser (:80) + │ + v +┌──────────────────────────────────────────────┐ +│ Caddy (reverse proxy) │ +│ Serves static Console UI + routes traffic │ +│ /home/* → supervisord RPC (:9001) │ +│ /lab/* → JupyterLab (:8888) │ +│ /vscode/* → code-server (:9999) │ +│ /rserver/* → RStudio Server (:8787) │ +│ /rshiny/* → Shiny Server (:3838) │ +│ /hermes/* → Hermes Dashboard (:9119) │ +│ /openclaw/* → OpenClaw (:18789) │ +└──────────────────────────────────────────────┘ + │ + v +┌──────────────────────────────────────────────┐ +│ supervisord (process manager) │ +│ Auto-detects available tools and manages │ +│ their start/stop/restart lifecycle │ +└──────────────────────────────────────────────┘ + │ + v +┌──────────────────────────────────────────────┐ +│ React SPA (Web Console) │ +│ Carbon Design System, Vite 8, React 19 │ +│ Card-based UI for service management │ +└──────────────────────────────────────────────┘ +``` + +## Project Structure + +``` +labnow-open/ +├── src/ +│ ├── labnow-open.Dockerfile # Multi-stage build (builder + runtime) +│ ├── labnow-open-web/ # React 19 + Vite + Carbon Design System +│ │ ├── src/App.jsx # Console UI with service card grid +│ │ ├── src/api/ # supervisord REST API client +│ │ ├── src/hooks/ # React hooks for state management +│ │ └── public/ # Static assets and logos +│ └── labnow-open-etc/ # Runtime configuration +│ ├── supervisord.conf # Process manager configuration +│ ├── Caddyfile # Reverse proxy routes +│ └── CaddyRoutes/ # Per-service route snippets +├── tool/ +│ ├── tool.sh # CI helper: build/push Docker images +│ └── cicd/ # Dev & staging Docker Compose files +└── .github/workflows/ + └── build-docker.yml # CI/CD: semgrep SAST, multi-variant builds +``` + +## Getting Started + +### Method 1: Using run-dev.sh (Recommended) + +Start the development container and enter into it: ```bash -# method 1: ./tool/cicd/run-dev.sh up ./tool/cicd/run-dev.sh enter +``` -# method 2: +### Method 2: Manual Docker Run + +```bash docker run -d \ --name dev-labnow-open \ - --container-name=dev-labnow-open \ -p 8888:80 -p 3000:3000 \ -v $(pwd):/root/ \ quay.io/labnow/data-science-dev \ @@ -21,28 +79,66 @@ docker run -d \ docker exec -it dev-labnow-open bash ``` -## Step 2: Update configs and start the supervisord +## Running the App in Dev Mode -```bash -mkdir -pv /etc/supervisord && ln -sf /root/src/labnow-open-etc/supervisord.conf /etc/supervisord/ -mkdir -pv /etc/caddy && ln -sf /root/src/labnow-open-etc/Caddyfile /etc/caddy/ +Inside the container, link configs and start services: +```bash +mkdir -pv /etc/supervisord && ln -sf /root/src/labnow-open-etc/supervisord.conf /etc/supervisord/ +mkdir -pv /etc/caddy && ln -sf /root/src/labnow-open-etc/Caddyfile /etc/caddy/ export STATIC_DIR=/root/src/labnow-open-web/dist && start-supervisord.sh ``` -## Step 3: Access the service from the browser +Then access: **http://localhost:8888/** -http://localhost:8888/ +## Standalone Frontend Development -## Step 4: Build the image and run the built image +Develop the React frontend independently (without Docker): ```bash -docker build -t quay.io/labnow-dev/labnow-open-dev:latest \ - -f src/labnow-open.Dockerfile \ - --build-arg PROFILE_LOCALIZE=aliyun-pub . +cd src/labnow-open-web +pnpm install +pnpm run dev # Dev server on http://localhost:3000 +pnpm run build # Production build to dist/ +``` +In dev mode, Vite proxies `/api` requests to `localhost:80`, so you can run the frontend standalone against a running backend. + +## Building Docker Images Locally + +```bash +docker build -t labnow-open-dev:latest \ + -f src/labnow-open.Dockerfile \ + --build-arg PROFILE_LOCALIZE=aliyun-pub . +``` + +Run the built image: + +```bash docker run --rm -it \ --name dev-labnow-open \ -p 8080:80 \ - quay.io/labnow-dev/labnow-open-dev:latest + labnow-open-dev:latest ``` + +## CI/CD + +On push to `main`, pull requests, tag releases (`v*`), and weekly schedules, GitHub Actions: + +1. Runs **semgrep SAST** (static analysis security testing) +2. Builds all four image variants in parallel +3. Pushes to `quay.io/labnow/` and `docker.io/labnow/` +4. Optionally syncs to a mirror registry + +See [.github/workflows/build-docker.yml](../.github/workflows/build-docker.yml) for details. + +### Image Variants + +All variants are built from a single [Dockerfile](../src/labnow-open.Dockerfile) and differ only in which base image they layer on top of: + +| Image Tag | Base Image | What It Contains | +|---|---|---| +| `labnow-open-dev` | `developer:latest` | Minimal dev tools + Web Console | +| `labnow-open-data-science` | `data-science-dev:latest` | Full data science stack (Python, R, Jupyter) | +| `labnow-open-hermes` | `hermes:latest` | Hermes gateway + dashboard | +| `labnow-open-openclaw` | `openclaw:latest` | OpenClaw AI agent platform | diff --git a/src/labnow-open-etc/CaddyRoutes/hermes.caddy b/src/labnow-open-etc/CaddyRoutes/hermes.caddy new file mode 100644 index 0000000..fcedc89 --- /dev/null +++ b/src/labnow-open-etc/CaddyRoutes/hermes.caddy @@ -0,0 +1,15 @@ +# Hermes +@path_user_hermes_no_slash path {$URL_PREFIX:}hermes +redir @path_user_hermes_no_slash {path}/ 308 + +@path_user_hermes path {$URL_PREFIX:}hermes/* +handle @path_user_hermes { + uri strip_prefix {$URL_PREFIX:}hermes + reverse_proxy localhost:9119 +} + +@path_user_hermes_readiness path {$URL_PREFIX:/}api +handle @path_user_hermes_readiness { + rewrite * /api/status + reverse_proxy 127.0.0.1:9119 +} diff --git a/src/labnow-open-etc/CaddyRoutes/openclaw.caddy b/src/labnow-open-etc/CaddyRoutes/openclaw.caddy new file mode 100644 index 0000000..50565c2 --- /dev/null +++ b/src/labnow-open-etc/CaddyRoutes/openclaw.caddy @@ -0,0 +1,13 @@ +# OpenClaw +# Redirect non-WebSocket requests to the canonical trailing-slash URL +@path_user_openclaw_no_slash { + path {$URL_PREFIX:}openclaw + not header Connection *Upgrade* +} +redir @path_user_openclaw_no_slash {path}/ 308 + +@path_user_openclaw path {$URL_PREFIX:}openclaw/* +handle @path_user_openclaw { + uri strip_prefix {$URL_PREFIX:}openclaw + reverse_proxy localhost:18789 +} diff --git a/src/labnow-open-etc/CaddyRoutes/rserver.caddy b/src/labnow-open-etc/CaddyRoutes/rserver.caddy new file mode 100644 index 0000000..0d78e43 --- /dev/null +++ b/src/labnow-open-etc/CaddyRoutes/rserver.caddy @@ -0,0 +1,10 @@ +# 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 + } +} +@path_user_rserver_no_slash path {$URL_PREFIX:}rserver +redir @path_user_rserver_no_slash {path}/ 308 diff --git a/src/labnow-open-etc/CaddyRoutes/selkies.caddy b/src/labnow-open-etc/CaddyRoutes/selkies.caddy new file mode 100644 index 0000000..5377774 --- /dev/null +++ b/src/labnow-open-etc/CaddyRoutes/selkies.caddy @@ -0,0 +1,9 @@ +# 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 +} diff --git a/src/labnow-open-etc/CaddyRoutes/shiny.caddy b/src/labnow-open-etc/CaddyRoutes/shiny.caddy new file mode 100644 index 0000000..44b7859 --- /dev/null +++ b/src/labnow-open-etc/CaddyRoutes/shiny.caddy @@ -0,0 +1,10 @@ +# 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 + } +} +@path_user_rshiny_no_slash path {$URL_PREFIX:}rshiny +redir @path_user_rshiny_no_slash {path}/ 308 diff --git a/src/labnow-open-etc/CaddyRoutes/vscode.caddy b/src/labnow-open-etc/CaddyRoutes/vscode.caddy new file mode 100644 index 0000000..5f4dea7 --- /dev/null +++ b/src/labnow-open-etc/CaddyRoutes/vscode.caddy @@ -0,0 +1,8 @@ +# 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 {$URL_PREFIX:}vscode +redir @path_user_vscode_no_slash {path}/ 308 diff --git a/src/labnow-open-etc/Caddyfile b/src/labnow-open-etc/Caddyfile index 0a2a26a..90df8a6 100644 --- a/src/labnow-open-etc/Caddyfile +++ b/src/labnow-open-etc/Caddyfile @@ -24,78 +24,13 @@ @path_user_home_no_slash path {$URL_PREFIX:}home redir @path_user_home_no_slash {path}/ 308 - # 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 {$URL_PREFIX:}vscode - redir @path_user_vscode_no_slash {path}/ 308 - - # 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 - } - } - @path_user_rserver_no_slash path {$URL_PREFIX:}rserver - redir @path_user_rserver_no_slash {path}/ 308 - - # 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 - } - } - @path_user_rshiny_no_slash path {$URL_PREFIX:}rshiny - redir @path_user_rshiny_no_slash {path}/ 308 - - # OpenClaw - # Redirect non-WebSocket requests to the canonical trailing-slash URL - @path_user_openclaw_no_slash { - path {$URL_PREFIX:}openclaw - not header Connection *Upgrade* - } - redir @path_user_openclaw_no_slash {path}/ 308 - - @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 - - @path_user_hermes path {$URL_PREFIX:}hermes/* - handle @path_user_hermes { - uri strip_prefix {$URL_PREFIX:}hermes - reverse_proxy localhost:9119 - } - - # 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 - } - - # Health check - handle_path {$URL_PREFIX:}api { + handle_path {$URL_PREFIX:}api { # default healthcheck endpoint respond "OK" 200 } - # Default backend - handle { + handle { # Proxy all other requests to a default backend server on port 8888 reverse_proxy localhost:8888 } + + import /etc/caddy/enabled-routes/*.caddy } diff --git a/src/labnow-open-web/src/App.jsx b/src/labnow-open-web/src/App.jsx index 2c3fb82..26ae2a9 100644 --- a/src/labnow-open-web/src/App.jsx +++ b/src/labnow-open-web/src/App.jsx @@ -33,41 +33,46 @@ import { useSupervisorController } from "./hooks/useSupervisorController"; import { buildHomePath, buildWorkspacePath } from "./utils/runtimeBase"; const PROGRAM_DEFINITIONS = { - caddy: { displayName: "Caddy Server", hidden: true }, - jupyter: { + "caddy": { displayName: "Caddy Server", hidden: true }, + "jupyter": { displayName: "JupyterLab", - link: "/lab", + link: "/lab/", logo: "logo-jupyter.svg", - description: - "Interactive notebooks for Python and data workflows. Great for exploration and quick experiments.", + description: "Interactive notebooks for Python and data workflows. Great for exploration and quick experiments." }, - vscode: { + "vscode": { displayName: "VS Code", - link: "/vscode", + link: "/vscode/", logo: "logo-vscode.svg", - description: - "A full-featured code editor in your browser. Edit files, run terminals, and manage projects.", + description: "A full-featured code editor in your browser. Edit files, run terminals, and manage projects." }, - rserver: { + "rserver": { displayName: "R-Studio", - link: "/rserver", + link: "/rserver/", logo: "logo-rserver.svg", - description: - "RStudio Server for R development and analysis. Build scripts, run models, and visualize results.", + description: "RStudio Server for R development and analysis. Build scripts, run models, and visualize results." }, - rshiny: { + "rshiny": { displayName: "R Shiny", - link: "/rshiny", + link: "/rshiny/", logo: "logo-rshiny.svg", - description: - "Run Shiny applications for interactive dashboards. Useful for sharing data apps with your team.", + description: "Run Shiny applications for interactive dashboards. Useful for sharing data apps with your team." }, - openclaw: { + "hermes-gateway": { + displayName: "Hermes Gateway", + hidden: true, + }, + "hermes-dashboard": { + displayName: "Hermes", + link: "/hermes/", + logo: "logo-hermes.svg", + description: "An agent workspace for managing sessions, skills, configurations, and plans." + }, + "openclaw": { displayName: "OpenClaw", link: "/openclaw/", logo: "logo-openclaw.svg", - description: - "OpenClaw AI Agent platform. Build and deploy intelligent agents with natural language.", + description: "OpenClaw AI Agent platform. Build and deploy intelligent agents with natural language." }, }; @@ -466,7 +471,7 @@ export default function App() { title="labnow.ai" > - LabNow.ai + LabNow® /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 + && chmod +x /usr/local/bin/start-caddy.sh /usr/local/bin/start-supervisord.sh \ && (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) \ - && (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) \ - ## 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) \ + && (type jupyter && printf "[program:jupyter]\ncommand=/usr/local/bin/start-jupyterlab.sh\n" >> /etc/supervisord/supervisord.conf || rm -f /opt/labnow-open/etc/CaddyRoutes/jupyter.caddy ) \ + && (type code-server && printf "[program:vscode]\ncommand=/usr/local/bin/start-code-server.sh\n" >> /etc/supervisord/supervisord.conf || rm -f /opt/labnow-open/etc/CaddyRoutes/vscode.caddy ) \ + && (type rserver && printf "[program:rserver]\ncommand=/usr/local/bin/start-rserver.sh\n" >> /etc/supervisord/supervisord.conf || rm -f /opt/labnow-open/etc/CaddyRoutes/rserver.caddy ) \ + && (type shiny-server && printf "[program:rshiny]\ncommand=/usr/local/bin/start-shiny-server.sh\n" >> /etc/supervisord/supervisord.conf || rm -f /opt/labnow-open/etc/CaddyRoutes/shiny.caddy ) \ + && (type openclaw && printf "[program:openclaw]\ncommand=/usr/local/bin/start-openclaw.sh\n" >> /etc/supervisord/supervisord.conf || rm -f /opt/labnow-open/etc/CaddyRoutes/openclaw.caddy ) \ + && (type hermes && printf "[program:hermes-gateway]\ncommand=/usr/local/bin/start-hermes.sh gateway\nautostart=true\n\n[program:hermes-dashboard]\ncommand=/usr/local/bin/start-hermes.sh dashboard --host 127.0.0.1 --port 9119 --no-open\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 diff --git a/tool/cicd/docker-compose.labnow-open.DEV.yml b/tool/cicd/docker-compose.labnow-open.DEV.yml index 8463dcf..e50539d 100644 --- a/tool/cicd/docker-compose.labnow-open.DEV.yml +++ b/tool/cicd/docker-compose.labnow-open.DEV.yml @@ -14,11 +14,14 @@ services: env_file: ["../credentials/${ENV_PROFILE:-DEV}-labnow-open.env"] user: "0:0" ports: ["${PORT_APP:-20000}:80", "${PORT_WEB:-30000}:3000"] + working_dir: /workspace volumes: - - ../../doc:/root/doc:rw - - ../../src:/root/src:rw - - ../../tool:/root/tool:rw + - ../../:/workspace:rw + - ${PNPM_STORE_PATH:-$HOME/.pnpm-store}:/workspace/.pnpm-store:rw + - ${PNPM_STORE_PATH:-$HOME/.pnpm-store}:/root/.pnpm-store:rw + # AI4Coding tools: - "$HOME/.claude:/root/.claude:rw" - - /data/.pnpm-store:/root/.local/share/pnpm:rw - working_dir: /root/src + - "$HOME/.codex:/root/.codex:rw" + - "$HOME/.copilot:/root/.copilot:rw" + - "$HOME/.gemini:/root/.gemini:rw" command: "tail -f /dev/null"