{"schema":"libjg2-1",
"vpath":"/git/",
"avatar":"/git/avatar/",
"alang":"",
"gen_ut":1765606914,
"reponame":"sai",
"desc":"Sai lightweight distributed CI",
"owner": { "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },"url":"https://warmcat.com/repo/sai",
"f":3,
"items": [
{"schema":"libjg2-1",
"cid":"d29ec912499952e85ffa2e6c4347e701",
"commit": {"type":"commit",
"time": 1764914553,
"time_ofs": 0,
"oid_tree": { "oid": "d49537669146a358b7182aa6e4b37ed541a47768", "alias": []},
"oid":{ "oid": "8cd5afdf934ad20f01f7e79f894d2426885c3534", "alias": [ "refs/heads/main"]},
"msg": "Add documentation for Windows Service setup",
"sig_commit": { "git_time": { "time": 1764914553, "offset": 0 }, "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },
"sig_author": { "git_time": { "time": 1764774369, "offset": 0 }, "name": "google-labs-jules[bot]", "email": "161369871+google-labs-jules[bot]@users.noreply.github.com", "md5": "20cfbb933d508ba0b7ab08eee2f21f56" }},
"body": "Add documentation for Windows Service setup\n\nAdds src/builder/README.Windows.md detailing how to build, create the user, grant necessary privileges, and register sai-builder as a Windows Service.\n"
,
"diff": "diff --git a/READMEs/README-build-windows.md b/READMEs/README-build-windows.md\nindex 96b4135..bcf1abd 100644\n--- a/READMEs/README-build-windows.md\n+++ b/READMEs/README-build-windows.md\n@@ -58,3 +58,76 @@ As administrator\n \n Then create the config JSON in `\u005cProgramData\u005csai\u005cbuilder\u005cconf`, using the\n platform name `windows-10`.\n+\n+## Running as a service with sai user\n+\n+\n+## Building\n+\n+Building on Windows follows the standard CMake flow. You can perform the build as a standard user.\n+\n+```cmd\n+cd sai\n+mkdir build\n+cd build\n+cmake ..\n+cmake --build . --config Debug\n+```\n+\n+Then in a Command Prompt with Administrator rights\n+\n+```cmd\n+cd \u0022\u005cUser\u005cyour-build-user\u005csai\u005cbuild\u0022\n+cmake --install . --config Debug\n+```\n+\n+This will collect the needed files into \u0022C:\u005cProgram Files (x86)\u005csai\u005cbin\u0022\n+\n+## Setup for Service Execution\n+\n+To run `sai-builder` as a Windows Service under a specific unprivileged user (e.g., `.\u005csai`), follow these steps.\n+\n+### 1. Create the User\n+\n+If the `sai` user does not exist, create it. Open a Command Prompt **as Administrator**.\n+\n+```cmd\n+net user sai \u003cpassword\u003e /add /passwordchg:no /expires:never\n+```\n+\n+### 2. Grant Privileges\n+\n+The `sai` user needs permission to:\n+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`.\n+2. **Shut down the system**: If you want the builder to handle power management (suspend/shutdown).\n+\n+To grant rights:\n+1. Run `secpol.msc` (Local Security Policy).\n+2. Go to **Local Policies** -\u003e **User Rights Assignment**.\n+3. Find **Shut down the system**.\n+4. Double-click, click **Add User or Group**, type `sai`, check Names, and OK.\n+5. (Optional) Ensure **Log on as a service** also includes `sai`.\n+\n+### 3. Install the Service\n+\n+Open a Command Prompt **as Administrator** (Right-click Start -\u003e Command Prompt (Admin) or PowerShell (Admin)).\n+\n+Run the following `sc create` command. **Note:** The space after the `\u003d` sign in options like `binPath\u003d` is mandatory.\n+\n+Replace the paths and password with your actual values.\n+\n+```cmd\n+sc create SaiBuilder binPath\u003d \u0022c:\u005cProgram Files (x86)\u005csai\u005cbin\u005csai-builder.exe --service\u0022 DisplayName\u003d \u0022Sai Builder\u0022 start\u003d auto obj\u003d \u0022.\u005csai\u0022 password\u003d\u0022password-for-sai-account\u0022 \n+```\n+\n+### 4. Manage the Service\n+\n+You can now start and stop the builder using standard Windows commands (Administrator required):\n+\n+```cmd\n+sc start SaiBuilder\n+sc stop SaiBuilder\n+```\n+\n+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.\n+\ndiff --git a/assets/freebsd.svg b/assets/freebsd.svg\nnew file mode 100644\nindex 0000000..fba9b89\n--- /dev/null\n+++ b/assets/freebsd.svg\n@@ -0,0 +1 @@\n+\u003csvg xmlns\u003d\u0022http://www.w3.org/2000/svg\u0022 viewBox\u003d\u00220 0 3.816 3.816\u0022 height\u003d\u002214.423\u0022 width\u003d\u002214.423\u0022 xmlns:v\u003d\u0022https://vecta.io/nano\u0022\u003e\u003ccircle r\u003d\u00221.908\u0022 cx\u003d\u00221.908\u0022 cy\u003d\u00221.908\u0022 opacity\u003d\u0022.42\u0022 fill\u003d\u0022#e6e6e6\u0022/\u003e\u003cellipse ry\u003d\u00221.521\u0022 rx\u003d\u00221.488\u0022 cy\u003d\u00221.954\u0022 cx\u003d\u00221.839\u0022 fill\u003d\u0022#fff\u0022 paint-order\u003d\u0022markers fill stroke\u0022/\u003e\u003cpath d\u003d\u0022M1.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\u0022 fill\u003d\u0022#fe0000\u0022/\u003e\u003c/svg\u003e\n\u005c No newline at end of file\ndiff --git a/assets/sai.js b/assets/sai.js\nindex 1741428..9f545f0 100644\n--- a/assets/sai.js\n+++ b/assets/sai.js\n@@ -1621,7 +1621,13 @@ function ws_open_sai()\n \t\t//\tconsole.log(msg.data);\n \t\t//\tif (msg.data.length \u003c 10)\n \t\t//\t\treturn;\n+\t\ttry {\n \t\t\tjso \u003d JSON.parse(msg.data);\n+\t\t} catch {\n+\t\t\tconsole.log(\u0022Bad JSON received:\u0022);\n+\t\t\tconsole.log(msg.data);\n+\t\t\treturn\n+\t\t}\n \t\t//\tconsole.log(jso.schema);\n \n \t\t\tif (jso.alang) {\ndiff --git a/src/builder/CMakeLists.txt b/src/builder/CMakeLists.txt\nindex 8768b0d..ee680f4 100644\n--- a/src/builder/CMakeLists.txt\n+++ b/src/builder/CMakeLists.txt\n@@ -82,6 +82,14 @@ if (requirements)\n \t\ttarget_link_libraries(${SUB} ws2_32.lib userenv.lib psapi.lib iphlpapi.lib)\n \tendif()\n \n+\tif (MSVC OR WIN32)\n+\t\t# Auto-copy dependencies (DLLs) during install\n+\t\tinstall(CODE \u0022\n+\t\t\tinclude(BundleUtilities)\n+\t\t\tfixup_bundle(\u005c\u0022\u005c${CMAKE_INSTALL_PREFIX}/${BIN_DIR}/sai-builder.exe\u005c\u0022 \u005c\u0022\u005c\u0022 \u005c\u0022\u005c\u0022)\n+\t\t\t\u0022 COMPONENT builder)\n+\tendif()\n+\n \tinstall(TARGETS \u0022sai-builder\u0022\n \t\tRUNTIME DESTINATION \u0022${BIN_DIR}\u0022 COMPONENT builder)\n \ndiff --git a/src/builder/b-task.c b/src/builder/b-task.c\nindex 5eadab8..ea575ea 100644\n--- a/src/builder/b-task.c\n+++ b/src/builder/b-task.c\n@@ -90,6 +90,7 @@ const char *git_helper_sh \u003d\n const char *git_helper_bat \u003d\n \t\u0022@echo on\u005cn\u0022\n \t\u0022setlocal EnableDelayedExpansion\u005cn\u0022\n+\t\u0022set \u005c\u0022PATH\u003d%PATH%;C:\u005c\u005cProgram Files\u005c\u005cGit\u005c\u005ccmd;C:\u005c\u005cWindows\u005c\u005cSystem32;C:\u005c\u005cWindows\u005c\u0022\u005cn\u0022\n \t\u0022echo \u005c\u0022git_helper_bat: starting\u005c\u0022\u005cn\u0022\n \t\u0022set \u005c\u0022OPERATION\u003d%~1\u005c\u0022\u005cn\u0022\n \t\u0022echo \u005c\u0022OPERATION: !OPERATION!\u005c\u0022\u005cn\u0022\n@@ -103,11 +104,10 @@ const char *git_helper_bat \u003d\n \t\u0022 echo \u005c\u0022HASH: !HASH!\u005c\u0022\u005cn\u0022\n \t\u0022 echo \u005c\u0022MIRROR_PATH: !MIRROR_PATH!\u005c\u0022\u005cn\u0022\n \t\u0022 :lock_wait\u005cn\u0022\n-\t\u0022 mkdir \u005c\u0022!MIRROR_PATH!.lock\u005c\u0022 2\u003enul\u005cn\u0022\n-\t\u0022 mkdir \u005c\u0022!MIRROR_PATH!\u005c\u0022 2\u003enul\u005cn\u0022\n+\t\u0022 mkdir \u005c\u0022!MIRROR_PATH!.lock\u005c\u0022\u005cn\u0022\n \t\u0022 if errorlevel 1 (\u005cn\u0022\n \t\u0022 echo \u005c\u0022git mirror locked, waiting...\u005c\u0022\u005cn\u0022\n-\t\u0022 C:\u005c\u005cWindows\u005c\u005cSystem32\u005c\u005ctimeout.exe /t 1 /nobreak \u003e nul\u005cn\u0022\n+\t\u0022 ping -n 2 127.0.0.1 \u003enul\u005cn\u0022\n \t\u0022 goto :lock_wait\u005cn\u0022\n \t\u0022 )\u005cn\u0022\n \t\u0022 if exist \u005c\u0022!MIRROR_PATH!\u005c\u005c\u005c\u005c.git\u005c\u0022 (\u005cn\u0022\ndiff --git a/src/builder/b-win32-service.c b/src/builder/b-win32-service.c\nindex aacab5d..e290e94 100644\n--- a/src/builder/b-win32-service.c\n+++ b/src/builder/b-win32-service.c\n@@ -20,7 +20,7 @@\n */\n \n #if defined(WIN32)\n-\n+#include \u003clibwebsockets.h\u003e\n #include \u003cwindows.h\u003e\n #include \u003ctchar.h\u003e\n #include \u003cstdio.h\u003e\ndiff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt\nindex e91bf34..0f413fc 100644\n--- a/src/web/CMakeLists.txt\n+++ b/src/web/CMakeLists.txt\n@@ -149,6 +149,7 @@ if (requirements)\n \t\t\t../../assets/virt-qemu.svg\n \t\t\t../../assets/w10.svg\n \t\t\t../../assets/w11.svg\n+\t\t\t../../assets/freebsd.svg\n \n \t\tDESTINATION \u0022${DATA_DIR}/sai/assets\u0022\n \t\tCOMPONENT web)\ndiff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c\nindex b6d0804..8570e0e 100644\n--- a/src/web/w-ws-browser.c\n+++ b/src/web/w-ws-browser.c\n@@ -950,7 +950,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss)\n \t\tlws_json_purify(esc1, pss-\u003eauth_user, sizeof(esc1) - 1, \u0026iu)\n \t);\n \n-\tsaiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, start,\n+\tsaiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,\n \t\t\t\t\t lws_ptr_diff_size_t(p, start),\n \t\t\t\t\t lws_write_ws_flags(LWS_WRITE_TEXT, 1, 0));\n \tp \u003d start;\n@@ -1007,7 +1007,7 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss)\n \t\tp +\u003d lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), \u0022{\u005c\u0022e\u005c\u0022:\u0022);\n \n \t\tif (lws_ptr_diff_size_t(end, p) \u003c 256) {\n-\t\t\tsaiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, start,\n+\t\t\tsaiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,\n \t\t\t\t\t\t\t lws_ptr_diff_size_t(p, start),\n \t\t\t\t\t\t\t lws_write_ws_flags(LWS_WRITE_TEXT, 0, 0));\n \t\t\tp \u003d start;\n","s":{"c":1765606914,"u": 7534}}
],"g": 10052,"chitpc": 0,"ehitpc": 0,"indexed":0
,
"ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}