diff --git a/READMEs/README-build-windows.md b/READMEs/README-build-windows.md
index 96b4135..bcf1abd 100644
--- a/READMEs/README-build-windows.md
+++ b/READMEs/README-build-windows.md
@@ -58,3 +58,76 @@ As administrator
Then create the config JSON in `\ProgramData\sai\builder\conf`, using the
platform name `windows-10`.
+
+## Running as a service with sai user
+
+
+## Building
+
+Building on Windows follows the standard CMake flow. You can perform the build as a standard user.
+
+```cmd
+cd sai
+mkdir build
+cd build
+cmake ..
+cmake --build . --config Debug
+```
+
+Then in a Command Prompt with Administrator rights
+
+```cmd
+cd "\User\your-build-user\sai\build"
+cmake --install . --config Debug
+```
+
+This will collect the needed files into "C:\Program Files (x86)\sai\bin"
+
+## Setup for Service Execution
+
+To run `sai-builder` as a Windows Service under a specific unprivileged user (e.g., `.\sai`), follow these steps.
+
+### 1. Create the User
+
+If the `sai` user does not exist, create it. Open a Command Prompt **as Administrator**.
+
+```cmd
+net user sai <password> /add /passwordchg:no /expires:never
+```
+
+### 2. Grant Privileges
+
+The `sai` user needs permission to:
+1. **Log on as a Service**: Windows usually grants this automatically when you register the service with a specific user, but you can check it in `secpol.msc`.
+2. **Shut down the system**: If you want the builder to handle power management (suspend/shutdown).
+
+To grant rights:
+1. Run `secpol.msc` (Local Security Policy).
+2. Go to **Local Policies** -> **User Rights Assignment**.
+3. Find **Shut down the system**.
+4. Double-click, click **Add User or Group**, type `sai`, check Names, and OK.
+5. (Optional) Ensure **Log on as a service** also includes `sai`.
+
+### 3. Install the Service
+
+Open a Command Prompt **as Administrator** (Right-click Start -> Command Prompt (Admin) or PowerShell (Admin)).
+
+Run the following `sc create` command. **Note:** The space after the `=` sign in options like `binPath=` is mandatory.
+
+Replace the paths and password with your actual values.
+
+```cmd
+sc create SaiBuilder binPath= "c:\Program Files (x86)\sai\bin\sai-builder.exe --service" DisplayName= "Sai Builder" start= auto obj= ".\sai" password="password-for-sai-account"
+```
+
+### 4. Manage the Service
+
+You can now start and stop the builder using standard Windows commands (Administrator required):
+
+```cmd
+sc start SaiBuilder
+sc stop SaiBuilder
+```
+
+To view logs or status, check the standard `sai-builder` logs (configured in your JSON config) or the Event Viewer (Application log) if the service fails to start immediately.
+
diff --git a/assets/freebsd.svg b/assets/freebsd.svg
new file mode 100644
index 0000000..fba9b89
--- /dev/null
+++ b/assets/freebsd.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3.816 3.816" height="14.423" width="14.423" xmlns:v="https://vecta.io/nano"><circle r="1.908" cx="1.908" cy="1.908" opacity=".42" fill="#e6e6e6"/><ellipse ry="1.521" rx="1.488" cy="1.954" cx="1.839" fill="#fff" paint-order="markers fill stroke"/><path d="M1.777 3.466a1.59 1.59 0 0 1-.551-.138 1.61 1.61 0 0 1-.394-.256l-.184-.195C.48 2.67.37 2.42.33 2.158.32 2.072.32 2.013.32 1.916a1.1 1.1 0 0 1 .05-.364c.04-.142.09-.26.162-.396l.02-.05v-.02c0-.012-.01-.025-.02-.037C.483.975.423.84.377.706.357.606.327.542.327.482c0-.05 0-.09.03-.126a.13.13 0 0 1 .168-.07c.05 0 .06.01.11.02a2.2 2.2 0 0 1 .469.246c.01.01.04.02.04.02s.02-.01.05-.02c.15-.07.27-.11.414-.128l.283-.02a1.15 1.15 0 0 1 .369.05c.14.04.3.1.407.165.01.01.02.01.02.01s.02-.01.05-.04c.07-.05.11-.07.173-.1.176-.09.35-.14.472-.14.09 0 .144.02.178.09.01.02.02.05.02.1 0 .1 0 .17-.04.256a1.91 1.91 0 0 1-.222.467l-.04.053v.02a.93.93 0 0 1 .07.196c.02.123.05.24.05.367.01.47-.186.91-.56 1.215-.217.18-.48.294-.762.334a1.82 1.82 0 0 1-.331.018zm.24-.06c.05 0 .07-.012.13-.017a1.43 1.43 0 0 0 .276-.077 1.5 1.5 0 0 0 .661-.498c.126-.166.22-.375.26-.576.03-.164.04-.31.01-.48-.01-.127-.05-.262-.09-.346v-.012l-.01.016c-.03.03-.04.04-.06.05a.47.47 0 0 1-.192.012.73.73 0 0 1-.349-.183c-.1-.088-.156-.203-.174-.312V.8c.01-.05.05-.1.1-.1.04-.01.05-.01.05-.02V.66a1.02 1.02 0 0 0-.257-.123 2.02 2.02 0 0 0-.375-.09c-.05-.01-.187-.01-.245 0-.216.01-.4.06-.584.16a1.41 1.41 0 0 0-.572.519 1.48 1.48 0 0 0-.222.659v.25a1.51 1.51 0 0 0 .658 1.108 1.49 1.49 0 0 0 .419.196 1.76 1.76 0 0 0 .216.047 3.27 3.27 0 0 0 .352.012zm-.184-.162l-.244-.03c-.174-.037-.324-.1-.465-.193-.168-.114-.31-.27-.417-.46-.04-.07-.09-.163-.11-.23-.02-.09-.06-.202-.07-.29v-.148c0-.22.1-.49.226-.69.07-.112.178-.23.278-.316.14-.123.35-.226.567-.286.06-.01.157-.04.16-.04s0 .01-.07.04a1.03 1.03 0 0 0-.232.122 1.34 1.34 0 0 0-.213.154c-.07.06-.152.146-.2.21-.09.11-.145.237-.175.36-.01.075-.02.13-.02.207 0 .103 0 .155.02.155 0 0 .02-.012.04-.013a.57.57 0 0 1 .163-.032c.06-.012.123 0 .18.023.11.037.216.112.318.22.05.06.1.108.158.197a.74.74 0 0 0 .135.161c.09.072.167.103.275.103.05 0 .07 0 .11-.012.28-.058.573-.343.702-.688.02-.06.04-.112.05-.194 0-.04 0-.043.02-.043.01 0 .07.012.09.012.01.024.05.07.06.152.02.082.02.098.02.253 0 .183 0 .226-.02.335-.02.092-.05.166-.1.27a1.27 1.27 0 0 1-.571.564c-.11.056-.218.093-.34.118-.05.012-.1.016-.174.023-.07.012-.1.012-.14.012zm1.252-1.846c.08-.012.13-.042.19-.124.03-.045.05-.067.07-.113a1.62 1.62 0 0 0 .164-.418c0-.06.01-.07.01-.137v-.11c0-.04-.04-.06-.05-.09-.09-.06-.28-.02-.504.09a1.32 1.32 0 0 0-.325.217c-.02.04-.05.05-.06.07-.04.09-.02.185.01.292.02.058.06.102.11.15s.1.087.158.115c.09.04.154.055.225.045zm-.05-.047c-.05-.012-.1-.022-.154-.047-.11-.055-.175-.126-.177-.192V1.08c.02-.06.1-.123.27-.214l.37-.213c.05-.04.07-.05.09-.1.01-.05.02-.05.04-.02 0 .01.01.04.01.06 0 .03 0 .05-.01.11-.03.146-.11.337-.166.44-.05.075-.1.14-.137.17a.19.19 0 0 1-.133.036zm-2.454-.3l.02-.037c0-.012 0-.012-.02-.05A1.42 1.42 0 0 1 .431.616C.4.506.4.433.43.42.49.43.667.54.86.68l.05.04.05-.04c.05-.04.07-.05.07-.06C1.01.59.9.51.82.455.72.395.645.355.578.344c-.02-.01-.1-.01-.123 0-.05.01-.07.04-.09.09v.13c.01.07.04.163.07.24.02.07.07.173.1.21 0 .012.02.037.02.037z" fill="#fe0000"/></svg>
\ No newline at end of file
diff --git a/assets/sai.js b/assets/sai.js
index 1741428..9f545f0 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -1621,7 +1621,13 @@ function ws_open_sai()
// console.log(msg.data);
// if (msg.data.length < 10)
// return;
+ try {
jso = JSON.parse(msg.data);
+ } catch {
+ console.log("Bad JSON received:");
+ console.log(msg.data);
+ return
+ }
// console.log(jso.schema);
if (jso.alang) {
diff --git a/src/builder/CMakeLists.txt b/src/builder/CMakeLists.txt
index 8768b0d..ee680f4 100644
--- a/src/builder/CMakeLists.txt
+++ b/src/builder/CMakeLists.txt
@@ -82,6 +82,14 @@ if (requirements)
target_link_libraries(${SUB} ws2_32.lib userenv.lib psapi.lib iphlpapi.lib)
endif()
+ if (MSVC OR WIN32)
+ # Auto-copy dependencies (DLLs) during install
+ install(CODE "
+ include(BundleUtilities)
+ fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${BIN_DIR}/sai-builder.exe\" \"\" \"\")
+ " COMPONENT builder)
+ endif()
+
install(TARGETS "sai-builder"
RUNTIME DESTINATION "${BIN_DIR}" COMPONENT builder)
diff --git a/src/builder/b-task.c b/src/builder/b-task.c
index 5eadab8..ea575ea 100644
--- a/src/builder/b-task.c
+++ b/src/builder/b-task.c
@@ -90,6 +90,7 @@ const char *git_helper_sh =
const char *git_helper_bat =
"@echo on\n"
"setlocal EnableDelayedExpansion\n"
+ "set \"PATH=%PATH%;C:\\Program Files\\Git\\cmd;C:\\Windows\\System32;C:\\Windows\"\n"
"echo \"git_helper_bat: starting\"\n"
"set \"OPERATION=%~1\"\n"
"echo \"OPERATION: !OPERATION!\"\n"
@@ -103,11 +104,10 @@ const char *git_helper_bat =
" echo \"HASH: !HASH!\"\n"
" echo \"MIRROR_PATH: !MIRROR_PATH!\"\n"
" :lock_wait\n"
- " mkdir \"!MIRROR_PATH!.lock\" 2>nul\n"
- " mkdir \"!MIRROR_PATH!\" 2>nul\n"
+ " mkdir \"!MIRROR_PATH!.lock\"\n"
" if errorlevel 1 (\n"
" echo \"git mirror locked, waiting...\"\n"
- " C:\\Windows\\System32\\timeout.exe /t 1 /nobreak > nul\n"
+ " ping -n 2 127.0.0.1 >nul\n"
" goto :lock_wait\n"
" )\n"
" if exist \"!MIRROR_PATH!\\\\.git\" (\n"
diff --git a/src/builder/b-win32-service.c b/src/builder/b-win32-service.c
index aacab5d..e290e94 100644
--- a/src/builder/b-win32-service.c
+++ b/src/builder/b-win32-service.c
@@ -20,7 +20,7 @@
*/
#if defined(WIN32)
-
+#include <libwebsockets.h>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt
index e91bf34..0f413fc 100644
--- a/src/web/CMakeLists.txt
+++ b/src/web/CMakeLists.txt
@@ -149,6 +149,7 @@ if (requirements)
../../assets/virt-qemu.svg
../../assets/w10.svg
../../assets/w11.svg
+ ../../assets/freebsd.svg
DESTINATION "${DATA_DIR}/sai/assets"
COMPONENT web)
diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c
index b6d0804..8570e0e 100644
--- a/src/web/w-ws-browser.c
+++ b/src/web/w-ws-browser.c
@@ -950,7 +950,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss)
lws_json_purify(esc1, pss->auth_user, sizeof(esc1) - 1, &iu)
);
- saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, start,
+ saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
lws_ptr_diff_size_t(p, start),
lws_write_ws_flags(LWS_WRITE_TEXT, 1, 0));
p = start;
@@ -1007,7 +1007,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss)
p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "{\"e\":");
if (lws_ptr_diff_size_t(end, p) < 256) {
- saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, start,
+ saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
lws_ptr_diff_size_t(p, start),
lws_write_ws_flags(LWS_WRITE_TEXT, 0, 0));
p = start;