Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d2b0991f0d |
@@ -79,7 +79,8 @@ If CUDA is already mounted or preinstalled outside `PATH`, the installer now det
|
|||||||
- The host-side install path assumes modern local tooling, but TransformerLab itself is provisioned from a pinned classic single-user layout.
|
- The host-side install path assumes modern local tooling, but TransformerLab itself is provisioned from a pinned classic single-user layout.
|
||||||
- TransformerLab is intentionally pinned to the older single-user `v0.28.2` release because newer upstream releases changed the project structure and behavior in ways that break this courseware.
|
- TransformerLab is intentionally pinned to the older single-user `v0.28.2` release because newer upstream releases changed the project structure and behavior in ways that break this courseware.
|
||||||
- This project does not rely on TransformerLab's upstream `install.sh`; the Ansible role provisions the pinned release directly so web assets, env layout, and runtime behavior stay reproducible.
|
- This project does not rely on TransformerLab's upstream `install.sh`; the Ansible role provisions the pinned release directly so web assets, env layout, and runtime behavior stay reproducible.
|
||||||
- The courseware repairs installed TransformerLab Fastchat plugin manifests so Fastchat-gated features such as Model Architecture and Visualize Logprobs stay available on pinned installs.
|
- The courseware repairs the pinned TransformerLab install for symlink-aware plugin file lookups and refreshes installed Fastchat plugin manifests so Fastchat-gated features such as Model Architecture, activations, and Visualize Logprobs stay available on pinned installs.
|
||||||
|
- The managed default TransformerLab student account is also seeded with the courseware Fastchat plugin plus the starter experiments and model metadata that `labctl up` depends on.
|
||||||
- No Ollama models are pulled during `./labctl up`; students pull models manually as part of the courseware.
|
- No Ollama models are pulled during `./labctl up`; students pull models manually as part of the courseware.
|
||||||
- WhiteRabbitNeo assets are handled separately from `./labctl up` and `./labctl preflight`.
|
- WhiteRabbitNeo assets are handled separately from `./labctl up` and `./labctl preflight`.
|
||||||
- Run `./labctl assets lab2` when you want to populate repo-local lab 2 assets in `assets/lab2/` from Hugging Face.
|
- Run `./labctl assets lab2` when you want to populate repo-local lab 2 assets in `assets/lab2/` from Hugging Face.
|
||||||
@@ -103,25 +104,6 @@ Default endpoints:
|
|||||||
- Unsloth Studio: `http://127.0.0.1:8888`
|
- Unsloth Studio: `http://127.0.0.1:8888`
|
||||||
- Promptfoo UI: `http://127.0.0.1:15500`
|
- Promptfoo UI: `http://127.0.0.1:15500`
|
||||||
- Wiki: `http://127.0.0.1:80`
|
- Wiki: `http://127.0.0.1:80`
|
||||||
- Lab 3 Terminal: `http://127.0.0.1:7681/wetty`
|
|
||||||
|
|
||||||
## Lab 3 Browser Terminal
|
|
||||||
|
|
||||||
Linux and WSL deployments now require a managed `student` password hash before `./labctl up` or `./labctl preflight`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export COURSEWARE_STUDENT_PASSWORD_HASH="$(openssl passwd -6 'student-password')"
|
|
||||||
./labctl up
|
|
||||||
```
|
|
||||||
|
|
||||||
The deployment will:
|
|
||||||
|
|
||||||
- create the managed `student` account
|
|
||||||
- create `/home/student/lab3`
|
|
||||||
- bind `sshd` to `127.0.0.1:22` only
|
|
||||||
- install WeTTY and expose it at `http://127.0.0.1:7681/wetty`
|
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,9 @@ courseware_unsloth_home: "{{ courseware_state_dir }}/unsloth-home"
|
|||||||
courseware_ollama_models_dir: "{{ courseware_models_dir }}/ollama"
|
courseware_ollama_models_dir: "{{ courseware_models_dir }}/ollama"
|
||||||
courseware_node_runtime_dir: "{{ courseware_tools_dir }}/node-runtime"
|
courseware_node_runtime_dir: "{{ courseware_tools_dir }}/node-runtime"
|
||||||
courseware_node_runtime_bin_dir: "{{ courseware_node_runtime_dir }}/node_modules/node/bin"
|
courseware_node_runtime_bin_dir: "{{ courseware_node_runtime_dir }}/node_modules/node/bin"
|
||||||
courseware_wetty_dir: "{{ courseware_tools_dir }}/wetty"
|
|
||||||
courseware_promptfoo_dir: "{{ courseware_lab6_dir }}"
|
courseware_promptfoo_dir: "{{ courseware_lab6_dir }}"
|
||||||
courseware_wiki_repo_dir: "{{ courseware_repos_dir }}/LLM-Labs"
|
courseware_wiki_repo_dir: "{{ courseware_repos_dir }}/LLM-Labs"
|
||||||
courseware_wiki_runtime_config_path: "{{ courseware_wiki_repo_dir }}/public/courseware-runtime.json"
|
|
||||||
courseware_llama_cpp_bin_dir: "{{ courseware_repos_dir }}/llama.cpp/build/bin"
|
courseware_llama_cpp_bin_dir: "{{ courseware_repos_dir }}/llama.cpp/build/bin"
|
||||||
courseware_lab3_dir: "/home/student/lab3"
|
|
||||||
|
|
||||||
courseware_bind_host: "0.0.0.0"
|
courseware_bind_host: "0.0.0.0"
|
||||||
courseware_url_host: "127.0.0.1"
|
courseware_url_host: "127.0.0.1"
|
||||||
@@ -36,7 +33,6 @@ courseware_ports:
|
|||||||
unsloth: 8888
|
unsloth: 8888
|
||||||
promptfoo: 15500
|
promptfoo: 15500
|
||||||
wiki: 80
|
wiki: 80
|
||||||
wetty: 7681
|
|
||||||
|
|
||||||
courseware_transformerlab_install_mode: "single-user-pinned"
|
courseware_transformerlab_install_mode: "single-user-pinned"
|
||||||
courseware_transformerlab_version: "v0.28.2"
|
courseware_transformerlab_version: "v0.28.2"
|
||||||
@@ -67,11 +63,7 @@ courseware_chunkviz_commit: "a891eacafda1f28a12373ad3b00102e68f07c57f"
|
|||||||
courseware_promptfoo_version: "0.119.0"
|
courseware_promptfoo_version: "0.119.0"
|
||||||
courseware_kiln_release_tag: "v0.18.1"
|
courseware_kiln_release_tag: "v0.18.1"
|
||||||
courseware_node_runtime_version: "20.20.2"
|
courseware_node_runtime_version: "20.20.2"
|
||||||
courseware_wetty_spec: "wetty@2.5.0"
|
|
||||||
courseware_wetty_base_path: "/wetty"
|
|
||||||
courseware_wiki_repo: "https://git.zuccaro.me/bzuccaro/LLM-Labs.git"
|
courseware_wiki_repo: "https://git.zuccaro.me/bzuccaro/LLM-Labs.git"
|
||||||
courseware_student_username: "student"
|
|
||||||
courseware_student_password_hash: "{{ lookup('env', 'COURSEWARE_STUDENT_PASSWORD_HASH') | default('', true) }}"
|
|
||||||
|
|
||||||
courseware_open_webui_spec: "open-webui"
|
courseware_open_webui_spec: "open-webui"
|
||||||
courseware_embedding_atlas_spec: "embedding-atlas"
|
courseware_embedding_atlas_spec: "embedding-atlas"
|
||||||
@@ -159,4 +151,3 @@ courseware_services:
|
|||||||
- "unsloth"
|
- "unsloth"
|
||||||
- "promptfoo"
|
- "promptfoo"
|
||||||
- "wiki"
|
- "wiki"
|
||||||
- "wetty"
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
- packages
|
- packages
|
||||||
- lab_assets
|
- lab_assets
|
||||||
- node_runtime
|
- node_runtime
|
||||||
- { role: terminal, when: ansible_system == "Linux" }
|
|
||||||
- llama_cpp
|
- llama_cpp
|
||||||
- transformerlab
|
- transformerlab
|
||||||
- open_webui
|
- open_webui
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
- name: Fail when student password hash is not configured
|
|
||||||
fail:
|
|
||||||
msg: >-
|
|
||||||
Set COURSEWARE_STUDENT_PASSWORD_HASH in the environment before running ./labctl up.
|
|
||||||
Example:
|
|
||||||
export COURSEWARE_STUDENT_PASSWORD_HASH="$(openssl passwd -6 'student-password')"
|
|
||||||
when: courseware_student_password_hash | trim | length == 0
|
|
||||||
|
|
||||||
- name: Install terminal prerequisites
|
|
||||||
become: true
|
|
||||||
apt:
|
|
||||||
name:
|
|
||||||
- openssh-server
|
|
||||||
state: present
|
|
||||||
update_cache: true
|
|
||||||
|
|
||||||
- name: Ensure sshd drop-in directory exists
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: /etc/ssh/sshd_config.d
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Configure courseware loopback-only sshd policy
|
|
||||||
become: true
|
|
||||||
template:
|
|
||||||
src: sshd-courseware-terminal.conf.j2
|
|
||||||
dest: /etc/ssh/sshd_config.d/50-courseware-terminal.conf
|
|
||||||
mode: "0644"
|
|
||||||
register: courseware_terminal_sshd_config
|
|
||||||
|
|
||||||
- name: Validate sshd configuration
|
|
||||||
become: true
|
|
||||||
command:
|
|
||||||
argv:
|
|
||||||
- /usr/sbin/sshd
|
|
||||||
- -t
|
|
||||||
- -f
|
|
||||||
- /etc/ssh/sshd_config
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Ensure sshd runtime directory exists
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: /run/sshd
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Start and enable sshd with systemd when available
|
|
||||||
become: true
|
|
||||||
systemd:
|
|
||||||
name: ssh
|
|
||||||
state: started
|
|
||||||
enabled: true
|
|
||||||
when: ansible_service_mgr == "systemd"
|
|
||||||
|
|
||||||
- name: Check for running sshd when systemd is unavailable
|
|
||||||
become: true
|
|
||||||
command: pgrep -x sshd
|
|
||||||
register: courseware_terminal_sshd_pid
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
when: ansible_service_mgr != "systemd"
|
|
||||||
|
|
||||||
- name: Reload running sshd when config changed outside systemd
|
|
||||||
become: true
|
|
||||||
command: pkill -HUP -x sshd
|
|
||||||
when:
|
|
||||||
- ansible_service_mgr != "systemd"
|
|
||||||
- courseware_terminal_sshd_pid.rc == 0
|
|
||||||
- courseware_terminal_sshd_config.changed
|
|
||||||
|
|
||||||
- name: Start sshd when it is not already running outside systemd
|
|
||||||
become: true
|
|
||||||
command:
|
|
||||||
argv:
|
|
||||||
- /usr/sbin/sshd
|
|
||||||
when:
|
|
||||||
- ansible_service_mgr != "systemd"
|
|
||||||
- courseware_terminal_sshd_pid.rc != 0
|
|
||||||
|
|
||||||
- name: Ensure managed terminal user exists
|
|
||||||
become: true
|
|
||||||
user:
|
|
||||||
name: "{{ courseware_student_username }}"
|
|
||||||
password: "{{ courseware_student_password_hash }}"
|
|
||||||
shell: /bin/bash
|
|
||||||
create_home: true
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Ensure lab 3 workspace root exists
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ courseware_lab3_dir }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ courseware_student_username }}"
|
|
||||||
group: "{{ courseware_student_username }}"
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Ensure lab 3 WhiteRabbitNeo workspace exists
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ courseware_lab3_dir }}/WhiteRabbitNeo"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ courseware_student_username }}"
|
|
||||||
group: "{{ courseware_student_username }}"
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Write lab 3 workspace README
|
|
||||||
become: true
|
|
||||||
template:
|
|
||||||
src: lab3-workspace-readme.txt.j2
|
|
||||||
dest: "{{ courseware_lab3_dir }}/README.txt"
|
|
||||||
owner: "{{ courseware_student_username }}"
|
|
||||||
group: "{{ courseware_student_username }}"
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Check for repo-local WhiteRabbitNeo base repo
|
|
||||||
stat:
|
|
||||||
path: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo-V3-7B"
|
|
||||||
register: courseware_lab3_base_repo_stat
|
|
||||||
|
|
||||||
- name: Check for repo-local WhiteRabbitNeo GGUF directory
|
|
||||||
stat:
|
|
||||||
path: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF"
|
|
||||||
register: courseware_lab3_gguf_repo_stat
|
|
||||||
|
|
||||||
- name: Link WhiteRabbitNeo base repo into the student workspace when repo-local assets exist
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
src: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo-V3-7B"
|
|
||||||
dest: "{{ courseware_lab3_dir }}/WhiteRabbitNeo/WhiteRabbitNeo-V3-7B"
|
|
||||||
state: link
|
|
||||||
force: true
|
|
||||||
owner: "{{ courseware_student_username }}"
|
|
||||||
group: "{{ courseware_student_username }}"
|
|
||||||
when: courseware_lab3_base_repo_stat.stat.exists
|
|
||||||
|
|
||||||
- name: Link WhiteRabbitNeo GGUF directory into the student workspace when repo-local assets exist
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
src: "{{ courseware_root }}/assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF"
|
|
||||||
dest: "{{ courseware_lab3_dir }}/WhiteRabbitNeo/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF"
|
|
||||||
state: link
|
|
||||||
force: true
|
|
||||||
owner: "{{ courseware_student_username }}"
|
|
||||||
group: "{{ courseware_student_username }}"
|
|
||||||
when: courseware_lab3_gguf_repo_stat.stat.exists
|
|
||||||
|
|
||||||
- name: Link WhiteRabbitNeo download helper into the student workspace
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
src: "{{ courseware_lab2_dir }}/download_whiterabbitneo-gguf.sh"
|
|
||||||
dest: "{{ courseware_lab3_dir }}/download_whiterabbitneo-gguf.sh"
|
|
||||||
state: link
|
|
||||||
force: true
|
|
||||||
owner: "{{ courseware_student_username }}"
|
|
||||||
group: "{{ courseware_student_username }}"
|
|
||||||
|
|
||||||
- name: Create contained WeTTY directory
|
|
||||||
file:
|
|
||||||
path: "{{ courseware_wetty_dir }}"
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Install contained WeTTY runtime
|
|
||||||
command:
|
|
||||||
argv:
|
|
||||||
- npm
|
|
||||||
- install
|
|
||||||
- "{{ courseware_wetty_spec }}"
|
|
||||||
args:
|
|
||||||
chdir: "{{ courseware_wetty_dir }}"
|
|
||||||
creates: "{{ courseware_wetty_dir }}/node_modules/.bin/wetty"
|
|
||||||
environment:
|
|
||||||
PATH: "{{ courseware_node_runtime_bin_dir }}:{{ ansible_env.PATH }}"
|
|
||||||
|
|
||||||
- name: Check loopback sshd listener
|
|
||||||
become: true
|
|
||||||
command: ss -ltn
|
|
||||||
register: courseware_terminal_ss_listeners
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Assert sshd is loopback-only
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- "'127.0.0.1:22' in courseware_terminal_ss_listeners.stdout"
|
|
||||||
- "'0.0.0.0:22' not in courseware_terminal_ss_listeners.stdout"
|
|
||||||
- "'[::]:22' not in courseware_terminal_ss_listeners.stdout"
|
|
||||||
fail_msg: "sshd must listen only on 127.0.0.1:22 for the browser terminal deployment."
|
|
||||||
|
|
||||||
- name: Assert WeTTY binary exists
|
|
||||||
stat:
|
|
||||||
path: "{{ courseware_wetty_dir }}/node_modules/.bin/wetty"
|
|
||||||
register: courseware_wetty_bin_stat
|
|
||||||
|
|
||||||
- name: Fail when WeTTY installation is incomplete
|
|
||||||
fail:
|
|
||||||
msg: "WeTTY was not installed under {{ courseware_wetty_dir }}."
|
|
||||||
when: not courseware_wetty_bin_stat.stat.exists
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
This workspace is managed for the Lab 3 browser terminal.
|
|
||||||
|
|
||||||
You should log in as:
|
|
||||||
- username: {{ courseware_student_username }}
|
|
||||||
|
|
||||||
Start here:
|
|
||||||
- working directory: {{ courseware_lab3_dir }}
|
|
||||||
|
|
||||||
Helpful paths:
|
|
||||||
- WhiteRabbitNeo helper: {{ courseware_lab3_dir }}/download_whiterabbitneo-gguf.sh
|
|
||||||
- repo-local base repo symlink: {{ courseware_lab3_dir }}/WhiteRabbitNeo/WhiteRabbitNeo-V3-7B
|
|
||||||
- repo-local GGUF symlink: {{ courseware_lab3_dir }}/WhiteRabbitNeo/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF
|
|
||||||
|
|
||||||
Some symlinks only appear after the corresponding repo-local lab assets are present.
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Managed by Local Courseware Deployment.
|
|
||||||
ListenAddress 127.0.0.1
|
|
||||||
AddressFamily inet
|
|
||||||
PermitRootLogin no
|
|
||||||
PasswordAuthentication yes
|
|
||||||
KbdInteractiveAuthentication no
|
|
||||||
ChallengeResponseAuthentication no
|
|
||||||
UsePAM yes
|
|
||||||
AllowUsers {{ courseware_student_username }}
|
|
||||||
AllowTcpForwarding no
|
|
||||||
X11Forwarding no
|
|
||||||
PrintMotd no
|
|
||||||
@@ -233,12 +233,27 @@
|
|||||||
dest: "{{ courseware_state_dir }}/repair_transformerlab_plugin_supports.py"
|
dest: "{{ courseware_state_dir }}/repair_transformerlab_plugin_supports.py"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Install TransformerLab source repair helper
|
||||||
|
copy:
|
||||||
|
src: "{{ playbook_dir }}/../../scripts/repair_transformerlab_symlink_paths.py"
|
||||||
|
dest: "{{ courseware_state_dir }}/repair_transformerlab_symlink_paths.py"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
- name: Install TransformerLab default-user helper
|
- name: Install TransformerLab default-user helper
|
||||||
copy:
|
copy:
|
||||||
src: "{{ playbook_dir }}/../../scripts/ensure_transformerlab_user.py"
|
src: "{{ playbook_dir }}/../../scripts/ensure_transformerlab_user.py"
|
||||||
dest: "{{ courseware_state_dir }}/ensure_transformerlab_user.py"
|
dest: "{{ courseware_state_dir }}/ensure_transformerlab_user.py"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Repair pinned TransformerLab symlink-aware plugin file lookups
|
||||||
|
command:
|
||||||
|
argv:
|
||||||
|
- python3
|
||||||
|
- "{{ courseware_state_dir }}/repair_transformerlab_symlink_paths.py"
|
||||||
|
- --transformerlab-dir
|
||||||
|
- "{{ courseware_transformerlab_home }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Repair installed Fastchat plugin supports
|
- name: Repair installed Fastchat plugin supports
|
||||||
command:
|
command:
|
||||||
argv:
|
argv:
|
||||||
|
|||||||
@@ -36,12 +36,6 @@
|
|||||||
environment:
|
environment:
|
||||||
PATH: "{{ courseware_node_runtime_bin_dir }}:{{ ansible_env.PATH }}"
|
PATH: "{{ courseware_node_runtime_bin_dir }}:{{ ansible_env.PATH }}"
|
||||||
|
|
||||||
- name: Render wiki runtime config
|
|
||||||
template:
|
|
||||||
src: courseware-runtime.json.j2
|
|
||||||
dest: "{{ courseware_wiki_runtime_config_path }}"
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Stat wiki build output
|
- name: Stat wiki build output
|
||||||
stat:
|
stat:
|
||||||
path: "{{ courseware_wiki_repo_dir }}/.next/BUILD_ID"
|
path: "{{ courseware_wiki_repo_dir }}/.next/BUILD_ID"
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"lab3TerminalUrl": "http://{{ courseware_url_host }}:{{ courseware_ports.wetty }}{{ courseware_wetty_base_path }}",
|
|
||||||
"lab3Username": "{{ courseware_student_username }}",
|
|
||||||
"lab3WorkingDirectory": "{{ courseware_lab3_dir }}"
|
|
||||||
}
|
|
||||||
@@ -11,12 +11,9 @@ COURSEWARE_EMBEDDING_ATLAS_PORT="{{ courseware_ports.embedding_atlas }}"
|
|||||||
COURSEWARE_UNSLOTH_PORT="{{ courseware_ports.unsloth }}"
|
COURSEWARE_UNSLOTH_PORT="{{ courseware_ports.unsloth }}"
|
||||||
COURSEWARE_PROMPTFOO_PORT="{{ courseware_ports.promptfoo }}"
|
COURSEWARE_PROMPTFOO_PORT="{{ courseware_ports.promptfoo }}"
|
||||||
COURSEWARE_WIKI_PORT="{{ courseware_ports.wiki }}"
|
COURSEWARE_WIKI_PORT="{{ courseware_ports.wiki }}"
|
||||||
COURSEWARE_WETTY_PORT="{{ courseware_ports.wetty }}"
|
|
||||||
OLLAMA_BIN="{{ courseware_ollama_bin }}"
|
OLLAMA_BIN="{{ courseware_ollama_bin }}"
|
||||||
OLLAMA_MODELS_DIR="{{ courseware_ollama_models_dir }}"
|
OLLAMA_MODELS_DIR="{{ courseware_ollama_models_dir }}"
|
||||||
NODE_RUNTIME_BIN_DIR="{{ courseware_node_runtime_bin_dir }}"
|
NODE_RUNTIME_BIN_DIR="{{ courseware_node_runtime_bin_dir }}"
|
||||||
WETTY_BIN="{{ courseware_wetty_dir }}/node_modules/.bin/wetty"
|
|
||||||
COURSEWARE_WETTY_BASE_PATH="{{ courseware_wetty_base_path }}"
|
|
||||||
OPEN_WEBUI_VENV="{{ courseware_venvs_dir }}/open-webui"
|
OPEN_WEBUI_VENV="{{ courseware_venvs_dir }}/open-webui"
|
||||||
OPEN_WEBUI_DATA_DIR="{{ courseware_state_dir }}/open-webui"
|
OPEN_WEBUI_DATA_DIR="{{ courseware_state_dir }}/open-webui"
|
||||||
CHUNKVIZ_DIR="{{ courseware_repos_dir }}/ChunkViz"
|
CHUNKVIZ_DIR="{{ courseware_repos_dir }}/ChunkViz"
|
||||||
@@ -28,13 +25,10 @@ TRANSFORMERLAB_DEFAULT_USER_EMAIL="{{ courseware_transformerlab_default_user_ema
|
|||||||
TRANSFORMERLAB_DEFAULT_USER_PASSWORD="{{ courseware_transformerlab_default_user_password }}"
|
TRANSFORMERLAB_DEFAULT_USER_PASSWORD="{{ courseware_transformerlab_default_user_password }}"
|
||||||
TRANSFORMERLAB_DEFAULT_USER_FIRST_NAME="{{ courseware_transformerlab_default_user_first_name }}"
|
TRANSFORMERLAB_DEFAULT_USER_FIRST_NAME="{{ courseware_transformerlab_default_user_first_name }}"
|
||||||
TRANSFORMERLAB_DEFAULT_USER_LAST_NAME="{{ courseware_transformerlab_default_user_last_name }}"
|
TRANSFORMERLAB_DEFAULT_USER_LAST_NAME="{{ courseware_transformerlab_default_user_last_name }}"
|
||||||
COURSEWARE_STUDENT_USERNAME="{{ courseware_student_username }}"
|
|
||||||
COURSEWARE_LAB3_DIR="{{ courseware_lab3_dir }}"
|
|
||||||
UNSLOTH_BIN="{{ ansible_env.HOME }}/.local/bin/unsloth"
|
UNSLOTH_BIN="{{ ansible_env.HOME }}/.local/bin/unsloth"
|
||||||
PROMPTFOO_DIR="{{ courseware_promptfoo_dir }}"
|
PROMPTFOO_DIR="{{ courseware_promptfoo_dir }}"
|
||||||
PROMPTFOO_BIN="{{ courseware_tools_dir }}/promptfoo/node_modules/.bin/promptfoo"
|
PROMPTFOO_BIN="{{ courseware_tools_dir }}/promptfoo/node_modules/.bin/promptfoo"
|
||||||
WIKI_DIR="{{ courseware_wiki_repo_dir }}"
|
WIKI_DIR="{{ courseware_wiki_repo_dir }}"
|
||||||
WIKI_RUNTIME_CONFIG_PATH="{{ courseware_wiki_runtime_config_path }}"
|
|
||||||
LLAMA_CPP_BIN_DIR="{{ courseware_llama_cpp_bin_dir }}"
|
LLAMA_CPP_BIN_DIR="{{ courseware_llama_cpp_bin_dir }}"
|
||||||
KILN_LINUX_BIN="{{ courseware_apps_dir }}/kiln/Kiln"
|
KILN_LINUX_BIN="{{ courseware_apps_dir }}/kiln/Kiln"
|
||||||
KILN_MAC_APP="{{ courseware_apps_dir }}/Kiln.app"
|
KILN_MAC_APP="{{ courseware_apps_dir }}/Kiln.app"
|
||||||
|
|||||||
@@ -49,31 +49,6 @@ Pinned component versions:
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
require_student_password_hash() {
|
|
||||||
local current_host_profile
|
|
||||||
|
|
||||||
current_host_profile=$(host_profile)
|
|
||||||
if [ "$current_host_profile" = "macos" ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "${COURSEWARE_STUDENT_PASSWORD_HASH:-}" ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<'EOF' >&2
|
|
||||||
Missing COURSEWARE_STUDENT_PASSWORD_HASH.
|
|
||||||
|
|
||||||
Set a password hash for the managed `student` login before running this command. Example:
|
|
||||||
|
|
||||||
export COURSEWARE_STUDENT_PASSWORD_HASH="$(openssl passwd -6 'student-password')"
|
|
||||||
|
|
||||||
Then rerun:
|
|
||||||
./labctl up
|
|
||||||
EOF
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
confirm_installation() {
|
confirm_installation() {
|
||||||
local response
|
local response
|
||||||
local tlab_version
|
local tlab_version
|
||||||
@@ -552,7 +527,6 @@ main() {
|
|||||||
case "$cmd" in
|
case "$cmd" in
|
||||||
up)
|
up)
|
||||||
confirm_installation
|
confirm_installation
|
||||||
require_student_password_hash
|
|
||||||
run_playbook up.yml
|
run_playbook up.yml
|
||||||
run_project_script "$ROOT_DIR/scripts/service_manager.sh" start all
|
run_project_script "$ROOT_DIR/scripts/service_manager.sh" start all
|
||||||
;;
|
;;
|
||||||
@@ -562,7 +536,6 @@ main() {
|
|||||||
;;
|
;;
|
||||||
preflight)
|
preflight)
|
||||||
confirm_installation
|
confirm_installation
|
||||||
require_student_password_hash
|
|
||||||
run_playbook up.yml --tags preflight
|
run_playbook up.yml --tags preflight
|
||||||
;;
|
;;
|
||||||
versions)
|
versions)
|
||||||
|
|||||||
+1
-19
@@ -17,15 +17,9 @@ load_runtime_env() {
|
|||||||
: "${COURSEWARE_URL_HOST:=127.0.0.1}"
|
: "${COURSEWARE_URL_HOST:=127.0.0.1}"
|
||||||
: "${COURSEWARE_PROMPTFOO_PORT:=15500}"
|
: "${COURSEWARE_PROMPTFOO_PORT:=15500}"
|
||||||
: "${COURSEWARE_WIKI_PORT:=80}"
|
: "${COURSEWARE_WIKI_PORT:=80}"
|
||||||
: "${COURSEWARE_WETTY_PORT:=7681}"
|
|
||||||
: "${COURSEWARE_WETTY_BASE_PATH:=/wetty}"
|
|
||||||
: "${COURSEWARE_STUDENT_USERNAME:=student}"
|
|
||||||
: "${COURSEWARE_LAB3_DIR:=/home/student/lab3}"
|
|
||||||
: "${NODE_RUNTIME_BIN_DIR:=$COURSEWARE_STATE_DIR/tools/node-runtime/node_modules/node/bin}"
|
: "${NODE_RUNTIME_BIN_DIR:=$COURSEWARE_STATE_DIR/tools/node-runtime/node_modules/node/bin}"
|
||||||
: "${WETTY_BIN:=$COURSEWARE_STATE_DIR/tools/wetty/node_modules/.bin/wetty}"
|
|
||||||
: "${PROMPTFOO_DIR:=$COURSEWARE_STATE_DIR/lab6}"
|
: "${PROMPTFOO_DIR:=$COURSEWARE_STATE_DIR/lab6}"
|
||||||
: "${WIKI_DIR:=$COURSEWARE_STATE_DIR/repos/LLM-Labs}"
|
: "${WIKI_DIR:=$COURSEWARE_STATE_DIR/repos/LLM-Labs}"
|
||||||
: "${WIKI_RUNTIME_CONFIG_PATH:=$WIKI_DIR/public/courseware-runtime.json}"
|
|
||||||
: "${LLAMA_CPP_BIN_DIR:=$COURSEWARE_STATE_DIR/repos/llama.cpp/build/bin}"
|
: "${LLAMA_CPP_BIN_DIR:=$COURSEWARE_STATE_DIR/repos/llama.cpp/build/bin}"
|
||||||
|
|
||||||
if [ -n "${OLLAMA_BIN:-}" ] && [[ "$OLLAMA_BIN" != */* ]] && command -v "$OLLAMA_BIN" >/dev/null 2>&1; then
|
if [ -n "${OLLAMA_BIN:-}" ] && [[ "$OLLAMA_BIN" != */* ]] && command -v "$OLLAMA_BIN" >/dev/null 2>&1; then
|
||||||
@@ -49,8 +43,7 @@ service_list() {
|
|||||||
"embedding-atlas" \
|
"embedding-atlas" \
|
||||||
"unsloth" \
|
"unsloth" \
|
||||||
"promptfoo" \
|
"promptfoo" \
|
||||||
"wiki" \
|
"wiki"
|
||||||
"wetty"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
service_pid_file() {
|
service_pid_file() {
|
||||||
@@ -71,7 +64,6 @@ service_port() {
|
|||||||
unsloth) printf '%s\n' "${COURSEWARE_UNSLOTH_PORT}" ;;
|
unsloth) printf '%s\n' "${COURSEWARE_UNSLOTH_PORT}" ;;
|
||||||
promptfoo) printf '%s\n' "${COURSEWARE_PROMPTFOO_PORT}" ;;
|
promptfoo) printf '%s\n' "${COURSEWARE_PROMPTFOO_PORT}" ;;
|
||||||
wiki) printf '%s\n' "${COURSEWARE_WIKI_PORT}" ;;
|
wiki) printf '%s\n' "${COURSEWARE_WIKI_PORT}" ;;
|
||||||
wetty) printf '%s\n' "${COURSEWARE_WETTY_PORT}" ;;
|
|
||||||
*) return 1 ;;
|
*) return 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -86,7 +78,6 @@ service_url() {
|
|||||||
unsloth) printf 'http://%s:%s\n' "$COURSEWARE_URL_HOST" "$COURSEWARE_UNSLOTH_PORT" ;;
|
unsloth) printf 'http://%s:%s\n' "$COURSEWARE_URL_HOST" "$COURSEWARE_UNSLOTH_PORT" ;;
|
||||||
promptfoo) printf 'http://%s:%s\n' "$COURSEWARE_URL_HOST" "$COURSEWARE_PROMPTFOO_PORT" ;;
|
promptfoo) printf 'http://%s:%s\n' "$COURSEWARE_URL_HOST" "$COURSEWARE_PROMPTFOO_PORT" ;;
|
||||||
wiki) printf 'http://%s:%s\n' "$COURSEWARE_URL_HOST" "$COURSEWARE_WIKI_PORT" ;;
|
wiki) printf 'http://%s:%s\n' "$COURSEWARE_URL_HOST" "$COURSEWARE_WIKI_PORT" ;;
|
||||||
wetty) printf 'http://%s:%s%s\n' "$COURSEWARE_URL_HOST" "$COURSEWARE_WETTY_PORT" "$COURSEWARE_WETTY_BASE_PATH" ;;
|
|
||||||
*) return 1 ;;
|
*) return 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -153,15 +144,6 @@ service_command() {
|
|||||||
"$COURSEWARE_BIND_HOST" \
|
"$COURSEWARE_BIND_HOST" \
|
||||||
"$COURSEWARE_WIKI_PORT"
|
"$COURSEWARE_WIKI_PORT"
|
||||||
;;
|
;;
|
||||||
wetty)
|
|
||||||
printf 'cd "%s" && PATH="%s:$PATH" exec "%s" --host %s --port %s --base %s --allow-iframe --ssh-host 127.0.0.1 --ssh-port 22 --ssh-auth password' \
|
|
||||||
"$COURSEWARE_ROOT" \
|
|
||||||
"$NODE_RUNTIME_BIN_DIR" \
|
|
||||||
"$WETTY_BIN" \
|
|
||||||
"$COURSEWARE_BIND_HOST" \
|
|
||||||
"$COURSEWARE_WETTY_PORT" \
|
|
||||||
"$COURSEWARE_WETTY_BASE_PATH"
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
return 1
|
return 1
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ from __future__ import annotations
|
|||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
DEFAULT_WORKSPACE_PLUGINS = ("fastchat_server",)
|
||||||
|
DEFAULT_WORKSPACE_EXPERIMENTS = ("alpha", "beta", "gamma")
|
||||||
|
DEFAULT_WORKSPACE_MODELS = ("unsloth_Llama-3.2-1B-Instruct",)
|
||||||
|
DEFAULT_MODEL_METADATA_FILES = ("_tlab_complete_provenance.json",)
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@@ -36,6 +42,92 @@ def bootstrap_source(transformerlab_dir: Path) -> None:
|
|||||||
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||||
|
|
||||||
|
|
||||||
|
def target_workspace(transformerlab_dir: Path, team_id: str) -> Path:
|
||||||
|
return transformerlab_dir / "orgs" / team_id / "workspace"
|
||||||
|
|
||||||
|
|
||||||
|
def workspace_team_id(workspace: Path, transformerlab_dir: Path) -> str | None:
|
||||||
|
orgs_dir = transformerlab_dir / "orgs"
|
||||||
|
try:
|
||||||
|
relative = workspace.relative_to(orgs_dir)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if len(relative.parts) >= 2 and relative.parts[1] == "workspace":
|
||||||
|
return relative.parts[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def candidate_workspaces(transformerlab_dir: Path, excluded_team_id: str) -> list[Path]:
|
||||||
|
candidates: list[Path] = []
|
||||||
|
root_workspace = transformerlab_dir / "workspace"
|
||||||
|
if root_workspace.is_dir():
|
||||||
|
candidates.append(root_workspace)
|
||||||
|
|
||||||
|
orgs_dir = transformerlab_dir / "orgs"
|
||||||
|
if not orgs_dir.is_dir():
|
||||||
|
return candidates
|
||||||
|
|
||||||
|
for workspace in sorted(orgs_dir.glob("*/workspace")):
|
||||||
|
if not workspace.is_dir():
|
||||||
|
continue
|
||||||
|
if workspace_team_id(workspace, transformerlab_dir) == excluded_team_id:
|
||||||
|
continue
|
||||||
|
candidates.append(workspace)
|
||||||
|
return candidates
|
||||||
|
|
||||||
|
|
||||||
|
def copy_dir_if_missing(source: Path | None, target: Path, label: str) -> bool:
|
||||||
|
if source is None or not source.is_dir() or target.exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copytree(source, target)
|
||||||
|
print(f"Seeded {label} from {source}.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def copy_file_if_missing(source: Path | None, target: Path, label: str) -> bool:
|
||||||
|
if source is None or not source.is_file() or target.exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(source, target)
|
||||||
|
print(f"Seeded {label} from {source}.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def find_workspace_seed(transformerlab_dir: Path, category: str, name: str, excluded_team_id: str) -> Path | None:
|
||||||
|
for workspace in candidate_workspaces(transformerlab_dir, excluded_team_id):
|
||||||
|
candidate = workspace / category / name
|
||||||
|
if candidate.exists():
|
||||||
|
return candidate
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def seed_workspace(transformerlab_dir: Path, team_id: str) -> None:
|
||||||
|
workspace = target_workspace(transformerlab_dir, team_id)
|
||||||
|
workspace.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for plugin in DEFAULT_WORKSPACE_PLUGINS:
|
||||||
|
source = transformerlab_dir / "src" / "transformerlab" / "plugins" / plugin
|
||||||
|
copy_dir_if_missing(source, workspace / "plugins" / plugin, f"plugin '{plugin}'")
|
||||||
|
|
||||||
|
for experiment in DEFAULT_WORKSPACE_EXPERIMENTS:
|
||||||
|
source = find_workspace_seed(transformerlab_dir, "experiments", experiment, team_id)
|
||||||
|
copy_dir_if_missing(source, workspace / "experiments" / experiment, f"experiment '{experiment}'")
|
||||||
|
|
||||||
|
copied_model = False
|
||||||
|
for model in DEFAULT_WORKSPACE_MODELS:
|
||||||
|
source = find_workspace_seed(transformerlab_dir, "models", model, team_id)
|
||||||
|
copied_model = copy_dir_if_missing(source, workspace / "models" / model, f"model '{model}'") or copied_model
|
||||||
|
|
||||||
|
for metadata_name in DEFAULT_MODEL_METADATA_FILES:
|
||||||
|
source = find_workspace_seed(transformerlab_dir, "models", metadata_name, team_id)
|
||||||
|
if copied_model or source is not None:
|
||||||
|
copy_file_if_missing(source, workspace / "models" / metadata_name, f"model metadata '{metadata_name}'")
|
||||||
|
|
||||||
|
|
||||||
async def ensure_user(args: argparse.Namespace) -> int:
|
async def ensure_user(args: argparse.Namespace) -> int:
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from transformerlab.db.constants import DATABASE_FILE_NAME
|
from transformerlab.db.constants import DATABASE_FILE_NAME
|
||||||
@@ -129,6 +221,8 @@ async def ensure_user(args: argparse.Namespace) -> int:
|
|||||||
await session.commit()
|
await session.commit()
|
||||||
print(f"Updated team role to owner for {args.email}.")
|
print(f"Updated team role to owner for {args.email}.")
|
||||||
|
|
||||||
|
seed_workspace(Path(args.transformerlab_dir), str(user_team.team_id))
|
||||||
|
|
||||||
action = "Created" if created else "Verified"
|
action = "Created" if created else "Verified"
|
||||||
print(f"{action} default TransformerLab user {args.email}.")
|
print(f"{action} default TransformerLab user {args.email}.")
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Patch pinned TransformerLab source to tolerate symlinked home directories."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
PATCH_MARKER = "with symlinked TransformerLab home directories."
|
||||||
|
TARGET_BLOCK = re.compile(
|
||||||
|
r"(?P<indent>[ \t]+)# The following prevents path traversal attacks:.*?"
|
||||||
|
r"(?P=indent)# now get the file contents",
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--transformerlab-dir", required=True)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def repair_plugins_router(path: Path) -> bool:
|
||||||
|
source = path.read_text(encoding="utf-8")
|
||||||
|
if PATCH_MARKER in source:
|
||||||
|
return False
|
||||||
|
|
||||||
|
replacement = (
|
||||||
|
" # The following prevents path traversal attacks while remaining compatible\n"
|
||||||
|
" # with symlinked TransformerLab home directories.\n"
|
||||||
|
" plugin_dir = Path(await lab_dirs.plugin_dir_by_name((pluginId)))\n"
|
||||||
|
" resolved_plugin_dir = plugin_dir.resolve()\n"
|
||||||
|
' final_path = (plugin_dir / f"{filename}{file_ext}").resolve()\n'
|
||||||
|
"\n"
|
||||||
|
" try:\n"
|
||||||
|
" final_path.relative_to(resolved_plugin_dir)\n"
|
||||||
|
" except ValueError:\n"
|
||||||
|
' return {"message": f"File {filename}{file_ext} is outside plugin directory"}\n'
|
||||||
|
"\n"
|
||||||
|
" # now get the file contents"
|
||||||
|
)
|
||||||
|
updated, count = TARGET_BLOCK.subn(replacement, source, count=1)
|
||||||
|
if count != 1:
|
||||||
|
raise RuntimeError(f"Could not find path traversal block in {path}")
|
||||||
|
|
||||||
|
path.write_text(updated, encoding="utf-8")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
root = Path(args.transformerlab_dir).expanduser().resolve()
|
||||||
|
plugins_router = root / "src" / "transformerlab" / "routers" / "experiment" / "plugins.py"
|
||||||
|
if not plugins_router.exists():
|
||||||
|
print(f"missing TransformerLab plugins router: {plugins_router}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
changed = repair_plugins_router(plugins_router)
|
||||||
|
if changed:
|
||||||
|
print(f"patched {plugins_router}")
|
||||||
|
else:
|
||||||
|
print(f"already patched {plugins_router}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -28,44 +28,6 @@ ensure_transformerlab_default_user() {
|
|||||||
--last-name "${TRANSFORMERLAB_DEFAULT_USER_LAST_NAME:-}" >>"$STATE_DIR/logs/transformerlab_default_user.log" 2>&1 || true
|
--last-name "${TRANSFORMERLAB_DEFAULT_USER_LAST_NAME:-}" >>"$STATE_DIR/logs/transformerlab_default_user.log" 2>&1 || true
|
||||||
}
|
}
|
||||||
|
|
||||||
check_wetty_prereqs() {
|
|
||||||
if ! id "$COURSEWARE_STUDENT_USERNAME" >/dev/null 2>&1; then
|
|
||||||
echo "Missing terminal user '$COURSEWARE_STUDENT_USERNAME'. Re-run ./labctl up after setting COURSEWARE_STUDENT_PASSWORD_HASH." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -x "$WETTY_BIN" ]; then
|
|
||||||
echo "Missing WeTTY binary at $WETTY_BIN. Re-run ./labctl up." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$WIKI_RUNTIME_CONFIG_PATH" ]; then
|
|
||||||
echo "Missing wiki runtime config at $WIKI_RUNTIME_CONFIG_PATH. Re-run ./labctl up." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d "$COURSEWARE_LAB3_DIR" ]; then
|
|
||||||
echo "Missing lab workspace at $COURSEWARE_LAB3_DIR. Re-run ./labctl up." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! python3 - <<'PY'
|
|
||||||
import socket, sys
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
sock.settimeout(1)
|
|
||||||
try:
|
|
||||||
sock.connect(("127.0.0.1", 22))
|
|
||||||
except OSError:
|
|
||||||
sys.exit(1)
|
|
||||||
finally:
|
|
||||||
sock.close()
|
|
||||||
PY
|
|
||||||
then
|
|
||||||
echo "Loopback sshd is not reachable on 127.0.0.1:22." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_targets() {
|
resolve_targets() {
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
echo "No target specified." >&2
|
echo "No target specified." >&2
|
||||||
@@ -120,7 +82,7 @@ service_ready() {
|
|||||||
promptfoo)
|
promptfoo)
|
||||||
curl -fsS "$(service_url "$service")/health" >/dev/null 2>&1
|
curl -fsS "$(service_url "$service")/health" >/dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
open-webui|chunkviz|embedding-atlas|unsloth|wiki|wetty)
|
open-webui|chunkviz|embedding-atlas|unsloth|wiki)
|
||||||
curl -fsS "$(service_url "$service")" >/dev/null 2>&1
|
curl -fsS "$(service_url "$service")" >/dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -198,6 +160,8 @@ start_one() {
|
|||||||
;;
|
;;
|
||||||
transformerlab)
|
transformerlab)
|
||||||
if command -v python3 >/dev/null 2>&1; then
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
python3 "$SCRIPT_DIR/repair_transformerlab_symlink_paths.py" \
|
||||||
|
--transformerlab-dir "$TRANSFORMERLAB_DIR" >>"$STATE_DIR/logs/transformerlab_source_repairs.log" 2>&1 || true
|
||||||
python3 "$SCRIPT_DIR/repair_transformerlab_plugin_supports.py" \
|
python3 "$SCRIPT_DIR/repair_transformerlab_plugin_supports.py" \
|
||||||
--transformerlab-dir "$TRANSFORMERLAB_DIR" \
|
--transformerlab-dir "$TRANSFORMERLAB_DIR" \
|
||||||
--plugin "fastchat_server" \
|
--plugin "fastchat_server" \
|
||||||
@@ -214,9 +178,6 @@ start_one() {
|
|||||||
--required-support "batched" >>"$STATE_DIR/logs/transformerlab_plugin_supports.log" 2>&1 || true
|
--required-support "batched" >>"$STATE_DIR/logs/transformerlab_plugin_supports.log" 2>&1 || true
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
wetty)
|
|
||||||
check_wetty_prereqs
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -330,7 +291,6 @@ Unsloth Studio: $(service_url unsloth)
|
|||||||
Promptfoo CLI: $PROMPTFOO_BIN
|
Promptfoo CLI: $PROMPTFOO_BIN
|
||||||
Promptfoo UI: $(service_url promptfoo)
|
Promptfoo UI: $(service_url promptfoo)
|
||||||
Wiki: $(service_url wiki)
|
Wiki: $(service_url wiki)
|
||||||
Lab 3 Terminal: $(service_url wetty)
|
|
||||||
Kiln app: ${KILN_LAUNCH_PATH:-not installed}
|
Kiln app: ${KILN_LAUNCH_PATH:-not installed}
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user