libre-webui 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -29
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.js +44 -0
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.js.map +1 -0
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js +4 -4
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.js +6 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.js +17 -4
- package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresSecurityRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresSecurityRepositories.js +4 -0
- package/backend/dist/persistence/postgresSecurityRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresWorkComputerMigration.d.ts +8 -0
- package/backend/dist/persistence/postgresWorkComputerMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresWorkComputerMigration.js +37 -0
- package/backend/dist/persistence/postgresWorkComputerMigration.js.map +1 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.js +38 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.js.map +1 -0
- package/backend/dist/persistence/resourceTypes.d.ts +5 -0
- package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
- package/backend/dist/persistence/resourceTypes.js.map +1 -1
- package/backend/dist/persistence/securityTypes.d.ts +1 -0
- package/backend/dist/persistence/securityTypes.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.d.ts +3 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.js +73 -0
- package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.js +17 -7
- package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteSecurityRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteSecurityRepositories.js +6 -0
- package/backend/dist/persistence/sqliteSecurityRepositories.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.js +87 -0
- package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.d.ts.map +1 -1
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.js +7 -3
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.js.map +1 -1
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.d.ts.map +1 -1
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js +8 -4
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js.map +1 -1
- package/backend/dist/platform/workPersistence/types.d.ts +2 -0
- package/backend/dist/platform/workPersistence/types.d.ts.map +1 -1
- package/backend/dist/platform/workPersistence/types.js.map +1 -1
- package/backend/dist/routes/auth.d.ts.map +1 -1
- package/backend/dist/routes/auth.js +8 -4
- package/backend/dist/routes/auth.js.map +1 -1
- package/backend/dist/routes/automations.d.ts.map +1 -1
- package/backend/dist/routes/automations.js +5 -0
- package/backend/dist/routes/automations.js.map +1 -1
- package/backend/dist/routes/work.d.ts.map +1 -1
- package/backend/dist/routes/work.js +205 -1
- package/backend/dist/routes/work.js.map +1 -1
- package/backend/dist/services/authService.d.ts +2 -0
- package/backend/dist/services/authService.d.ts.map +1 -1
- package/backend/dist/services/authService.js +20 -0
- package/backend/dist/services/authService.js.map +1 -1
- package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
- package/backend/dist/services/automationSchedulerService.js +24 -0
- package/backend/dist/services/automationSchedulerService.js.map +1 -1
- package/backend/dist/services/automationService.d.ts +4 -1
- package/backend/dist/services/automationService.d.ts.map +1 -1
- package/backend/dist/services/automationService.js +27 -0
- package/backend/dist/services/automationService.js.map +1 -1
- package/backend/dist/services/mfaService.d.ts.map +1 -1
- package/backend/dist/services/mfaService.js +2 -3
- package/backend/dist/services/mfaService.js.map +1 -1
- package/backend/dist/services/webauthnService.d.ts.map +1 -1
- package/backend/dist/services/webauthnService.js +1 -0
- package/backend/dist/services/webauthnService.js.map +1 -1
- package/backend/dist/services/websocketTicketService.d.ts +1 -1
- package/backend/dist/services/websocketTicketService.d.ts.map +1 -1
- package/backend/dist/services/websocketTicketService.js +3 -1
- package/backend/dist/services/websocketTicketService.js.map +1 -1
- package/backend/dist/services/workAgentGuidance.d.ts +14 -0
- package/backend/dist/services/workAgentGuidance.d.ts.map +1 -1
- package/backend/dist/services/workAgentGuidance.js +38 -2
- package/backend/dist/services/workAgentGuidance.js.map +1 -1
- package/backend/dist/services/workAgentService.d.ts +9 -0
- package/backend/dist/services/workAgentService.d.ts.map +1 -1
- package/backend/dist/services/workAgentService.js +503 -13
- package/backend/dist/services/workAgentService.js.map +1 -1
- package/backend/dist/services/workComputerSetupService.d.ts +33 -0
- package/backend/dist/services/workComputerSetupService.d.ts.map +1 -0
- package/backend/dist/services/workComputerSetupService.js +164 -0
- package/backend/dist/services/workComputerSetupService.js.map +1 -0
- package/backend/dist/services/workComputerTeachService.d.ts +105 -0
- package/backend/dist/services/workComputerTeachService.d.ts.map +1 -0
- package/backend/dist/services/workComputerTeachService.js +513 -0
- package/backend/dist/services/workComputerTeachService.js.map +1 -0
- package/backend/dist/services/workKubernetesDriver.d.ts +8 -0
- package/backend/dist/services/workKubernetesDriver.d.ts.map +1 -1
- package/backend/dist/services/workKubernetesDriver.js +22 -0
- package/backend/dist/services/workKubernetesDriver.js.map +1 -1
- package/backend/dist/services/workModelProviderService.d.ts.map +1 -1
- package/backend/dist/services/workModelProviderService.js +102 -8
- package/backend/dist/services/workModelProviderService.js.map +1 -1
- package/backend/dist/services/workPolicyService.d.ts +8 -0
- package/backend/dist/services/workPolicyService.d.ts.map +1 -1
- package/backend/dist/services/workPolicyService.js +25 -0
- package/backend/dist/services/workPolicyService.js.map +1 -1
- package/backend/dist/services/workRuntimeDriver.d.ts +32 -0
- package/backend/dist/services/workRuntimeDriver.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeDriver.js +54 -8
- package/backend/dist/services/workRuntimeDriver.js.map +1 -1
- package/backend/dist/services/workRuntimeService.d.ts +212 -1
- package/backend/dist/services/workRuntimeService.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeService.js +1184 -9
- package/backend/dist/services/workRuntimeService.js.map +1 -1
- package/backend/dist/services/workRuntimeShared.d.ts +11 -1
- package/backend/dist/services/workRuntimeShared.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeShared.js +24 -1
- package/backend/dist/services/workRuntimeShared.js.map +1 -1
- package/backend/dist/services/workScreenControlService.d.ts +60 -0
- package/backend/dist/services/workScreenControlService.d.ts.map +1 -0
- package/backend/dist/services/workScreenControlService.js +201 -0
- package/backend/dist/services/workScreenControlService.js.map +1 -0
- package/backend/dist/services/workTaskService.d.ts +11 -0
- package/backend/dist/services/workTaskService.d.ts.map +1 -1
- package/backend/dist/services/workTaskService.js +31 -0
- package/backend/dist/services/workTaskService.js.map +1 -1
- package/backend/dist/types/index.d.ts +7 -0
- package/backend/dist/types/index.d.ts.map +1 -1
- package/backend/dist/types/index.js.map +1 -1
- package/backend/dist/types/work.d.ts +13 -0
- package/backend/dist/types/work.d.ts.map +1 -1
- package/backend/dist/utils/webauthn.d.ts +6 -2
- package/backend/dist/utils/webauthn.d.ts.map +1 -1
- package/backend/dist/utils/webauthn.js +32 -3
- package/backend/dist/utils/webauthn.js.map +1 -1
- package/backend/dist/websocketServer.d.ts.map +1 -1
- package/backend/dist/websocketServer.js +9 -0
- package/backend/dist/websocketServer.js.map +1 -1
- package/backend/dist/workScreenServer.d.ts +12 -0
- package/backend/dist/workScreenServer.d.ts.map +1 -0
- package/backend/dist/workScreenServer.js +291 -0
- package/backend/dist/workScreenServer.js.map +1 -0
- package/backend/package.json +1 -1
- package/deploy/work-computer/Dockerfile +48 -0
- package/deploy/work-computer/README.md +93 -0
- package/deploy/work-computer/fonts/fonts.css +335 -0
- package/deploy/work-computer/fonts/v16-aFTR7PB1QTsUX8KYvumzEY2tbYf-Vlh3uA.woff2 +0 -0
- package/deploy/work-computer/fonts/v16-aFTR7PB1QTsUX8KYvumzEYOtbYf-Vlg.woff2 +0 -0
- package/deploy/work-computer/fonts/v16-aFTU7PB1QTsUX8KYthSQBK6PYK3EXw.woff2 +0 -0
- package/deploy/work-computer/fonts/v16-aFTU7PB1QTsUX8KYthqQBK6PYK0.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggOxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggSxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggexSvfedN4.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggixSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggmxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggqxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPb54C_k3HqUtEw.woff2 +0 -0
- package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPb94C_k3HqUtEw.woff2 +0 -0
- package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPbF4C_k3HqU.woff2 +0 -0
- package/deploy/work-computer/start-computer.sh +155 -0
- package/deploy/work-computer/start-page.html +251 -0
- package/deploy/work-computer/tint2rc +76 -0
- package/deploy/work-computer/wallpaper.png +0 -0
- package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
- package/frontend/dist/assets/index-DGy6mo0B.js +33 -0
- package/frontend/dist/assets/index-DqLJKWF-.css +1 -0
- package/frontend/dist/index.html +31 -16
- package/frontend/dist/js/{ArtifactContainer-C9zNtCay.js → ArtifactContainer-DvIMxleC.js} +1 -1
- package/frontend/dist/js/{ArtifactDemoPage-C-EBFBCt.js → ArtifactDemoPage-BqYskFqc.js} +1 -1
- package/frontend/dist/js/{ArtifactSandboxFrame-CTA1j_d1.js → ArtifactSandboxFrame-9wuufHn6.js} +1 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-DXESMtPh.js +1 -0
- package/frontend/dist/js/AutomationsPage-DByxhIKj.js +1 -0
- package/frontend/dist/js/AvatarUpload-DnCGQ46o.js +1 -0
- package/frontend/dist/js/{Button-C0r3J9G_.js → Button-CpobxJ2P.js} +1 -1
- package/frontend/dist/js/CalendarPage-JywVP-9y.js +1 -0
- package/frontend/dist/js/ChannelsPage-B31rcARd.js +1 -0
- package/frontend/dist/js/ChatPage-CHXJY8m3.js +14 -0
- package/frontend/dist/js/{EvaluationsPage-FWIJu2fI.js → EvaluationsPage-CcmB5wdF.js} +1 -1
- package/frontend/dist/js/FirstTimeSetup-DDU7SowM.js +1 -0
- package/frontend/dist/js/GalleryPage-XfX_7iCf.js +1 -0
- package/frontend/dist/js/HomePage-DAZAkoAV.js +1 -0
- package/frontend/dist/js/LibreClawPage-De0gZmbF.js +1 -0
- package/frontend/dist/js/Logo-yKM43xGa.js +1 -0
- package/frontend/dist/js/LogoMark-PmpKQOre.js +1 -0
- package/frontend/dist/js/{ModelManagerModals-BY5TK38_.js → ModelManagerModals-hz2cZZ4a.js} +1 -1
- package/frontend/dist/js/ModelSelector-BoFehQy-.js +1 -0
- package/frontend/dist/js/NotesPage-BGPI3gG6.js +3 -0
- package/frontend/dist/js/OptimizedSyntaxHighlighter-DrejdNUm.js +1 -0
- package/frontend/dist/js/OptimizedSyntaxHighlighter-hNqZPLiB.js +1 -0
- package/frontend/dist/js/PasswordStrengthMeter-RwModpZ1.js +1 -0
- package/frontend/dist/js/PersonasPage-BkNRD2wO.js +13 -0
- package/frontend/dist/js/PluginUsagePage-Cqt7V2SQ.js +1 -0
- package/frontend/dist/js/RichMessageContent-B2QSyX-l.js +1 -0
- package/frontend/dist/js/RichMessageContent-CYEhT6uU.js +3 -0
- package/frontend/dist/js/SettingsModal-IbIvVyss.js +19 -0
- package/frontend/dist/js/{SettingsToggle-BIVyfGjl.js → SettingsToggle-3GxA96E0.js} +1 -1
- package/frontend/dist/js/ShareDialog-BoE4OVhV.js +1 -0
- package/frontend/dist/js/{SystemPage-BT07dTMT.js → SystemPage-B5ecz5H3.js} +1 -1
- package/frontend/dist/js/TurnstileWidget-DHU9IUM1.js +1 -0
- package/frontend/dist/js/UserManagementPage-C0VUfbMk.js +1 -0
- package/frontend/dist/js/WorkPage-EweYI5Dx.js +74 -0
- package/frontend/dist/js/api-Bbh2AHHN.js +1 -0
- package/frontend/dist/js/api-F0CnlVH8.js +14 -0
- package/frontend/dist/js/appNavigation-DqV69ohR.js +1 -0
- package/frontend/dist/js/appStore-SLLBamo2.js +2 -0
- package/frontend/dist/js/appVersion-46bF9UkF.js +1 -0
- package/frontend/dist/js/ar-C4yo7x5J.js +2 -0
- package/frontend/dist/js/authStore-CcfL8T6T.js +1 -0
- package/frontend/dist/js/authStore-DlSQ1TcP.js +4 -0
- package/frontend/dist/js/axios-_ZpUYd3e.js +1 -0
- package/frontend/dist/js/bn-BeZ6d2qF.js +2 -0
- package/frontend/dist/js/chatStore-DI79tEJ-.js +3 -0
- package/frontend/dist/js/classPrivateMethodInitSpec-B31Q4d2M.js +1 -0
- package/frontend/dist/js/{config-BqZ5304Q.js → config-0QgGZCrI.js} +1 -1
- package/frontend/dist/js/cs-9K5LILI5.js +2 -0
- package/frontend/dist/js/da-BWbBhLK8.js +2 -0
- package/frontend/dist/js/de-BXD9t2uY.js +2 -0
- package/frontend/dist/js/es-Dv4wZKlU.js +2 -0
- package/frontend/dist/js/{estree-DPkpBuOU.js → estree-WDwviHUZ.js} +1 -1
- package/frontend/dist/js/evaluationsApi-wjf0nh5J.js +1 -0
- package/frontend/dist/js/fr-BkXGyEei.js +2 -0
- package/frontend/dist/js/hi-BS1ldbWk.js +2 -0
- package/frontend/dist/js/{html-BpGUXG8E.js → html-Cj0HNcRt.js} +1 -1
- package/frontend/dist/js/id-Cr1njx5M.js +2 -0
- package/frontend/dist/js/is-BqpNCtDd.js +2 -0
- package/frontend/dist/js/{it-AxEMG8Jg.js → it-Dp-iwH27.js} +1 -1
- package/frontend/dist/js/ja-_e4v_6Mn.js +2 -0
- package/frontend/dist/js/ko-CjXf2pIX.js +2 -0
- package/frontend/dist/js/{logger-DSXjF4ye.js → logger-CpCQz3Vi.js} +1 -1
- package/frontend/dist/js/{markdown-DCBRWJSK.js → markdown-DTahdtRF.js} +1 -1
- package/frontend/dist/js/{messageCodeStyles-BQwf7sSz.js → messageCodeStyles-COJFoq4P.js} +2 -2
- package/frontend/dist/js/{ms-BhEwb4b7.js → ms-xpn-zACj.js} +1 -1
- package/frontend/dist/js/nl-Baf0a2X0.js +2 -0
- package/frontend/dist/js/pl-BEOTrcUv.js +2 -0
- package/frontend/dist/js/pt-B5FUaihM.js +2 -0
- package/frontend/dist/js/rfb-bJaPz33m.js +5 -0
- package/frontend/dist/js/ru-BjzgMmkJ.js +2 -0
- package/frontend/dist/js/{standalone-BFrK0R2A.js → standalone-C04jT26m.js} +1 -1
- package/frontend/dist/js/sv-DDX_gP1z.js +2 -0
- package/frontend/dist/js/teamEventStream-CgUkgjFC.js +8 -0
- package/frontend/dist/js/{th-DVnW2yyh.js → th-BXr3KovE.js} +1 -1
- package/frontend/dist/js/tr-CdqxHEM8.js +2 -0
- package/frontend/dist/js/ttsBatching-BfhNT3c5.js +2 -0
- package/frontend/dist/js/{typescript-DYVyvmm_.js → typescript-FtGI7Y5P.js} +1 -1
- package/frontend/dist/js/ui-DFa2W4QC.js +2 -0
- package/frontend/dist/js/ui-vendor-CRZXLK0Q.js +177 -0
- package/frontend/dist/js/{uk-BNPKIZga.js → uk-CotKDP2Q.js} +1 -1
- package/frontend/dist/js/useChat-BawGm7ua.js +10 -0
- package/frontend/dist/js/useQuery-BboI5V5G.js +1 -0
- package/frontend/dist/js/{useTranslation-5b5UhAc9.js → useTranslation-D7uLySn1.js} +1 -1
- package/frontend/dist/js/{utils-BGrdX9ie.js → utils-sD-qFh8D.js} +1 -1
- package/frontend/dist/js/utils-vendor-DTLgmsuc.js +9 -0
- package/frontend/dist/js/vi-F2NC1HaJ.js +2 -0
- package/frontend/dist/js/webauthnClient-CPSq528Q.js +1 -0
- package/frontend/dist/js/workApi-8mBuh_-J.js +6 -0
- package/frontend/dist/js/workApi-DJtLOslf.js +1 -0
- package/frontend/dist/js/workStatus-CcO9Fg41.js +1 -0
- package/frontend/dist/js/zh-DZ-kpEEA.js +2 -0
- package/package.json +4 -2
- package/frontend/dist/assets/index-DBAf_VaS.js +0 -47
- package/frontend/dist/assets/index-kn_Axjts.css +0 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-BOENbOiv.js +0 -1
- package/frontend/dist/js/AutomationsPage-C58MQ4te.js +0 -1
- package/frontend/dist/js/CalendarPage-BcdAZ292.js +0 -1
- package/frontend/dist/js/ChannelsPage-DPr9Yajs.js +0 -1
- package/frontend/dist/js/ChatPage-DsbB748s.js +0 -14
- package/frontend/dist/js/FirstTimeSetup-Cy-w6xko.js +0 -1
- package/frontend/dist/js/GalleryPage-DDAtxM-x.js +0 -1
- package/frontend/dist/js/HomePage-DLKwmE7n.js +0 -1
- package/frontend/dist/js/LibreClawPage-vKOj-9w9.js +0 -1
- package/frontend/dist/js/ModelSelector-CoCS6MZ9.js +0 -1
- package/frontend/dist/js/NotesPage-BElIpXgA.js +0 -3
- package/frontend/dist/js/OptimizedSyntaxHighlighter-xgyE3dRA.js +0 -1
- package/frontend/dist/js/PersonasPage-P40jVbfU.js +0 -13
- package/frontend/dist/js/PluginUsagePage-CKUEPCJN.js +0 -1
- package/frontend/dist/js/RichMessageContent-Bm2WaEGi.js +0 -3
- package/frontend/dist/js/SettingsModal-DxSGzx9A.js +0 -19
- package/frontend/dist/js/UserManagementPage-B9Vz5cm3.js +0 -1
- package/frontend/dist/js/WorkPage-qoN_e_jK.js +0 -6
- package/frontend/dist/js/api-D7Ff5M2Q.js +0 -14
- package/frontend/dist/js/appStore-DmV5wqDz.js +0 -2
- package/frontend/dist/js/ar-YHHTVP8f.js +0 -2
- package/frontend/dist/js/authStore-BYW8-kP6.js +0 -4
- package/frontend/dist/js/bn-C8VTxBVl.js +0 -2
- package/frontend/dist/js/chatStore-D-NTlArO.js +0 -3
- package/frontend/dist/js/cs-CCngB5fC.js +0 -2
- package/frontend/dist/js/da-CM2PP2ma.js +0 -2
- package/frontend/dist/js/de-BqVkD-dw.js +0 -2
- package/frontend/dist/js/es-BtWsWBjZ.js +0 -2
- package/frontend/dist/js/evaluationsApi-Dnf2dTd-.js +0 -1
- package/frontend/dist/js/fr-C68w9abT.js +0 -2
- package/frontend/dist/js/hi-DtBlcyXZ.js +0 -2
- package/frontend/dist/js/id-BWzIu8tn.js +0 -2
- package/frontend/dist/js/is-yeFwu4dJ.js +0 -2
- package/frontend/dist/js/ja-B-VyokB8.js +0 -2
- package/frontend/dist/js/ko-CvwzYr9k.js +0 -2
- package/frontend/dist/js/nl-Dq-QRx1L.js +0 -2
- package/frontend/dist/js/pl-BNEy8mQm.js +0 -2
- package/frontend/dist/js/pt-_3Jl3FdY.js +0 -2
- package/frontend/dist/js/ru-IhQiJ8ta.js +0 -2
- package/frontend/dist/js/sv-DWunXUNz.js +0 -2
- package/frontend/dist/js/tr-Do8CFmhR.js +0 -2
- package/frontend/dist/js/ui-db9meqAB.js +0 -2
- package/frontend/dist/js/ui-vendor-C0DZ8-Cm.js +0 -177
- package/frontend/dist/js/utils-vendor-9bQ62WeD.js +0 -9
- package/frontend/dist/js/vi-CZMFo9je.js +0 -2
- package/frontend/dist/js/workApi-CcprMH_1.js +0 -6
- package/frontend/dist/js/zh-Pg0881v0.js +0 -2
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Libre WebUI
|
|
3
|
+
* Copyright (C) 2025 Kroonen AI, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at:
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Work Computer screen relay: bridges an authenticated browser WebSocket to
|
|
19
|
+
* the websockify endpoints inside a task's sandbox (screen and audio).
|
|
20
|
+
* Whether a connection can drive is decided by VNC credentials: watchers
|
|
21
|
+
* hold the view-only password, the takeover-lease holder the control one.
|
|
22
|
+
*
|
|
23
|
+
* Auth mirrors the Work terminal exactly — a short-lived one-use ticket
|
|
24
|
+
* bound to the 'work-screen' audience and the task, then a live check that
|
|
25
|
+
* the current database state still grants Work access and task ownership.
|
|
26
|
+
* The relay itself is a raw byte pipe after both WebSocket handshakes: the
|
|
27
|
+
* browser and websockify speak RFB over WS framing end to end, so nothing
|
|
28
|
+
* here parses frames. The GUI policy gate and the published-port lookup run
|
|
29
|
+
* per connection; a task whose policy lost the GUI cannot be watched.
|
|
30
|
+
*/
|
|
31
|
+
import http from 'http';
|
|
32
|
+
import net from 'net';
|
|
33
|
+
import { websocketTicketService, WebSocketTicketCoordinationError, } from './services/websocketTicketService.js';
|
|
34
|
+
import { requireCurrentTerminalTask } from './workTerminalServer.js';
|
|
35
|
+
import workRuntimeService from './services/workRuntimeService.js';
|
|
36
|
+
import workPolicyService from './services/workPolicyService.js';
|
|
37
|
+
import { WorkRuntimeError } from './services/workRuntimeShared.js';
|
|
38
|
+
import { createLogger } from './utils/logger.js';
|
|
39
|
+
const logger = createLogger('work-screen');
|
|
40
|
+
export const WORK_SCREEN_WS_PATH = '/ws/work-screen';
|
|
41
|
+
export const WORK_AUDIO_WS_PATH = '/ws/work-audio';
|
|
42
|
+
/** Concurrent viewers per task and surface; watching is cheap but not free. */
|
|
43
|
+
export const WORK_SCREEN_MAX_VIEWERS_PER_TASK = 4;
|
|
44
|
+
/** Screen viewing counts as task activity at this cadence (idle sweep). */
|
|
45
|
+
const ACTIVITY_TICK_MS = 60000;
|
|
46
|
+
/** Response headers a WebSocket client may see from the upstream 101. */
|
|
47
|
+
const SAFE_RESPONSE_HEADERS = new Set([
|
|
48
|
+
'upgrade',
|
|
49
|
+
'connection',
|
|
50
|
+
'sec-websocket-accept',
|
|
51
|
+
'sec-websocket-protocol',
|
|
52
|
+
'sec-websocket-extensions',
|
|
53
|
+
]);
|
|
54
|
+
const viewersByTask = new Map();
|
|
55
|
+
const httpError = (socket, status, message) => {
|
|
56
|
+
if (!socket.destroyed) {
|
|
57
|
+
socket.end(`HTTP/1.1 ${status} ${http.STATUS_CODES[status] || 'Error'}\r\n` +
|
|
58
|
+
`Content-Type: text/plain\r\nConnection: close\r\n\r\n${message}`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
async function authorizeScreenRequest(req) {
|
|
62
|
+
const url = new URL(req.url || '', 'http://localhost');
|
|
63
|
+
const ticket = url.searchParams.get('ticket') || '';
|
|
64
|
+
const taskId = url.searchParams.get('taskId') || '';
|
|
65
|
+
if (!ticket || !taskId) {
|
|
66
|
+
throw new WorkRuntimeError('The Work Computer screen requires authentication.', 401, 'WORK_SCREEN_UNAUTHORIZED');
|
|
67
|
+
}
|
|
68
|
+
let consumed;
|
|
69
|
+
try {
|
|
70
|
+
consumed = await websocketTicketService.consume(ticket, 'work-screen', taskId);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof WebSocketTicketCoordinationError) {
|
|
74
|
+
throw new WorkRuntimeError('Work Computer authentication is temporarily unavailable.', 503, 'WORK_SCREEN_AUTH_UNAVAILABLE');
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
if (!consumed) {
|
|
79
|
+
throw new WorkRuntimeError('Invalid or expired screen session ticket.', 401, 'WORK_SCREEN_UNAUTHORIZED');
|
|
80
|
+
}
|
|
81
|
+
// Same live re-check the terminal uses: active account, current Work
|
|
82
|
+
// access mode, and task ownership — a revocation is effective immediately.
|
|
83
|
+
const task = await requireCurrentTerminalTask(consumed.userId, taskId);
|
|
84
|
+
return {
|
|
85
|
+
task,
|
|
86
|
+
userId: consumed.userId,
|
|
87
|
+
...(consumed.sessionExpiresAt
|
|
88
|
+
? { sessionExpiresAt: consumed.sessionExpiresAt }
|
|
89
|
+
: {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Claims upgrade requests for the screen path. Returns true when handled.
|
|
94
|
+
* Registered from the shared upgrade dispatcher after origin filtering.
|
|
95
|
+
*/
|
|
96
|
+
export function tryHandleScreenUpgrade(request, socket, head) {
|
|
97
|
+
let pathname;
|
|
98
|
+
try {
|
|
99
|
+
pathname = new URL(request.url || '', 'http://localhost').pathname;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const surface = pathname === WORK_SCREEN_WS_PATH
|
|
105
|
+
? 'screen'
|
|
106
|
+
: pathname === WORK_AUDIO_WS_PATH
|
|
107
|
+
? 'audio'
|
|
108
|
+
: undefined;
|
|
109
|
+
if (!surface)
|
|
110
|
+
return false;
|
|
111
|
+
socket.pause();
|
|
112
|
+
void handleScreenUpgrade(request, socket, head, surface).catch(error => {
|
|
113
|
+
logger.warn(`Work ${surface} upgrade failed:`, error);
|
|
114
|
+
if (!socket.destroyed)
|
|
115
|
+
socket.destroy();
|
|
116
|
+
});
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
async function handleScreenUpgrade(request, socket, head, surface) {
|
|
120
|
+
let auth;
|
|
121
|
+
try {
|
|
122
|
+
auth = await authorizeScreenRequest(request);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const status = error instanceof WorkRuntimeError ? error.status : 500;
|
|
126
|
+
const message = error instanceof WorkRuntimeError
|
|
127
|
+
? error.message
|
|
128
|
+
: 'Screen authorization failed.';
|
|
129
|
+
httpError(socket, status, message);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const { task } = auth;
|
|
133
|
+
const policy = await workPolicyService.resolve(task.policyId);
|
|
134
|
+
if (policy.guiEnabled !== true) {
|
|
135
|
+
httpError(socket, 403, 'This task has no Work Computer.');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const viewerKey = `${surface}:${task.id}`;
|
|
139
|
+
if ((viewersByTask.get(viewerKey) ?? 0) >= WORK_SCREEN_MAX_VIEWERS_PER_TASK) {
|
|
140
|
+
httpError(socket, 409, 'Too many viewers on this screen.');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
let endpoint;
|
|
144
|
+
try {
|
|
145
|
+
endpoint =
|
|
146
|
+
surface === 'audio'
|
|
147
|
+
? await workRuntimeService.driver.audioEndpoint(task)
|
|
148
|
+
: await workRuntimeService.driver.screenEndpoint(task);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
logger.warn(`Work ${surface} endpoint lookup failed for ${task.id}:`, error);
|
|
152
|
+
}
|
|
153
|
+
if (!endpoint) {
|
|
154
|
+
httpError(socket, 409, 'The Work Computer screen is not running.');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
socket.resume();
|
|
158
|
+
// Forward only the WebSocket handshake headers websockify needs. The
|
|
159
|
+
// upstream leg is a raw TCP socket with a hand-written handshake: Node's
|
|
160
|
+
// HTTP-client upgrade path proved to tear the connection down shortly
|
|
161
|
+
// after large frames transit, while a plain piped socket streams the VNC
|
|
162
|
+
// session indefinitely.
|
|
163
|
+
const headerLines = [
|
|
164
|
+
`GET / HTTP/1.1`,
|
|
165
|
+
`Host: ${endpoint.host}:${endpoint.port}`,
|
|
166
|
+
`Connection: Upgrade`,
|
|
167
|
+
`Upgrade: ${request.headers.upgrade || 'websocket'}`,
|
|
168
|
+
];
|
|
169
|
+
for (const name of [
|
|
170
|
+
'sec-websocket-key',
|
|
171
|
+
'sec-websocket-version',
|
|
172
|
+
'sec-websocket-protocol',
|
|
173
|
+
'sec-websocket-extensions',
|
|
174
|
+
]) {
|
|
175
|
+
const value = request.headers[name];
|
|
176
|
+
if (typeof value === 'string')
|
|
177
|
+
headerLines.push(`${name}: ${value}`);
|
|
178
|
+
}
|
|
179
|
+
socket.once('error', error => {
|
|
180
|
+
logger.debug('Screen client socket closed with an error:', error);
|
|
181
|
+
});
|
|
182
|
+
const upstreamSocket = net.connect({
|
|
183
|
+
host: endpoint.host,
|
|
184
|
+
port: endpoint.port,
|
|
185
|
+
});
|
|
186
|
+
upstreamSocket.setNoDelay(true);
|
|
187
|
+
let upstreamBuffer = Buffer.alloc(0);
|
|
188
|
+
let established = false;
|
|
189
|
+
const failUpstream = (message) => {
|
|
190
|
+
logger.warn(`Work screen relay failed: ${message}`);
|
|
191
|
+
if (!established)
|
|
192
|
+
httpError(socket, 502, message);
|
|
193
|
+
upstreamSocket.destroy();
|
|
194
|
+
if (established)
|
|
195
|
+
socket.destroy();
|
|
196
|
+
};
|
|
197
|
+
upstreamSocket.once('error', error => {
|
|
198
|
+
logger.debug('Screen upstream socket closed with an error:', error);
|
|
199
|
+
failUpstream('The Work Computer screen is unreachable.');
|
|
200
|
+
});
|
|
201
|
+
upstreamSocket.setTimeout(10000, () => {
|
|
202
|
+
if (!established)
|
|
203
|
+
failUpstream('The Work Computer screen timed out.');
|
|
204
|
+
});
|
|
205
|
+
upstreamSocket.once('connect', () => {
|
|
206
|
+
upstreamSocket.write(`${headerLines.join('\r\n')}\r\n\r\n`);
|
|
207
|
+
});
|
|
208
|
+
const onHandshakeData = (chunk) => {
|
|
209
|
+
upstreamBuffer = Buffer.concat([upstreamBuffer, chunk]);
|
|
210
|
+
const headerEnd = upstreamBuffer.indexOf('\r\n\r\n');
|
|
211
|
+
if (headerEnd < 0) {
|
|
212
|
+
if (upstreamBuffer.length > 16384) {
|
|
213
|
+
failUpstream('The Work Computer screen sent an invalid handshake.');
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
upstreamSocket.off('data', onHandshakeData);
|
|
218
|
+
const rawResponse = upstreamBuffer.subarray(0, headerEnd).toString();
|
|
219
|
+
const remainder = upstreamBuffer.subarray(headerEnd + 4);
|
|
220
|
+
upstreamBuffer = Buffer.alloc(0);
|
|
221
|
+
const [statusLine = '', ...responseHeaderLines] = rawResponse.split('\r\n');
|
|
222
|
+
if (!/^HTTP\/1\.1 101 /.test(statusLine)) {
|
|
223
|
+
failUpstream('Screen upgrade failed.');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const safeLines = responseHeaderLines.filter(line => {
|
|
227
|
+
const name = line.slice(0, line.indexOf(':')).trim().toLowerCase();
|
|
228
|
+
return SAFE_RESPONSE_HEADERS.has(name);
|
|
229
|
+
});
|
|
230
|
+
established = true;
|
|
231
|
+
upstreamSocket.setTimeout(0);
|
|
232
|
+
// A raw upgraded socket keeps the HTTP server's keep-alive timeout
|
|
233
|
+
// unless it is cleared — the ws library does this for the terminal; a
|
|
234
|
+
// raw relay must do it itself.
|
|
235
|
+
socket.setTimeout?.(0);
|
|
236
|
+
socket.setNoDelay?.(true);
|
|
237
|
+
viewersByTask.set(viewerKey, (viewersByTask.get(viewerKey) ?? 0) + 1);
|
|
238
|
+
workRuntimeService.noteTaskActivity(task.id);
|
|
239
|
+
// The viewer owns the running container for the connection's lifetime:
|
|
240
|
+
// without this hold, any workspace-helper call (a Files refresh) would
|
|
241
|
+
// stop the sandbox and kill the GUI session mid-view.
|
|
242
|
+
let releaseScreenSession;
|
|
243
|
+
void workRuntimeService
|
|
244
|
+
.beginScreenSession(task)
|
|
245
|
+
.then(release => {
|
|
246
|
+
releaseScreenSession = release;
|
|
247
|
+
if (socket.destroyed)
|
|
248
|
+
void release();
|
|
249
|
+
})
|
|
250
|
+
.catch(error => {
|
|
251
|
+
logger.warn(`Could not hold the Work container for a screen session on ${task.id}:`, error);
|
|
252
|
+
});
|
|
253
|
+
const activityTimer = setInterval(() => {
|
|
254
|
+
workRuntimeService.noteTaskActivity(task.id);
|
|
255
|
+
}, ACTIVITY_TICK_MS);
|
|
256
|
+
activityTimer.unref();
|
|
257
|
+
// The ticket's backing auth session bounds how long a viewer may stay.
|
|
258
|
+
const expiryTimer = auth.sessionExpiresAt
|
|
259
|
+
? setTimeout(() => socket.destroy(), Math.max(0, auth.sessionExpiresAt - Date.now()))
|
|
260
|
+
: undefined;
|
|
261
|
+
expiryTimer?.unref();
|
|
262
|
+
socket.write(`HTTP/1.1 101 Switching Protocols\r\n${safeLines.join('\r\n')}\r\n\r\n`);
|
|
263
|
+
if (remainder.length > 0)
|
|
264
|
+
socket.write(remainder);
|
|
265
|
+
if (head.length > 0)
|
|
266
|
+
upstreamSocket.write(head);
|
|
267
|
+
const cleanup = () => {
|
|
268
|
+
clearInterval(activityTimer);
|
|
269
|
+
if (expiryTimer)
|
|
270
|
+
clearTimeout(expiryTimer);
|
|
271
|
+
const remaining = (viewersByTask.get(viewerKey) ?? 1) - 1;
|
|
272
|
+
if (remaining <= 0)
|
|
273
|
+
viewersByTask.delete(viewerKey);
|
|
274
|
+
else
|
|
275
|
+
viewersByTask.set(viewerKey, remaining);
|
|
276
|
+
workRuntimeService.noteTaskActivity(task.id);
|
|
277
|
+
void releaseScreenSession?.();
|
|
278
|
+
};
|
|
279
|
+
upstreamSocket.once('close', () => {
|
|
280
|
+
socket.destroy();
|
|
281
|
+
});
|
|
282
|
+
socket.once('close', () => {
|
|
283
|
+
upstreamSocket.destroy();
|
|
284
|
+
cleanup();
|
|
285
|
+
});
|
|
286
|
+
upstreamSocket.pipe(socket);
|
|
287
|
+
socket.pipe(upstreamSocket);
|
|
288
|
+
};
|
|
289
|
+
upstreamSocket.on('data', onHandshakeData);
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=workScreenServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workScreenServer.js","sourceRoot":"","sources":["../src/workScreenServer.ts"],"names":[],"mappings":"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"}
|
package/backend/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Libre Work Computer — GUI-capable Work sandbox.
|
|
2
|
+
#
|
|
3
|
+
# Layers a virtual desktop (Xvfb + fluxbox), Chromium, and a localhost-only
|
|
4
|
+
# VNC-to-WebSocket bridge over the standard Work image. xdotool and
|
|
5
|
+
# imagemagick ship now so later phases (agent control, teach mode) need no
|
|
6
|
+
# image change. Adds ~650 MB over the base image.
|
|
7
|
+
ARG WORK_BASE_IMAGE
|
|
8
|
+
FROM ${WORK_BASE_IMAGE}
|
|
9
|
+
|
|
10
|
+
USER root
|
|
11
|
+
|
|
12
|
+
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
13
|
+
xvfb \
|
|
14
|
+
openbox \
|
|
15
|
+
tint2 \
|
|
16
|
+
lxterminal \
|
|
17
|
+
adwaita-icon-theme \
|
|
18
|
+
x11vnc \
|
|
19
|
+
websockify \
|
|
20
|
+
chromium \
|
|
21
|
+
xdotool \
|
|
22
|
+
imagemagick \
|
|
23
|
+
x11-utils \
|
|
24
|
+
pulseaudio \
|
|
25
|
+
pulseaudio-utils \
|
|
26
|
+
socat \
|
|
27
|
+
dbus-x11 \
|
|
28
|
+
fonts-dejavu-core \
|
|
29
|
+
fonts-noto-color-emoji \
|
|
30
|
+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
31
|
+
|
|
32
|
+
COPY start-computer.sh /usr/local/bin/start-computer
|
|
33
|
+
RUN chmod +x /usr/local/bin/start-computer
|
|
34
|
+
|
|
35
|
+
# A presentable desktop instead of a bare X root: wallpaper, dock config,
|
|
36
|
+
# and a start page for the browser — static assets baked at build time so
|
|
37
|
+
# the runtime stays read-only. Drop a wallpaper.png beside this Dockerfile
|
|
38
|
+
# to ship your own (the [g] glob makes it optional); otherwise a gradient
|
|
39
|
+
# is generated.
|
|
40
|
+
COPY start-page.html tint2rc wallpaper.pn[g] /usr/local/share/libre-computer/
|
|
41
|
+
# The site's own typefaces (Space Grotesk / Manrope / DM Mono), self-hosted
|
|
42
|
+
# exactly like librewebui.org hosts them.
|
|
43
|
+
COPY fonts /usr/local/share/libre-computer/fonts
|
|
44
|
+
RUN [ -f /usr/local/share/libre-computer/wallpaper.png ] \
|
|
45
|
+
|| convert -size 1280x800 \
|
|
46
|
+
gradient:'#141824'-'#2b3350' \
|
|
47
|
+
-attenuate 0.15 +noise Gaussian \
|
|
48
|
+
/usr/local/share/libre-computer/wallpaper.png
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Libre Work Computer image
|
|
2
|
+
|
|
3
|
+
A GUI-capable variant of the Work sandbox: a virtual desktop (Xvfb +
|
|
4
|
+
openbox with a tint2 dock), Chromium, and a loopback-only VNC server bridged to a WebSocket
|
|
5
|
+
(x11vnc + websockify), layered over the standard Work base image. Tasks
|
|
6
|
+
running this image under a policy with **Work Computer** enabled get a live,
|
|
7
|
+
watchable screen in the Work view.
|
|
8
|
+
|
|
9
|
+
## Pull (recommended)
|
|
10
|
+
|
|
11
|
+
CI publishes this image on every change to this directory, built on the
|
|
12
|
+
exact Work base pin from the application source:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
docker pull ghcr.io/libre-webui/libre-work-computer:main # or :dev
|
|
16
|
+
docker tag ghcr.io/libre-webui/libre-work-computer:main libre-work-computer:latest
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Behind a filtered Docker API proxy that denies `/build`, this is the whole
|
|
20
|
+
install: pull and tag on the Docker host, then press **Enable**.
|
|
21
|
+
|
|
22
|
+
## Build
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
docker build \
|
|
26
|
+
--build-arg WORK_BASE_IMAGE="node:22.22-bookworm@sha256:2d178f2785b96dfbf62a416ca2e40f50e30150b4ff3320d706f0d96e90600eb3" \
|
|
27
|
+
-t libre-work-computer:latest \
|
|
28
|
+
deploy/work-computer
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use the same base your deployment already runs (the default above matches
|
|
32
|
+
the built-in `WORK_RUNTIME_IMAGE`), so shell tooling stays identical between
|
|
33
|
+
GUI and headless tasks.
|
|
34
|
+
|
|
35
|
+
## Enable it
|
|
36
|
+
|
|
37
|
+
The easy path: an administrator presses **Enable** on the Work Computer
|
|
38
|
+
card shown on the Work landing page. The backend builds this image from
|
|
39
|
+
its bundled copy of this directory and creates a ready **Work Computer**
|
|
40
|
+
policy automatically. When the image already exists on the daemon,
|
|
41
|
+
**Enable** skips the build and only creates the policy.
|
|
42
|
+
|
|
43
|
+
The manual path, for deployments that build images elsewhere — including
|
|
44
|
+
those behind a filtered Docker API proxy (such as
|
|
45
|
+
`deploy/private/docker-compose.work-proxy.yml`), which denies the build
|
|
46
|
+
endpoint on purpose:
|
|
47
|
+
|
|
48
|
+
1. Build (or push and pull) the image on the Docker host that runs Work
|
|
49
|
+
sandboxes.
|
|
50
|
+
2. In **User management → Work policies**, create or edit a policy: set its
|
|
51
|
+
**Image** to `libre-work-computer:latest` and check **Work Computer
|
|
52
|
+
(GUI + browser)**.
|
|
53
|
+
3. Start a Work task under that policy (network access on). The **Screen**
|
|
54
|
+
tab appears in the workspace pane; opening it starts the GUI session
|
|
55
|
+
and connects the screen.
|
|
56
|
+
|
|
57
|
+
## What runs inside
|
|
58
|
+
|
|
59
|
+
`start-computer` (idempotent, invoked by the server when the screen is
|
|
60
|
+
opened) starts: Xvfb on display `:1` (1280×800), openbox with a baked-in wallpaper and a tint2 dock (Chromium and terminal launchers, running apps, clock), lxterminal available, Chromium maximized on a branded start page with an
|
|
61
|
+
isolated profile persisted at `/workspace/.browser-profile` (logins survive
|
|
62
|
+
container restarts), x11vnc bound to `127.0.0.1`, and websockify listening
|
|
63
|
+
on container port 6080 — the only network-reachable surface. The backend
|
|
64
|
+
publishes that port on the Docker host's loopback and re-authenticates
|
|
65
|
+
every viewer with a one-use, task-bound ticket.
|
|
66
|
+
|
|
67
|
+
Audio: PulseAudio plays into a null sink; its monitor is captured per
|
|
68
|
+
connection (`socat` + `parec`, raw s16le 44.1 kHz stereo) and served over a
|
|
69
|
+
second websockify bridge on container port 6081, published and
|
|
70
|
+
authenticated exactly like the screen. Silence transmits nothing — Pulse
|
|
71
|
+
suspends idle sources.
|
|
72
|
+
|
|
73
|
+
The VNC session has two per-session passwords generated at start (0600
|
|
74
|
+
passwd file in the state dir): the entry before `__BEGIN_VIEWONLY__`
|
|
75
|
+
grants full mouse/keyboard control and is released by the backend only to
|
|
76
|
+
the current takeover-lease holder; the entry after it is view-only and
|
|
77
|
+
goes to every authorized watcher. The VNC server itself keeps view-only
|
|
78
|
+
connections' input inert, so a leaked watch credential cannot drive the
|
|
79
|
+
screen.
|
|
80
|
+
|
|
81
|
+
xdotool and ImageMagick power the agent's `computer_observe` and
|
|
82
|
+
`computer_act` tools (screenshots in, OS-level input out). Chromium also
|
|
83
|
+
exposes its DevTools endpoint on the container's loopback (port 9222,
|
|
84
|
+
never published): the observe/act scripts read the active tab URL, page
|
|
85
|
+
focus, and focused-element identity from it for focus assertions and
|
|
86
|
+
outcome verification. It shares the trust domain of the DISPLAY the
|
|
87
|
+
sandbox already fully controls. Images built before this flag keep
|
|
88
|
+
working — the semantic signals are simply absent, and focus assertions
|
|
89
|
+
then fail closed — but rebuild to get them.
|
|
90
|
+
|
|
91
|
+
Overhead relative to the base image: ~650 MB on disk; a running GUI session
|
|
92
|
+
adds roughly 300–800 MB of memory depending on browser use. Size policies
|
|
93
|
+
accordingly (2g memory is a workable floor, 4g is comfortable).
|