@mseep/open-computer-use 1.0.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.
Files changed (769) hide show
  1. package/.coderabbit.yaml +25 -0
  2. package/.dockerignore +95 -0
  3. package/.env.example +137 -0
  4. package/.githooks/pre-commit +68 -0
  5. package/.github/CODEOWNERS +125 -0
  6. package/.github/ISSUE_TEMPLATE/adr-proposal.md +41 -0
  7. package/.github/ISSUE_TEMPLATE/bug-report.md +49 -0
  8. package/.github/ISSUE_TEMPLATE/component-proposal.md +38 -0
  9. package/.github/ISSUE_TEMPLATE/config.yml +15 -0
  10. package/.github/ISSUE_TEMPLATE/dependency-proposal.md +59 -0
  11. package/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
  12. package/.github/ISSUE_TEMPLATE/nfr-proposal.md +44 -0
  13. package/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  14. package/.github/codeql/codeql-config.yml +11 -0
  15. package/.github/codeql/extensions/security-models/python-sanitizers.model.yml +17 -0
  16. package/.github/codeql/extensions/security-models/qlpack.yml +7 -0
  17. package/.github/dependabot.yml +23 -0
  18. package/.github/security-exceptions.yml +23 -0
  19. package/.github/workflows/build.yml +420 -0
  20. package/.github/workflows/codeql.yml +33 -0
  21. package/.github/workflows/contracts-lint.yml +90 -0
  22. package/.github/workflows/docs-lint.yml +151 -0
  23. package/.github/workflows/helm.yml +131 -0
  24. package/.github/workflows/identity-lint.yml +30 -0
  25. package/.github/workflows/release-chart.yml +177 -0
  26. package/.github/workflows/release.yml +95 -0
  27. package/.github/workflows/security.yml +332 -0
  28. package/.github/workflows/stale.yml +31 -0
  29. package/.github/workflows/supply-chain.yml +242 -0
  30. package/.gitleaks.toml +53 -0
  31. package/.markdownlint.yaml +51 -0
  32. package/.semgrepignore +85 -0
  33. package/.vale/styles/Architecture/ap13-data-class-substrate.yml +12 -0
  34. package/.vale/styles/Architecture/banned-phrases.yml +23 -0
  35. package/.vale/styles/Architecture/banned-vocab.yml +23 -0
  36. package/.vale/styles/Architecture/marketing-tone.yml +19 -0
  37. package/.vale.ini +18 -0
  38. package/CHANGELOG.md +411 -0
  39. package/CLAUDE.md +218 -0
  40. package/CONTRIBUTING.md +82 -0
  41. package/Dockerfile +676 -0
  42. package/LICENSE +98 -0
  43. package/LICENSE-APACHE +202 -0
  44. package/LICENSE-MIT +21 -0
  45. package/NOTICE +36 -0
  46. package/README.md +516 -0
  47. package/SECURITY.md +45 -0
  48. package/THIRD-PARTY-LICENSES.md +14 -0
  49. package/apt-packages.txt +108 -0
  50. package/computer-use-server/.dockerignore +13 -0
  51. package/computer-use-server/Dockerfile +44 -0
  52. package/computer-use-server/README.md +84 -0
  53. package/computer-use-server/app.py +1544 -0
  54. package/computer-use-server/bin/list-subagent-models +449 -0
  55. package/computer-use-server/cli-defaults/README.md +31 -0
  56. package/computer-use-server/cli-defaults/codex.json +7 -0
  57. package/computer-use-server/cli-defaults/opencode.json +18 -0
  58. package/computer-use-server/cli_adapters/__init__.py +46 -0
  59. package/computer-use-server/cli_adapters/claude.py +163 -0
  60. package/computer-use-server/cli_adapters/codex.py +163 -0
  61. package/computer-use-server/cli_adapters/opencode.py +169 -0
  62. package/computer-use-server/cli_adapters/result.py +34 -0
  63. package/computer-use-server/cli_runtime.py +316 -0
  64. package/computer-use-server/context_vars.py +24 -0
  65. package/computer-use-server/docker_manager.py +1100 -0
  66. package/computer-use-server/docs_html.py +12 -0
  67. package/computer-use-server/mcp_resources.py +170 -0
  68. package/computer-use-server/mcp_tools.py +1430 -0
  69. package/computer-use-server/requirements.txt +17 -0
  70. package/computer-use-server/security.py +50 -0
  71. package/computer-use-server/skill_manager.py +664 -0
  72. package/computer-use-server/static/browser-viewer.js +445 -0
  73. package/computer-use-server/static/chart.umd.js +14 -0
  74. package/computer-use-server/static/docs.html +203 -0
  75. package/computer-use-server/static/github-dark.min.css +10 -0
  76. package/computer-use-server/static/github.min.css +10 -0
  77. package/computer-use-server/static/highlight.min.js +1213 -0
  78. package/computer-use-server/static/highlightjs-line-numbers.min.js +1 -0
  79. package/computer-use-server/static/icons.js +74 -0
  80. package/computer-use-server/static/jszip.min.js +13 -0
  81. package/computer-use-server/static/katex/auto-render.min.js +1 -0
  82. package/computer-use-server/static/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
  83. package/computer-use-server/static/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
  84. package/computer-use-server/static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  85. package/computer-use-server/static/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  86. package/computer-use-server/static/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  87. package/computer-use-server/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  88. package/computer-use-server/static/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  89. package/computer-use-server/static/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  90. package/computer-use-server/static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  91. package/computer-use-server/static/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  92. package/computer-use-server/static/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  93. package/computer-use-server/static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  94. package/computer-use-server/static/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  95. package/computer-use-server/static/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  96. package/computer-use-server/static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  97. package/computer-use-server/static/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
  98. package/computer-use-server/static/katex/fonts/KaTeX_Main-Bold.woff +0 -0
  99. package/computer-use-server/static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  100. package/computer-use-server/static/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  101. package/computer-use-server/static/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  102. package/computer-use-server/static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  103. package/computer-use-server/static/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
  104. package/computer-use-server/static/katex/fonts/KaTeX_Main-Italic.woff +0 -0
  105. package/computer-use-server/static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  106. package/computer-use-server/static/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
  107. package/computer-use-server/static/katex/fonts/KaTeX_Main-Regular.woff +0 -0
  108. package/computer-use-server/static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  109. package/computer-use-server/static/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  110. package/computer-use-server/static/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  111. package/computer-use-server/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  112. package/computer-use-server/static/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
  113. package/computer-use-server/static/katex/fonts/KaTeX_Math-Italic.woff +0 -0
  114. package/computer-use-server/static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  115. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  116. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  117. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  118. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  119. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  120. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  121. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  122. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  123. package/computer-use-server/static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  124. package/computer-use-server/static/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
  125. package/computer-use-server/static/katex/fonts/KaTeX_Script-Regular.woff +0 -0
  126. package/computer-use-server/static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  127. package/computer-use-server/static/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
  128. package/computer-use-server/static/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
  129. package/computer-use-server/static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  130. package/computer-use-server/static/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
  131. package/computer-use-server/static/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
  132. package/computer-use-server/static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  133. package/computer-use-server/static/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
  134. package/computer-use-server/static/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
  135. package/computer-use-server/static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  136. package/computer-use-server/static/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
  137. package/computer-use-server/static/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
  138. package/computer-use-server/static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  139. package/computer-use-server/static/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  140. package/computer-use-server/static/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  141. package/computer-use-server/static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  142. package/computer-use-server/static/katex/katex.min.css +1 -0
  143. package/computer-use-server/static/katex/katex.min.js +1 -0
  144. package/computer-use-server/static/locale.js +242 -0
  145. package/computer-use-server/static/mammoth.browser.min.js +21 -0
  146. package/computer-use-server/static/marked.min.js +6 -0
  147. package/computer-use-server/static/mermaid.min.js +2811 -0
  148. package/computer-use-server/static/pdf.min.js +22 -0
  149. package/computer-use-server/static/pdf.worker.min.js +22 -0
  150. package/computer-use-server/static/pptxviewjs.min.js +1 -0
  151. package/computer-use-server/static/preact-htm.min.js +1 -0
  152. package/computer-use-server/static/preview.css +1030 -0
  153. package/computer-use-server/static/preview.js +1522 -0
  154. package/computer-use-server/static/xlsx.full.min.js +22 -0
  155. package/computer-use-server/static/xterm-addon-fit.min.js +2 -0
  156. package/computer-use-server/static/xterm-addon-web-links.min.js +2 -0
  157. package/computer-use-server/static/xterm.css +218 -0
  158. package/computer-use-server/static/xterm.min.js +2 -0
  159. package/computer-use-server/system_prompt.py +761 -0
  160. package/computer-use-server/uploads.py +82 -0
  161. package/contracts/README.md +53 -0
  162. package/contracts/audit/audit-fanin.asyncapi.yaml +407 -0
  163. package/contracts/exec/exec-channel.schema.json +240 -0
  164. package/contracts/mcp/2025-06-18/ocu-constraints.schema.json +178 -0
  165. package/contracts/storage/file-artifact-api.schema.json +390 -0
  166. package/contracts/storage/file-ops.schema.json +217 -0
  167. package/contracts/storage/mount-config.schema.json +197 -0
  168. package/cron/Dockerfile +15 -0
  169. package/cron/cleanup-quick.sh +21 -0
  170. package/cron/cleanup.sh +127 -0
  171. package/data/outputs/.gitkeep +0 -0
  172. package/data/uploads/.gitkeep +0 -0
  173. package/docker-compose.test.yml +54 -0
  174. package/docker-compose.webui.yml +77 -0
  175. package/docker-compose.yml +96 -0
  176. package/docs/CLOUD.md +29 -0
  177. package/docs/COMPARISON.md +128 -0
  178. package/docs/DOCKER.md +469 -0
  179. package/docs/DYNAMIC-SKILLS.md +77 -0
  180. package/docs/FEATURES.md +100 -0
  181. package/docs/INSTALL.md +111 -0
  182. package/docs/KNOWN-BUGS.md +86 -0
  183. package/docs/MCP.md +320 -0
  184. package/docs/SCREENSHOTS.md +39 -0
  185. package/docs/SKILLS-USER-GUIDE.md +86 -0
  186. package/docs/SKILLS.md +483 -0
  187. package/docs/TERMINAL-TAB.md +56 -0
  188. package/docs/architecture/02-trust-boundaries.md +224 -0
  189. package/docs/architecture/03-c4-context.md +61 -0
  190. package/docs/architecture/04-bounded-contexts.md +119 -0
  191. package/docs/architecture/05-c4-container.md +88 -0
  192. package/docs/architecture/06-threat-model.md +172 -0
  193. package/docs/architecture/08-contracts.md +105 -0
  194. package/docs/architecture/MANIFESTO.md +38 -0
  195. package/docs/architecture/PROCESS.md +64 -0
  196. package/docs/architecture/README.md +37 -0
  197. package/docs/architecture/adr/0000-template.md +65 -0
  198. package/docs/architecture/adr/0001-layer-0-gate-legacy-exclusion.md +75 -0
  199. package/docs/architecture/adr/0002-session-view-descriptor.md +57 -0
  200. package/docs/architecture/adr/0003-sandbox-runtime-tier-ladder.md +63 -0
  201. package/docs/architecture/adr/0004-operator-authentication-substrate.md +63 -0
  202. package/docs/architecture/adr/0005-egress-credential-delivery-envoy-sds.md +62 -0
  203. package/docs/architecture/adr/0006-egress-forward-proxy-substrate.md +65 -0
  204. package/docs/architecture/adr/0007-egress-auth-mechanism.md +72 -0
  205. package/docs/architecture/adr/0008-session-egress-attribution.md +59 -0
  206. package/docs/architecture/adr/0009-audit-pipeline-pluggable-by-contract.md +76 -0
  207. package/docs/architecture/adr/0010-storage-backend-pluggable-adapter.md +60 -0
  208. package/docs/architecture/adr/0011-storage-egress-lane.md +67 -0
  209. package/docs/architecture/adr/0012-implementation-language.md +67 -0
  210. package/docs/architecture/adr/0020-sandbox-image-provisioning.md +82 -0
  211. package/docs/architecture/adr/README.md +53 -0
  212. package/docs/architecture/compliance/.gitkeep +0 -0
  213. package/docs/architecture/components/00-overview.md +42 -0
  214. package/docs/architecture/components/0000-template.md +50 -0
  215. package/docs/architecture/components/01-mcp-gateway.md +80 -0
  216. package/docs/architecture/components/02-control-operator-api.md +80 -0
  217. package/docs/architecture/components/04-storage-broker.md +104 -0
  218. package/docs/architecture/components/05-session-sandbox.md +93 -0
  219. package/docs/architecture/components/06-egress-trust-edge.md +95 -0
  220. package/docs/architecture/components/07-audit-pipeline.md +110 -0
  221. package/docs/architecture/diagrams/.gitkeep +0 -0
  222. package/docs/architecture/diagrams/02-trust-boundaries.mmd +111 -0
  223. package/docs/architecture/diagrams/06-threat-model.mmd +41 -0
  224. package/docs/architecture/diagrams/08-contracts.mmd +47 -0
  225. package/docs/architecture/diagrams/c4-container.mmd +59 -0
  226. package/docs/architecture/diagrams/c4-context.mmd +46 -0
  227. package/docs/architecture/glossary.md +172 -0
  228. package/docs/architecture/manifesto/.gitkeep +0 -0
  229. package/docs/architecture/manifesto/01-audience-and-buyer.md +57 -0
  230. package/docs/architecture/manifesto/02-nfrs.md +325 -0
  231. package/docs/architecture/manifesto/03-non-negotiables.md +35 -0
  232. package/docs/architecture/manifesto/04-non-goals.md +23 -0
  233. package/docs/architecture/manifesto/05-licensing-posture.md +61 -0
  234. package/docs/architecture/manifesto/06-starter-mode-policy.md +49 -0
  235. package/docs/architecture/manifesto/07-governance.md +60 -0
  236. package/docs/architecture/primitives-backlog.md +51 -0
  237. package/docs/architecture.svg +117 -0
  238. package/docs/claude-code-gateway.md +173 -0
  239. package/docs/cli-config-templates.md +240 -0
  240. package/docs/data-flow.svg +72 -0
  241. package/docs/demo-landing-page.gif +0 -0
  242. package/docs/demo-qwen-trending.gif +0 -0
  243. package/docs/dynamic-skills.svg +77 -0
  244. package/docs/file-flow.svg +126 -0
  245. package/docs/future-architecture/README.md +152 -0
  246. package/docs/future-architecture/adr/0001-control-plane-language-go.md +80 -0
  247. package/docs/future-architecture/adr/0002-guest-agent-language-go.md +84 -0
  248. package/docs/future-architecture/adr/0003-docker-poc-first-then-k8s.md +37 -0
  249. package/docs/future-architecture/adr/0004-pluggable-runtime-via-runtimeclass.md +34 -0
  250. package/docs/future-architecture/adr/0005-mcp-as-control-plane-gateway.md +34 -0
  251. package/docs/future-architecture/adr/0006-no-agpl-no-bsl-dependencies.md +41 -0
  252. package/docs/future-architecture/adr/0007-superseded-by-future-architecture.md +37 -0
  253. package/docs/future-architecture/adr/0008-internal-grpc-external-rest-mcp.md +106 -0
  254. package/docs/future-architecture/adr/0009-external-protocol-dialects.md +94 -0
  255. package/docs/future-architecture/adr/0010-lambda-as-inspiration-not-runtime.md +86 -0
  256. package/docs/future-architecture/adr/0011-kata-as-first-class-dind-runtime.md +84 -0
  257. package/docs/future-architecture/antipatterns.md +552 -0
  258. package/docs/future-architecture/architecture/01-layers.md +109 -0
  259. package/docs/future-architecture/architecture/02-layer4-control-plane.md +122 -0
  260. package/docs/future-architecture/architecture/03-layer3-providers.md +174 -0
  261. package/docs/future-architecture/architecture/04-layer2-runtimes.md +114 -0
  262. package/docs/future-architecture/architecture/04b-credential-broker.md +153 -0
  263. package/docs/future-architecture/architecture/05-layer1-guest-agent.md +138 -0
  264. package/docs/future-architecture/architecture/06-storage.md +134 -0
  265. package/docs/future-architecture/architecture/07-security.md +194 -0
  266. package/docs/future-architecture/architecture/08-networking.md +149 -0
  267. package/docs/future-architecture/architecture/09-templates.md +122 -0
  268. package/docs/future-architecture/architecture/10-observability.md +121 -0
  269. package/docs/future-architecture/design-notes.md +72 -0
  270. package/docs/future-architecture/gaps.md +281 -0
  271. package/docs/future-architecture/phase-template.md +123 -0
  272. package/docs/future-architecture/references.md +225 -0
  273. package/docs/future-architecture/research/01-kata-containers.md +100 -0
  274. package/docs/future-architecture/research/02-e2b-infra.md +133 -0
  275. package/docs/future-architecture/research/03-coder.md +115 -0
  276. package/docs/future-architecture/research/04-cloud-hypervisor.md +99 -0
  277. package/docs/future-architecture/research/05-firecracker.md +114 -0
  278. package/docs/future-architecture/research/06-agent-sandbox.md +142 -0
  279. package/docs/future-architecture/research/07-chromedp.md +78 -0
  280. package/docs/future-architecture/research/08-microsandbox.md +78 -0
  281. package/docs/future-architecture/research/09-agentbox.md +135 -0
  282. package/docs/future-architecture/research/10-sysbox.md +100 -0
  283. package/docs/future-architecture/research/11-firecracker-containerd.md +93 -0
  284. package/docs/future-architecture/research/12-docker-socket-proxy.md +59 -0
  285. package/docs/future-architecture/research/14-e2b-desktop-and-surf.md +107 -0
  286. package/docs/future-architecture/research/18-open-webui-terminals-observed.md +135 -0
  287. package/docs/future-architecture/research/bank-buyer.md +96 -0
  288. package/docs/future-architecture/research/enthusiast-audience.md +106 -0
  289. package/docs/future-architecture/research/proof-uipath-anthropic-2026-05.md +76 -0
  290. package/docs/future-architecture/research/widemoat-thesis-advisor.md +124 -0
  291. package/docs/future-architecture/roadmap.md +438 -0
  292. package/docs/kata-runtime.md +267 -0
  293. package/docs/kubernetes.md +86 -0
  294. package/docs/logo.png +0 -0
  295. package/docs/multi-cli.md +161 -0
  296. package/docs/openwebui-filter.md +134 -0
  297. package/docs/roadmap/implementation-roadmap.md +104 -0
  298. package/docs/sandbox-contents.svg +229 -0
  299. package/docs/screenshots/01-create-document.png +0 -0
  300. package/docs/screenshots/02-file-preview.png +0 -0
  301. package/docs/screenshots/03-browser-viewer.png +0 -0
  302. package/docs/screenshots/04-sub-agent-terminal.png +0 -0
  303. package/docs/screenshots/05-chat-overview.png +0 -0
  304. package/docs/screenshots/06-sub-agent-dashboard.png +0 -0
  305. package/docs/screenshots/07-frontend-design-skill.png +0 -0
  306. package/docs/screenshots/08-pptx-skill.png +0 -0
  307. package/docs/screenshots/09-skill-creator.png +0 -0
  308. package/docs/screenshots/10-data-chart.png +0 -0
  309. package/docs/shared-browser.svg +102 -0
  310. package/docs/system-prompt.md +113 -0
  311. package/docs/terminal-flow.svg +69 -0
  312. package/examples/helm/README.md +20 -0
  313. package/examples/helm/standalone/values.yaml +49 -0
  314. package/examples/helm/with-open-webui/README.md +99 -0
  315. package/examples/helm/with-open-webui/values-computer-use.yaml +32 -0
  316. package/examples/helm/with-open-webui/values-open-webui.yaml +67 -0
  317. package/fonts/NotoEmoji-Regular.ttf +0 -0
  318. package/helm/computer-use-server/.helmignore +17 -0
  319. package/helm/computer-use-server/Chart.yaml +32 -0
  320. package/helm/computer-use-server/README.md +211 -0
  321. package/helm/computer-use-server/templates/NOTES.txt +66 -0
  322. package/helm/computer-use-server/templates/_helpers.tpl +115 -0
  323. package/helm/computer-use-server/templates/configmap-dind-init.yaml +82 -0
  324. package/helm/computer-use-server/templates/configmap.yaml +18 -0
  325. package/helm/computer-use-server/templates/deployment.yaml +248 -0
  326. package/helm/computer-use-server/templates/ingress.yaml +38 -0
  327. package/helm/computer-use-server/templates/networkpolicy.yaml +50 -0
  328. package/helm/computer-use-server/templates/pdb.yaml +16 -0
  329. package/helm/computer-use-server/templates/pvc-data.yaml +20 -0
  330. package/helm/computer-use-server/templates/pvc-skills-cache.yaml +20 -0
  331. package/helm/computer-use-server/templates/pvc-user-data.yaml +20 -0
  332. package/helm/computer-use-server/templates/pvc-var-lib-docker.yaml +27 -0
  333. package/helm/computer-use-server/templates/secret.yaml +23 -0
  334. package/helm/computer-use-server/templates/service.yaml +22 -0
  335. package/helm/computer-use-server/templates/serviceaccount.yaml +15 -0
  336. package/helm/computer-use-server/templates/tests/test-health.yaml +23 -0
  337. package/helm/computer-use-server/values.schema.json +183 -0
  338. package/helm/computer-use-server/values.yaml +297 -0
  339. package/lychee.toml +36 -0
  340. package/openwebui/Dockerfile +52 -0
  341. package/openwebui/README.md +38 -0
  342. package/openwebui/functions/README.md +48 -0
  343. package/openwebui/functions/computer_link_filter.py +487 -0
  344. package/openwebui/init.sh +305 -0
  345. package/openwebui/patches/README.md +44 -0
  346. package/openwebui/patches/fix_artifacts_auto_show.py +441 -0
  347. package/openwebui/patches/fix_attached_files_position.py +87 -0
  348. package/openwebui/patches/fix_large_tool_args.py +156 -0
  349. package/openwebui/patches/fix_large_tool_results.py +289 -0
  350. package/openwebui/patches/fix_preview_url_detection.py +230 -0
  351. package/openwebui/patches/fix_skip_embedding_chat_files.py +229 -0
  352. package/openwebui/patches/fix_skip_rag_files_native_fc.py +100 -0
  353. package/openwebui/patches/fix_tool_loop_errors.py +510 -0
  354. package/package.json +39 -0
  355. package/requirements.txt +112 -0
  356. package/scripts/check-config.sh +141 -0
  357. package/scripts/docs-lint/ai-slop-detector.sh +202 -0
  358. package/scripts/docs-lint/architecture-tree-whitelist.sh +131 -0
  359. package/scripts/docs-lint/ascii-diagram-detector.sh +58 -0
  360. package/scripts/docs-lint/front-matter-validator.sh +97 -0
  361. package/scripts/docs-lint/gitignored-ref-detector.sh +122 -0
  362. package/scripts/docs-lint/identity-email-detector.sh +48 -0
  363. package/scripts/docs-lint/test-linters.sh +354 -0
  364. package/scripts/docs-lint/wc-budget.sh +61 -0
  365. package/scripts/githooks/pre-push +75 -0
  366. package/server.json +13 -0
  367. package/settings-wrapper/Dockerfile +9 -0
  368. package/settings-wrapper/README.md +119 -0
  369. package/settings-wrapper/app.py +113 -0
  370. package/settings-wrapper/requirements.txt +2 -0
  371. package/settings-wrapper/skills.json +25 -0
  372. package/skills/README.md +46 -0
  373. package/skills/examples/algorithmic-art/SKILL.md +405 -0
  374. package/skills/examples/algorithmic-art/templates/generator_template.js +223 -0
  375. package/skills/examples/algorithmic-art/templates/viewer.html +601 -0
  376. package/skills/examples/artifacts-builder/SKILL.md +74 -0
  377. package/skills/examples/artifacts-builder/scripts/bundle-artifact.sh +54 -0
  378. package/skills/examples/artifacts-builder/scripts/init-artifact.sh +322 -0
  379. package/skills/examples/artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  380. package/skills/examples/canvas-design/LICENSE.txt +202 -0
  381. package/skills/examples/canvas-design/SKILL.md +130 -0
  382. package/skills/examples/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
  383. package/skills/examples/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  384. package/skills/examples/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  385. package/skills/examples/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
  386. package/skills/examples/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  387. package/skills/examples/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
  388. package/skills/examples/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  389. package/skills/examples/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  390. package/skills/examples/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  391. package/skills/examples/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  392. package/skills/examples/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  393. package/skills/examples/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  394. package/skills/examples/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
  395. package/skills/examples/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  396. package/skills/examples/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
  397. package/skills/examples/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  398. package/skills/examples/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
  399. package/skills/examples/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  400. package/skills/examples/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  401. package/skills/examples/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
  402. package/skills/examples/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  403. package/skills/examples/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
  404. package/skills/examples/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  405. package/skills/examples/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  406. package/skills/examples/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
  407. package/skills/examples/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  408. package/skills/examples/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  409. package/skills/examples/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  410. package/skills/examples/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  411. package/skills/examples/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  412. package/skills/examples/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  413. package/skills/examples/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  414. package/skills/examples/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  415. package/skills/examples/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  416. package/skills/examples/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  417. package/skills/examples/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  418. package/skills/examples/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  419. package/skills/examples/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
  420. package/skills/examples/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  421. package/skills/examples/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  422. package/skills/examples/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  423. package/skills/examples/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  424. package/skills/examples/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  425. package/skills/examples/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  426. package/skills/examples/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
  427. package/skills/examples/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
  428. package/skills/examples/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  429. package/skills/examples/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  430. package/skills/examples/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  431. package/skills/examples/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  432. package/skills/examples/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  433. package/skills/examples/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  434. package/skills/examples/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  435. package/skills/examples/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
  436. package/skills/examples/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  437. package/skills/examples/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  438. package/skills/examples/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  439. package/skills/examples/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  440. package/skills/examples/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  441. package/skills/examples/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  442. package/skills/examples/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  443. package/skills/examples/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
  444. package/skills/examples/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
  445. package/skills/examples/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  446. package/skills/examples/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  447. package/skills/examples/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
  448. package/skills/examples/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  449. package/skills/examples/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
  450. package/skills/examples/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  451. package/skills/examples/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  452. package/skills/examples/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
  453. package/skills/examples/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  454. package/skills/examples/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  455. package/skills/examples/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  456. package/skills/examples/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  457. package/skills/examples/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  458. package/skills/examples/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  459. package/skills/examples/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
  460. package/skills/examples/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  461. package/skills/examples/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  462. package/skills/examples/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  463. package/skills/examples/copy-editing/SKILL.md +447 -0
  464. package/skills/examples/copy-editing/evals/evals.json +89 -0
  465. package/skills/examples/copy-editing/references/plain-english-alternatives.md +394 -0
  466. package/skills/examples/internal-comms/LICENSE.txt +202 -0
  467. package/skills/examples/internal-comms/SKILL.md +32 -0
  468. package/skills/examples/internal-comms/examples/3p-updates.md +47 -0
  469. package/skills/examples/internal-comms/examples/company-newsletter.md +65 -0
  470. package/skills/examples/internal-comms/examples/faq-answers.md +30 -0
  471. package/skills/examples/internal-comms/examples/general-comms.md +16 -0
  472. package/skills/examples/mcp-builder/SKILL.md +328 -0
  473. package/skills/examples/mcp-builder/reference/evaluation.md +602 -0
  474. package/skills/examples/mcp-builder/reference/mcp_best_practices.md +915 -0
  475. package/skills/examples/mcp-builder/reference/node_mcp_server.md +916 -0
  476. package/skills/examples/mcp-builder/reference/python_mcp_server.md +752 -0
  477. package/skills/examples/mcp-builder/scripts/connections.py +151 -0
  478. package/skills/examples/mcp-builder/scripts/evaluation.py +373 -0
  479. package/skills/examples/mcp-builder/scripts/example_evaluation.xml +22 -0
  480. package/skills/examples/mcp-builder/scripts/requirements.txt +2 -0
  481. package/skills/examples/product-marketing-context/SKILL.md +241 -0
  482. package/skills/examples/product-marketing-context/evals/evals.json +85 -0
  483. package/skills/examples/single-cell-rna-qc/SKILL.md +175 -0
  484. package/skills/examples/single-cell-rna-qc/references/scverse_qc_guidelines.md +186 -0
  485. package/skills/examples/single-cell-rna-qc/scripts/qc_analysis.py +232 -0
  486. package/skills/examples/single-cell-rna-qc/scripts/qc_core.py +233 -0
  487. package/skills/examples/single-cell-rna-qc/scripts/qc_plotting.py +235 -0
  488. package/skills/examples/skill-creator/SKILL.md +355 -0
  489. package/skills/examples/skill-creator/references/output-patterns.md +82 -0
  490. package/skills/examples/skill-creator/references/workflows.md +28 -0
  491. package/skills/examples/skill-creator/scripts/init_skill.py +303 -0
  492. package/skills/examples/skill-creator/scripts/package_skill.py +110 -0
  493. package/skills/examples/skill-creator/scripts/quick_validate.py +95 -0
  494. package/skills/examples/slack-gif-creator/SKILL.md +254 -0
  495. package/skills/examples/slack-gif-creator/core/easing.py +234 -0
  496. package/skills/examples/slack-gif-creator/core/frame_composer.py +176 -0
  497. package/skills/examples/slack-gif-creator/core/gif_builder.py +269 -0
  498. package/skills/examples/slack-gif-creator/core/validators.py +136 -0
  499. package/skills/examples/slack-gif-creator/requirements.txt +4 -0
  500. package/skills/examples/social-content/SKILL.md +278 -0
  501. package/skills/examples/social-content/evals/evals.json +92 -0
  502. package/skills/examples/social-content/references/platforms.md +170 -0
  503. package/skills/examples/social-content/references/post-templates.md +177 -0
  504. package/skills/examples/social-content/references/reverse-engineering.md +195 -0
  505. package/skills/examples/theme-factory/SKILL.md +59 -0
  506. package/skills/examples/theme-factory/theme-showcase.pdf +0 -0
  507. package/skills/examples/theme-factory/themes/arctic-frost.md +19 -0
  508. package/skills/examples/theme-factory/themes/botanical-garden.md +19 -0
  509. package/skills/examples/theme-factory/themes/desert-rose.md +19 -0
  510. package/skills/examples/theme-factory/themes/forest-canopy.md +19 -0
  511. package/skills/examples/theme-factory/themes/golden-hour.md +19 -0
  512. package/skills/examples/theme-factory/themes/midnight-galaxy.md +19 -0
  513. package/skills/examples/theme-factory/themes/modern-minimalist.md +19 -0
  514. package/skills/examples/theme-factory/themes/ocean-depths.md +19 -0
  515. package/skills/examples/theme-factory/themes/sunset-boulevard.md +19 -0
  516. package/skills/examples/theme-factory/themes/tech-innovation.md +19 -0
  517. package/skills/examples/web-artifacts-builder/LICENSE.txt +202 -0
  518. package/skills/examples/web-artifacts-builder/SKILL.md +74 -0
  519. package/skills/examples/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  520. package/skills/examples/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  521. package/skills/examples/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  522. package/skills/examples/writing-skills/SKILL.md +655 -0
  523. package/skills/examples/writing-skills/anthropic-best-practices.md +1150 -0
  524. package/skills/examples/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  525. package/skills/examples/writing-skills/graphviz-conventions.dot +172 -0
  526. package/skills/examples/writing-skills/persuasion-principles.md +187 -0
  527. package/skills/examples/writing-skills/render-graphs.js +168 -0
  528. package/skills/examples/writing-skills/testing-skills-with-subagents.md +384 -0
  529. package/skills/public/describe-image/SKILL.md +105 -0
  530. package/skills/public/describe-image/scripts/describe.py +389 -0
  531. package/skills/public/doc-coauthoring/SKILL.md +375 -0
  532. package/skills/public/docx/LICENSE.txt +30 -0
  533. package/skills/public/docx/SKILL.md +199 -0
  534. package/skills/public/docx/docx-js.md +350 -0
  535. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  536. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  537. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  538. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  539. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  540. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  541. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  542. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  543. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  544. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  545. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  546. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  547. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  548. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  549. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  550. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  551. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  552. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  553. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  554. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  555. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  556. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  557. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  558. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  559. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  560. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  561. package/skills/public/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  562. package/skills/public/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  563. package/skills/public/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  564. package/skills/public/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  565. package/skills/public/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  566. package/skills/public/docx/ooxml/schemas/mce/mc.xsd +75 -0
  567. package/skills/public/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  568. package/skills/public/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  569. package/skills/public/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  570. package/skills/public/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  571. package/skills/public/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  572. package/skills/public/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  573. package/skills/public/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  574. package/skills/public/docx/ooxml/scripts/pack.py +159 -0
  575. package/skills/public/docx/ooxml/scripts/unpack.py +29 -0
  576. package/skills/public/docx/ooxml/scripts/validate.py +69 -0
  577. package/skills/public/docx/ooxml/scripts/validation/__init__.py +15 -0
  578. package/skills/public/docx/ooxml/scripts/validation/base.py +951 -0
  579. package/skills/public/docx/ooxml/scripts/validation/docx.py +274 -0
  580. package/skills/public/docx/ooxml/scripts/validation/pptx.py +315 -0
  581. package/skills/public/docx/ooxml/scripts/validation/redlining.py +279 -0
  582. package/skills/public/docx/ooxml.md +632 -0
  583. package/skills/public/docx/scripts/__init__.py +1 -0
  584. package/skills/public/docx/scripts/document.py +1292 -0
  585. package/skills/public/docx/scripts/templates/comments.xml +3 -0
  586. package/skills/public/docx/scripts/templates/commentsExtended.xml +3 -0
  587. package/skills/public/docx/scripts/templates/commentsExtensible.xml +3 -0
  588. package/skills/public/docx/scripts/templates/commentsIds.xml +3 -0
  589. package/skills/public/docx/scripts/templates/people.xml +3 -0
  590. package/skills/public/docx/scripts/utilities.py +374 -0
  591. package/skills/public/file-reading/LICENSE.txt +30 -0
  592. package/skills/public/file-reading/SKILL.md +350 -0
  593. package/skills/public/frontend-design/LICENSE.txt +177 -0
  594. package/skills/public/frontend-design/SKILL.md +42 -0
  595. package/skills/public/gitlab-explorer/SKILL.md +174 -0
  596. package/skills/public/gitlab-explorer/references/git-commands.md +323 -0
  597. package/skills/public/gitlab-explorer/references/glab-commands.md +282 -0
  598. package/skills/public/gitlab-explorer/scripts/check_gitlab_auth.sh +109 -0
  599. package/skills/public/pdf/FORMS.md +205 -0
  600. package/skills/public/pdf/REFERENCE.md +612 -0
  601. package/skills/public/pdf/SKILL.md +364 -0
  602. package/skills/public/pdf/scripts/check_bounding_boxes.py +70 -0
  603. package/skills/public/pdf/scripts/check_bounding_boxes_test.py +226 -0
  604. package/skills/public/pdf/scripts/check_fillable_fields.py +12 -0
  605. package/skills/public/pdf/scripts/convert_pdf_to_images.py +35 -0
  606. package/skills/public/pdf/scripts/create_validation_image.py +41 -0
  607. package/skills/public/pdf/scripts/extract_form_field_info.py +152 -0
  608. package/skills/public/pdf/scripts/fill_fillable_fields.py +114 -0
  609. package/skills/public/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  610. package/skills/public/pdf-reading/LICENSE.txt +30 -0
  611. package/skills/public/pdf-reading/REFERENCE.md +196 -0
  612. package/skills/public/pdf-reading/SKILL.md +305 -0
  613. package/skills/public/playwright-cli/SKILL.md +278 -0
  614. package/skills/public/playwright-cli/references/request-mocking.md +87 -0
  615. package/skills/public/playwright-cli/references/running-code.md +232 -0
  616. package/skills/public/playwright-cli/references/session-management.md +169 -0
  617. package/skills/public/playwright-cli/references/storage-state.md +275 -0
  618. package/skills/public/playwright-cli/references/test-generation.md +88 -0
  619. package/skills/public/playwright-cli/references/tracing.md +139 -0
  620. package/skills/public/playwright-cli/references/video-recording.md +43 -0
  621. package/skills/public/pptx/LICENSE.txt +30 -0
  622. package/skills/public/pptx/SKILL.md +484 -0
  623. package/skills/public/pptx/css.md +335 -0
  624. package/skills/public/pptx/html2pptx.md +893 -0
  625. package/skills/public/pptx/html2pptx.tgz +0 -0
  626. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  627. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  628. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  629. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  630. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  631. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  632. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  633. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  634. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  635. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  636. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  637. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  638. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  639. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  640. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  641. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  642. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  643. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  644. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  645. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  646. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  647. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  648. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  649. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  650. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  651. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  652. package/skills/public/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  653. package/skills/public/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  654. package/skills/public/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  655. package/skills/public/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  656. package/skills/public/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  657. package/skills/public/pptx/ooxml/schemas/mce/mc.xsd +75 -0
  658. package/skills/public/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  659. package/skills/public/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  660. package/skills/public/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  661. package/skills/public/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  662. package/skills/public/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  663. package/skills/public/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  664. package/skills/public/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  665. package/skills/public/pptx/ooxml/scripts/pack.py +159 -0
  666. package/skills/public/pptx/ooxml/scripts/unpack.py +29 -0
  667. package/skills/public/pptx/ooxml/scripts/validate.py +69 -0
  668. package/skills/public/pptx/ooxml/scripts/validation/__init__.py +15 -0
  669. package/skills/public/pptx/ooxml/scripts/validation/base.py +951 -0
  670. package/skills/public/pptx/ooxml/scripts/validation/docx.py +274 -0
  671. package/skills/public/pptx/ooxml/scripts/validation/pptx.py +315 -0
  672. package/skills/public/pptx/ooxml/scripts/validation/redlining.py +279 -0
  673. package/skills/public/pptx/ooxml.md +427 -0
  674. package/skills/public/pptx/scripts/inventory.py +1020 -0
  675. package/skills/public/pptx/scripts/rearrange.py +231 -0
  676. package/skills/public/pptx/scripts/replace.py +385 -0
  677. package/skills/public/pptx/scripts/thumbnail.py +450 -0
  678. package/skills/public/skill-creator/SKILL.md +356 -0
  679. package/skills/public/skill-creator/references/output-patterns.md +82 -0
  680. package/skills/public/skill-creator/references/workflows.md +28 -0
  681. package/skills/public/skill-creator/scripts/init_skill.py +303 -0
  682. package/skills/public/skill-creator/scripts/package_skill.py +110 -0
  683. package/skills/public/skill-creator/scripts/quick_validate.py +95 -0
  684. package/skills/public/sub-agent/SKILL.md +186 -0
  685. package/skills/public/sub-agent/references/security-review.md +153 -0
  686. package/skills/public/sub-agent/references/usage.md +207 -0
  687. package/skills/public/sub-agent/scripts/list_subagent_models.sh +22 -0
  688. package/skills/public/test-driven-development/SKILL.md +371 -0
  689. package/skills/public/test-driven-development/testing-anti-patterns.md +299 -0
  690. package/skills/public/webapp-testing/LICENSE.txt +202 -0
  691. package/skills/public/webapp-testing/SKILL.md +96 -0
  692. package/skills/public/webapp-testing/examples/console_logging.py +35 -0
  693. package/skills/public/webapp-testing/examples/element_discovery.py +40 -0
  694. package/skills/public/webapp-testing/examples/static_html_automation.py +33 -0
  695. package/skills/public/webapp-testing/scripts/with_server.py +106 -0
  696. package/skills/public/xlsx/LICENSE.txt +30 -0
  697. package/skills/public/xlsx/SKILL.md +316 -0
  698. package/skills/public/xlsx/preview_data.py +93 -0
  699. package/skills/public/xlsx/recalc.py +178 -0
  700. package/tests/README.md +42 -0
  701. package/tests/fixtures/cli/claude_v0.9.2.0_argv.json +46 -0
  702. package/tests/fixtures/cli/claude_v0.9.2.0_stdout.json +32 -0
  703. package/tests/fixtures/cli/codex_run.jsonl +4 -0
  704. package/tests/fixtures/cli/opencode_run.jsonl +6 -0
  705. package/tests/integration/README.md +56 -0
  706. package/tests/integration/conftest.py +280 -0
  707. package/tests/integration/pytest.ini +13 -0
  708. package/tests/integration/test_mcp_auth.py +85 -0
  709. package/tests/integration/test_mcp_tools.py +101 -0
  710. package/tests/integration/test_workspace_lifecycle.py +125 -0
  711. package/tests/orchestrator/mock_llm_server.py +343 -0
  712. package/tests/orchestrator/test_cli_adapters.py +566 -0
  713. package/tests/orchestrator/test_cli_adapters_live.py +527 -0
  714. package/tests/orchestrator/test_cli_runtime.py +451 -0
  715. package/tests/orchestrator/test_docker_manager.py +302 -0
  716. package/tests/orchestrator/test_dynamic_instructions.py +69 -0
  717. package/tests/orchestrator/test_mcp_resources.py +140 -0
  718. package/tests/orchestrator/test_mcp_tools.py +224 -0
  719. package/tests/orchestrator/test_passthrough_isolation.py +201 -0
  720. package/tests/orchestrator/test_readme_in_container.py +76 -0
  721. package/tests/orchestrator/test_render_cache.py +84 -0
  722. package/tests/orchestrator/test_runtime_cli_endpoint.py +108 -0
  723. package/tests/orchestrator/test_single_user_mode.py +212 -0
  724. package/tests/orchestrator/test_startup_warnings.py +123 -0
  725. package/tests/orchestrator/test_sub_agent_dispatch.py +327 -0
  726. package/tests/orchestrator/test_subagent_claude_compat.py +367 -0
  727. package/tests/orchestrator/test_system_prompt_endpoint.py +191 -0
  728. package/tests/orchestrator/test_tool_descriptions.py +52 -0
  729. package/tests/orchestrator/test_view_image.py +201 -0
  730. package/tests/patches/conftest.py +30 -0
  731. package/tests/patches/fixtures/__init__.py +10 -0
  732. package/tests/patches/fixtures/middleware_v0.9.1.py +5057 -0
  733. package/tests/patches/fixtures/middleware_v0.9.2.py +5120 -0
  734. package/tests/patches/fixtures/retrieval_v0.9.1.py +2684 -0
  735. package/tests/patches/fixtures/retrieval_v0.9.2.py +2700 -0
  736. package/tests/patches/test_fix_attached_files_position.py +118 -0
  737. package/tests/patches/test_fix_large_tool_args.py +130 -0
  738. package/tests/patches/test_fix_large_tool_results.py +531 -0
  739. package/tests/patches/test_fix_skip_embedding_chat_files.py +160 -0
  740. package/tests/patches/test_fix_skip_rag_files_native_fc.py +120 -0
  741. package/tests/patches/test_fix_tool_loop_errors.py +128 -0
  742. package/tests/security/test_path_traversal_app.py +132 -0
  743. package/tests/security/test_path_traversal_docker.py +36 -0
  744. package/tests/security/test_path_traversal_settings.py +87 -0
  745. package/tests/security/test_safe_path_util.py +166 -0
  746. package/tests/security/test_xss_preview.py +46 -0
  747. package/tests/test-default-model-resolution.py +136 -0
  748. package/tests/test-docker-image.sh +358 -0
  749. package/tests/test-list-subagent-models.sh +421 -0
  750. package/tests/test-mcp-endpoint-live.sh +92 -0
  751. package/tests/test-mcp-native-surface.sh +213 -0
  752. package/tests/test-no-cyrillic.sh +135 -0
  753. package/tests/test-opencode-error-mapping.py +130 -0
  754. package/tests/test-pr88-skills.sh +305 -0
  755. package/tests/test-project-structure.sh +202 -0
  756. package/tests/test-single-user-mode.sh +269 -0
  757. package/tests/test-skill-no-hardcoded-models.sh +65 -0
  758. package/tests/test-subagent-cli-surface.py +137 -0
  759. package/tests/test-subagent-runtime.sh +109 -0
  760. package/tests/test_codex_toml_converter.py +204 -0
  761. package/tests/test_default_resolver_no_legacy_global.py +159 -0
  762. package/tests/test_filter.py +648 -0
  763. package/tests/test_init_sh_unchanged.sh +49 -0
  764. package/tests/test_opencode_alias_map_drop.py +144 -0
  765. package/tests/test_requirements.py +91 -0
  766. package/tests/test_subagent_docstring.py +193 -0
  767. package/tests/test_tools.py +34 -0
  768. package/vendor/extract-text/README.md +46 -0
  769. package/vendor/extract-text/extract-text +0 -0
@@ -0,0 +1,648 @@
1
+ # SPDX-License-Identifier: FSL-1.1-Apache-2.0
2
+ # Copyright (c) 2025 Open Computer Use Contributors
3
+ """Tests for computer_link_filter (Open WebUI Function).
4
+
5
+ Run: python -m pytest tests/test_filter.py -v
6
+ """
7
+
8
+ import sys
9
+ import time
10
+ import unittest
11
+ import urllib.error
12
+ from pathlib import Path
13
+ from unittest.mock import MagicMock, patch
14
+
15
+ ROOT = Path(__file__).resolve().parent.parent
16
+ sys.path.insert(0, str(ROOT / "openwebui" / "functions"))
17
+
18
+ import computer_link_filter # noqa: E402
19
+
20
+
21
+ def _urlopen_mock(
22
+ text: str = "PROMPT",
23
+ public_base_url: str = "http://localhost:8081",
24
+ ) -> MagicMock:
25
+ """Create a mock satisfying: with urlopen(req, timeout=N) as resp: resp.read() + resp.headers.
26
+
27
+ `public_base_url` is the value returned by the server in the X-Public-Base-URL
28
+ header — outlet() builds browser-facing links from it.
29
+ """
30
+ cm = MagicMock()
31
+ cm.__enter__ = MagicMock(return_value=cm)
32
+ cm.__exit__ = MagicMock(return_value=False)
33
+ cm.read.return_value = text.encode("utf-8")
34
+ cm.headers = {"X-Public-Base-URL": public_base_url}
35
+ return cm
36
+
37
+
38
+ def _make_filter(
39
+ orchestrator_url: str = "http://localhost:8081",
40
+ ) -> "computer_link_filter.Filter":
41
+ f = computer_link_filter.Filter()
42
+ f.valves.ORCHESTRATOR_URL = orchestrator_url
43
+ return f
44
+
45
+
46
+ def _prime_cache(
47
+ f: "computer_link_filter.Filter",
48
+ chat_id: str,
49
+ user_email: str = "",
50
+ public_url: str = "http://localhost:8081",
51
+ prompt: str = "PROMPT",
52
+ ) -> None:
53
+ """Seed the filter's prompt cache so outlet() has a public_url to decorate with.
54
+
55
+ outlet() never invents a public URL — it pulls from cache populated by inlet().
56
+ Tests that exercise outlet() in isolation must prime the cache first.
57
+ """
58
+ f._prompt_cache[(chat_id, user_email)] = (time.time(), (public_url, prompt))
59
+
60
+
61
+ def _active_body() -> dict:
62
+ return {
63
+ "tool_ids": ["ai_computer_use"],
64
+ "messages": [{"role": "user", "content": "hi"}],
65
+ }
66
+
67
+
68
+ def _system_content(body: dict) -> str:
69
+ for m in body["messages"]:
70
+ if m.get("role") == "system":
71
+ return m.get("content", "")
72
+ return ""
73
+
74
+
75
+ def _assistant_body_with_file(chat_id: str = "abc") -> dict:
76
+ link = f"http://localhost:8081/files/{chat_id}/report.pdf"
77
+ return {"messages": [{"role": "assistant", "content": f"see {link}"}]}
78
+
79
+
80
+ class TrailingSlashNormalisation(unittest.TestCase):
81
+ """Any URL Valve may arrive with a trailing slash; URLs must never end up with `//files/`."""
82
+
83
+ def setUp(self):
84
+ # inlet() fetches the prompt over HTTP — mock the response so it contains
85
+ # the expected URL verbatim + the X-Public-Base-URL header outlet() reads.
86
+ self._urlopen_patcher = patch(
87
+ "urllib.request.urlopen",
88
+ return_value=_urlopen_mock(
89
+ "System prompt with http://localhost:8081/files/abc baked",
90
+ public_base_url="http://localhost:8081",
91
+ ),
92
+ )
93
+ self._urlopen_patcher.start()
94
+ self.addCleanup(self._urlopen_patcher.stop)
95
+
96
+ def test_inlet_does_not_emit_double_slash(self):
97
+ f = _make_filter("http://localhost:8081/")
98
+ body = f.inlet(_active_body(), __metadata__={"chat_id": "abc"})
99
+ self.assertNotIn("//files/", _system_content(body))
100
+
101
+ def test_outlet_archive_button_has_no_double_slash(self):
102
+ f = _make_filter("http://localhost:8081/")
103
+ # Simulate server that returned public URL with trailing slash.
104
+ _prime_cache(f, "abc", public_url="http://localhost:8081/")
105
+ link = "http://localhost:8081/files/abc/report.pdf"
106
+ body = {"messages": [{"role": "assistant", "content": f"see {link}"}]}
107
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
108
+ content = out["messages"][0]["content"]
109
+ self.assertNotIn("//files/", content)
110
+ self.assertIn("http://localhost:8081/files/abc/archive", content)
111
+
112
+
113
+ class EmptyChatIdHandling(unittest.TestCase):
114
+ """When chat_id is missing, the injected prompt must not reference broken /files/ URLs."""
115
+
116
+ def test_inlet_skips_injection_when_chat_id_is_none(self):
117
+ f = _make_filter()
118
+ body = f.inlet(_active_body(), __metadata__={})
119
+ self.assertEqual("", _system_content(body),
120
+ "System prompt should not be injected when chat_id is missing")
121
+
122
+ def test_inlet_skips_injection_when_metadata_missing(self):
123
+ f = _make_filter()
124
+ body = f.inlet(_active_body(), __metadata__=None)
125
+ self.assertEqual("", _system_content(body))
126
+
127
+
128
+ class BaselineBehaviour(unittest.TestCase):
129
+ """Regression guards: normal happy path must keep working."""
130
+
131
+ def setUp(self):
132
+ self._urlopen_patcher = patch(
133
+ "urllib.request.urlopen",
134
+ return_value=_urlopen_mock("System prompt with http://localhost:8081/files/abc baked"),
135
+ )
136
+ self._urlopen_patcher.start()
137
+ self.addCleanup(self._urlopen_patcher.stop)
138
+
139
+ def test_inlet_injects_when_tool_active_and_chat_id_present(self):
140
+ f = _make_filter()
141
+ body = f.inlet(_active_body(), __metadata__={"chat_id": "abc"})
142
+ self.assertIn("http://localhost:8081/files/abc", _system_content(body))
143
+
144
+ def test_inlet_no_injection_when_tool_inactive(self):
145
+ f = _make_filter()
146
+ body = f.inlet(
147
+ {"tool_ids": [], "messages": [{"role": "user", "content": "hi"}]},
148
+ __metadata__={"chat_id": "abc"},
149
+ )
150
+ self.assertEqual("", _system_content(body))
151
+
152
+ def test_inlet_handles_non_string_system_content(self):
153
+ """Open WebUI multimodal flows can deliver system `content` as a list of
154
+ parts instead of a string. inlet() must not crash on re.search and must
155
+ still inject the Computer Use prompt. Regression for CodeRabbit finding
156
+ on 2026-04-12 (filter.py:220)."""
157
+ f = _make_filter()
158
+ structured_content = [{"type": "text", "text": "hello"}]
159
+ body = {
160
+ "tool_ids": ["ai_computer_use"],
161
+ "messages": [
162
+ {"role": "system", "content": structured_content},
163
+ {"role": "user", "content": "hi"},
164
+ ],
165
+ }
166
+ result = f.inlet(body, __metadata__={"chat_id": "abc"})
167
+ # Did not raise; injection still happened somewhere in the system slot
168
+ system_content = result["messages"][0]["content"]
169
+ self.assertIsInstance(system_content, str)
170
+ self.assertIn("http://localhost:8081/files/abc", system_content)
171
+
172
+ def test_outlet_appends_archive_button_once(self):
173
+ f = _make_filter()
174
+ _prime_cache(f, "abc")
175
+ link = "http://localhost:8081/files/abc/report.pdf"
176
+ body = {"messages": [{"role": "assistant", "content": f"see {link}"}]}
177
+ out1 = f.outlet(body, __metadata__={"chat_id": "abc"})
178
+ out2 = f.outlet(out1, __metadata__={"chat_id": "abc"})
179
+ self.assertEqual(
180
+ out1["messages"][0]["content"],
181
+ out2["messages"][0]["content"],
182
+ "Archive button must be idempotent (not duplicated on repeat outlet calls)",
183
+ )
184
+
185
+ def test_outlet_does_not_modify_non_assistant_messages(self):
186
+ """User/system/tool messages must be left untouched even if they contain a file URL."""
187
+ f = _make_filter()
188
+ _prime_cache(f, "abc")
189
+ link = "http://localhost:8081/files/abc/report.pdf"
190
+ original_user = f"check {link}"
191
+ original_system = f"context with {link}"
192
+ body = {
193
+ "messages": [
194
+ {"role": "user", "content": original_user},
195
+ {"role": "system", "content": original_system},
196
+ {"role": "tool", "content": f"tool output {link}"},
197
+ ]
198
+ }
199
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
200
+ self.assertEqual(out["messages"][0]["content"], original_user)
201
+ self.assertEqual(out["messages"][1]["content"], original_system)
202
+ self.assertNotIn("archive", out["messages"][2]["content"].lower())
203
+
204
+ def test_outlet_ignores_file_urls_for_other_chat_ids(self):
205
+ """Archive button must NOT be appended when the only file URL belongs to a
206
+ different chat_id (e.g. a multi-user workspace or a quoted prior transcript).
207
+ Regression guard for W-01: outlet previously matched any chat_id via `[^/]+`.
208
+ """
209
+ f = _make_filter()
210
+ _prime_cache(f, "abc")
211
+ other_link = "http://localhost:8081/files/other-chat/report.pdf"
212
+ original_content = f"see artefact from the other chat: {other_link}"
213
+ body = {"messages": [{"role": "assistant", "content": original_content}]}
214
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
215
+ self.assertEqual(
216
+ out["messages"][0]["content"],
217
+ original_content,
218
+ "Message referencing a file URL for a different chat_id must be left untouched",
219
+ )
220
+ self.assertNotIn("archive", out["messages"][0]["content"].lower())
221
+
222
+
223
+ class SystemPromptFetchCache(unittest.TestCase):
224
+ """HTTP-fetch + LRU cache + stale-cache fallback.
225
+
226
+ v4.0.0: _fetch_system_prompt() returns (public_url, prompt) tuple instead of
227
+ just the prompt — public_url comes from the X-Public-Base-URL response header
228
+ so outlet() doesn't need its own URL Valve.
229
+ """
230
+
231
+ def test_fresh_fetch_populates_cache(self):
232
+ f = _make_filter()
233
+ with patch(
234
+ "urllib.request.urlopen",
235
+ return_value=_urlopen_mock("PROMPT_V1", public_base_url="http://pub:8081"),
236
+ ):
237
+ result = f._fetch_system_prompt("chat-a", "")
238
+ self.assertEqual(result, ("http://pub:8081", "PROMPT_V1"))
239
+ self.assertIn(("chat-a", ""), f._prompt_cache)
240
+ self.assertEqual(f._prompt_cache[("chat-a", "")][1], ("http://pub:8081", "PROMPT_V1"))
241
+
242
+ def test_fetch_falls_back_to_orchestrator_url_when_header_missing(self):
243
+ """Older servers may not send X-Public-Base-URL. Filter falls back to
244
+ reusing ORCHESTRATOR_URL as public_url (only correct for bare-metal)."""
245
+ f = _make_filter("http://int:8081")
246
+ cm = _urlopen_mock("PROMPT")
247
+ cm.headers = {} # server did not set the header
248
+ with patch("urllib.request.urlopen", return_value=cm):
249
+ result = f._fetch_system_prompt("chat-a", "")
250
+ self.assertEqual(result, ("http://int:8081", "PROMPT"))
251
+
252
+ def test_cache_hit_within_ttl_skips_http(self):
253
+ f = _make_filter()
254
+ f._prompt_cache[("chat-a", "")] = (time.time(), ("http://pub:8081", "CACHED"))
255
+ with patch("urllib.request.urlopen", side_effect=AssertionError("urlopen must not be called")) as m:
256
+ result = f._fetch_system_prompt("chat-a", "")
257
+ self.assertEqual(result, ("http://pub:8081", "CACHED"))
258
+ m.assert_not_called()
259
+
260
+ def test_ttl_expiry_triggers_refetch(self):
261
+ f = _make_filter()
262
+ f._prompt_cache[("chat-a", "")] = (time.time() - 301, ("http://pub:8081", "OLD"))
263
+ with patch("urllib.request.urlopen", return_value=_urlopen_mock("FRESH")):
264
+ result = f._fetch_system_prompt("chat-a", "")
265
+ self.assertEqual(result[1], "FRESH")
266
+ self.assertGreater(f._prompt_cache[("chat-a", "")][0], time.time() - 5)
267
+
268
+ def test_lru_eviction_at_max_size(self):
269
+ f = _make_filter()
270
+ with patch("urllib.request.urlopen", side_effect=lambda *a, **kw: _urlopen_mock("P")):
271
+ for i in range(1, 102): # 101 distinct chat ids
272
+ f._fetch_system_prompt(f"chat-{i}", "")
273
+ self.assertEqual(len(f._prompt_cache), 100)
274
+ self.assertNotIn(("chat-1", ""), f._prompt_cache)
275
+ self.assertIn(("chat-101", ""), f._prompt_cache)
276
+
277
+ def test_stale_cache_fallback_on_server_down(self):
278
+ f = _make_filter()
279
+ f._prompt_cache[("chat-a", "")] = (
280
+ time.time() - 9999,
281
+ ("http://pub:8081", "STALE"),
282
+ )
283
+ with patch("urllib.request.urlopen", side_effect=urllib.error.URLError("conn refused")):
284
+ result = f._fetch_system_prompt("chat-a", "")
285
+ self.assertEqual(result, ("http://pub:8081", "STALE"))
286
+
287
+ def test_cold_cache_returns_none_when_server_down(self):
288
+ f = _make_filter()
289
+ with patch("urllib.request.urlopen", side_effect=urllib.error.URLError("conn refused")):
290
+ result = f._fetch_system_prompt("chat-x", "")
291
+ self.assertIsNone(result, f"Expected None on cold-cache failure, got {result!r}")
292
+
293
+ def test_user_email_propagated_to_query_string(self):
294
+ f = _make_filter()
295
+ captured = {}
296
+
297
+ def _capture(req, timeout=0):
298
+ captured["url"] = req.full_url
299
+ return _urlopen_mock("P")
300
+
301
+ with patch("urllib.request.urlopen", side_effect=_capture):
302
+ f._fetch_system_prompt("chat-a", "user@example.com")
303
+ self.assertIn("chat_id=chat-a", captured["url"])
304
+ self.assertIn("user_email=user%40example.com", captured["url"])
305
+
306
+ def test_rejects_non_http_scheme_without_urlopen(self):
307
+ """Valves misconfiguration (file://, ftp://, etc.) must not reach urlopen.
308
+ Regression guard for ruff S310: ORCHESTRATOR_URL=file:///etc/passwd would
309
+ otherwise read the file as the injected system prompt.
310
+ """
311
+ f = _make_filter("file:///etc/passwd")
312
+ with patch("urllib.request.urlopen", side_effect=AssertionError("must not be called")) as m:
313
+ result = f._fetch_system_prompt("chat-a", "")
314
+ self.assertIsNone(result)
315
+ m.assert_not_called()
316
+
317
+ def test_rejects_non_http_scheme_serves_stale_cache_when_available(self):
318
+ """If the scheme is invalid but a cached value exists, serve it (same
319
+ policy as transport failure)."""
320
+ f = _make_filter("ftp://example.com")
321
+ f._prompt_cache[("chat-a", "")] = (
322
+ time.time() - 9999,
323
+ ("http://pub:8081", "STALE"),
324
+ )
325
+ with patch("urllib.request.urlopen", side_effect=AssertionError("must not be called")):
326
+ result = f._fetch_system_prompt("chat-a", "")
327
+ self.assertEqual(result, ("http://pub:8081", "STALE"))
328
+
329
+ def test_narrow_exception_propagates_programming_errors(self):
330
+ """A broad `except Exception` used to swallow programming bugs (e.g.
331
+ AttributeError from internal misuse) as silent stale-cache fallbacks.
332
+ The narrowed handler must re-raise non-transport failures."""
333
+ f = _make_filter()
334
+ with patch("urllib.request.urlopen", side_effect=AttributeError("boom")):
335
+ with self.assertRaises(AttributeError):
336
+ f._fetch_system_prompt("chat-a", "")
337
+
338
+ def test_cache_isolates_different_users_on_same_chat(self):
339
+ """Two users sharing a chat_id must NOT see each other's baked <available_skills>."""
340
+ f = _make_filter()
341
+ prompts = iter(["PROMPT_FOR_ALICE", "PROMPT_FOR_BOB"])
342
+
343
+ def _serve_next(req, timeout=0):
344
+ return _urlopen_mock(next(prompts))
345
+
346
+ with patch("urllib.request.urlopen", side_effect=_serve_next):
347
+ a = f._fetch_system_prompt("chat-shared", "alice@example.com")
348
+ b = f._fetch_system_prompt("chat-shared", "bob@example.com")
349
+ self.assertEqual(a[1], "PROMPT_FOR_ALICE")
350
+ self.assertEqual(b[1], "PROMPT_FOR_BOB")
351
+ self.assertIn(("chat-shared", "alice@example.com"), f._prompt_cache)
352
+ self.assertIn(("chat-shared", "bob@example.com"), f._prompt_cache)
353
+ # No cross-contamination: Alice's cached entry still holds Alice's prompt
354
+ self.assertEqual(
355
+ f._prompt_cache[("chat-shared", "alice@example.com")][1][1],
356
+ "PROMPT_FOR_ALICE",
357
+ )
358
+
359
+
360
+ class PreviewButton(unittest.TestCase):
361
+ """Covers PREVIEW-02 (markdown button is the only preview decoration),
362
+ PREVIEW-04 (button idempotency), and the v4.1.0 invariant that outlet()
363
+ never emits a fenced ```html block or <iframe> — that was removed because
364
+ it suppressed the fix_preview_url_detection frontend patch.
365
+ """
366
+
367
+ def _filter(self, public_url: str = "http://localhost:8081") -> "computer_link_filter.Filter":
368
+ f = _make_filter()
369
+ _prime_cache(f, "abc", public_url=public_url)
370
+ return f
371
+
372
+ def test_outlet_appends_preview_button_by_default(self):
373
+ f = self._filter()
374
+ body = f.outlet(_assistant_body_with_file(), __metadata__={"chat_id": "abc"})
375
+ content = body["messages"][0]["content"]
376
+ self.assertIn(
377
+ "[🖥️ Open preview](http://localhost:8081/preview/abc)", content
378
+ )
379
+
380
+ def test_outlet_never_emits_fenced_html_or_iframe(self):
381
+ """v4.1.0 invariant: outlet() must not add a fenced html block or raw
382
+ iframe. The frontend patch turns the /preview/ URL into an inline
383
+ artifact on its own; emitting a block here would (a) render as a code
384
+ fence in chat on stock Open WebUI and (b) suppress the patch on
385
+ patched builds (its guard `!htmlGroups.some(o=>o.html)` fails)."""
386
+ f = self._filter()
387
+ body = f.outlet(_assistant_body_with_file(), __metadata__={"chat_id": "abc"})
388
+ content = body["messages"][0]["content"]
389
+ self.assertNotIn("<iframe", content)
390
+ self.assertNotIn("```html", content)
391
+
392
+ def test_outlet_preview_button_is_idempotent(self):
393
+ f = self._filter()
394
+ body = _assistant_body_with_file()
395
+ out1 = f.outlet(body, __metadata__={"chat_id": "abc"})
396
+ out2 = f.outlet(out1, __metadata__={"chat_id": "abc"})
397
+ self.assertEqual(out1["messages"][0]["content"], out2["messages"][0]["content"])
398
+ self.assertEqual(
399
+ out2["messages"][0]["content"].count("[🖥️ Open preview]"),
400
+ 1,
401
+ )
402
+
403
+ def test_outlet_preview_mode_off_skips_button(self):
404
+ f = self._filter()
405
+ f.valves.PREVIEW_MODE = "off"
406
+ body = f.outlet(_assistant_body_with_file(), __metadata__={"chat_id": "abc"})
407
+ self.assertNotIn("[🖥️ Open preview]", body["messages"][0]["content"])
408
+
409
+ def test_outlet_preview_button_respects_other_chat_ids(self):
410
+ f = self._filter()
411
+ other_link = "http://localhost:8081/files/other-chat/report.pdf"
412
+ original = f"see artefact from another chat: {other_link}"
413
+ body = {"messages": [{"role": "assistant", "content": original}]}
414
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
415
+ self.assertEqual(out["messages"][0]["content"], original)
416
+
417
+ def test_outlet_not_added_to_non_assistant_roles(self):
418
+ f = self._filter()
419
+ link = "http://localhost:8081/files/abc/report.pdf"
420
+ body = {
421
+ "messages": [
422
+ {"role": "user", "content": f"u {link}"},
423
+ {"role": "system", "content": f"s {link}"},
424
+ {"role": "tool", "content": f"t {link}"},
425
+ ]
426
+ }
427
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
428
+ for msg in out["messages"]:
429
+ self.assertNotIn("[🖥️ Open preview]", msg["content"])
430
+ self.assertNotIn("archive", msg["content"].lower())
431
+
432
+ def test_outlet_preview_url_has_no_double_slash_when_trailing_slash(self):
433
+ f = self._filter(public_url="http://localhost:8081/")
434
+ body = f.outlet(_assistant_body_with_file(), __metadata__={"chat_id": "abc"})
435
+ content = body["messages"][0]["content"]
436
+ self.assertNotIn("//preview/", content)
437
+ self.assertIn(
438
+ "[🖥️ Open preview](http://localhost:8081/preview/abc)", content
439
+ )
440
+
441
+ def test_legacy_preview_mode_values_rejected_on_construction(self):
442
+ """v3.x / v4.0.0 values ("artifact", "both") must be rejected when Open WebUI
443
+ instantiates Valves from a saved-DB blob — the Literal type narrowing catches
444
+ operators who haven't re-seeded Valves after the upgrade. Loud error > silent
445
+ no-op.
446
+
447
+ Note: Pydantic validates on construction by default, not on attribute
448
+ assignment, so we test the construction path (which is how Open WebUI
449
+ reconstitutes Valves from its stored JSON).
450
+ """
451
+ from pydantic import ValidationError
452
+ ValvesModel = computer_link_filter.Filter.Valves
453
+ for legacy in ("artifact", "both"):
454
+ with self.assertRaises(ValidationError, msg=f"{legacy!r} must be rejected"):
455
+ ValvesModel(PREVIEW_MODE=legacy) # type: ignore[arg-type]
456
+
457
+
458
+ class BrowserToolTrigger(unittest.TestCase):
459
+ """Covers the second outlet() trigger: a `<details type="tool_calls">` block
460
+ referencing a browser tool (playwright / chromium / screenshot / start-browser).
461
+
462
+ Added to exercise the 2026-04-18 fix — previously untested. Sessions that
463
+ drive a browser without producing a downloadable file must still get a
464
+ preview iframe; archive button must stay gated on file URLs only.
465
+ """
466
+
467
+ def _tool_call_details(self, name: str = "playwright", arguments: str = "{}") -> str:
468
+ # Attribute values come html-escaped in production but substring
469
+ # keyword matching is robust to that, per _content_has_browser_tool's
470
+ # docstring. Use raw values here for readability.
471
+ return f'<details type="tool_calls" name="{name}" arguments="{arguments}" result=""></details>'
472
+
473
+ def test_helper_detects_each_browser_keyword(self):
474
+ """_content_has_browser_tool() must match every keyword in the allow-list."""
475
+ for kw in computer_link_filter._BROWSER_TOOL_KEYWORDS:
476
+ content = self._tool_call_details(name=kw)
477
+ self.assertTrue(
478
+ computer_link_filter._content_has_browser_tool(content),
479
+ f"Keyword {kw!r} inside <details type=\"tool_calls\"> should trigger detection",
480
+ )
481
+
482
+ def test_helper_matches_keyword_in_arguments(self):
483
+ """Keyword may live in the html-escaped `arguments="..."` attribute rather
484
+ than the tool name. Open WebUI escapes quotes inside JSON args as `&#34;`,
485
+ so plaintext keywords remain a valid substring of the escaped blob."""
486
+ escaped_args = "{&#34;action&#34;:&#34;chromium-launch&#34;}"
487
+ content = self._tool_call_details(name="mcp", arguments=escaped_args)
488
+ self.assertTrue(computer_link_filter._content_has_browser_tool(content))
489
+
490
+ def test_helper_ignores_freetext_keyword_mentions(self):
491
+ """Keyword in plain assistant text (outside a tool_calls details block) must NOT trigger."""
492
+ content = "How does playwright handle screenshot capture in chromium?"
493
+ self.assertFalse(computer_link_filter._content_has_browser_tool(content))
494
+
495
+ def test_helper_ignores_non_toolcall_details_blocks(self):
496
+ """`<details type="reasoning">playwright</details>` must NOT trigger — detection is
497
+ scoped to type=\"tool_calls\" only."""
498
+ content = '<details type="reasoning" name="playwright">thinking...</details>'
499
+ self.assertFalse(computer_link_filter._content_has_browser_tool(content))
500
+
501
+ def test_helper_returns_false_on_empty_content(self):
502
+ self.assertFalse(computer_link_filter._content_has_browser_tool(""))
503
+ self.assertFalse(computer_link_filter._content_has_browser_tool(None)) # type: ignore[arg-type]
504
+
505
+ def _primed_filter(self) -> "computer_link_filter.Filter":
506
+ f = _make_filter()
507
+ _prime_cache(f, "abc")
508
+ return f
509
+
510
+ def test_outlet_appends_preview_button_on_browser_tool_without_file_url(self):
511
+ """outlet() must inject preview button when a browser tool ran but produced no file."""
512
+ f = self._primed_filter()
513
+ content = "I navigated to the page. " + self._tool_call_details(name="playwright")
514
+ body = {"messages": [{"role": "assistant", "content": content}]}
515
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
516
+ self.assertIn(
517
+ "[🖥️ Open preview](http://localhost:8081/preview/abc)",
518
+ out["messages"][0]["content"],
519
+ )
520
+
521
+ def test_outlet_browser_tool_trigger_emits_no_fenced_html_or_iframe(self):
522
+ """v4.1.0 invariant on the browser-tool path: no fenced html / iframe is ever
523
+ emitted, only the markdown button. The frontend patch turns the URL into an
524
+ artifact."""
525
+ f = self._primed_filter()
526
+ content = self._tool_call_details(name="chromium")
527
+ body = {"messages": [{"role": "assistant", "content": content}]}
528
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
529
+ decorated = out["messages"][0]["content"]
530
+ self.assertNotIn("<iframe", decorated)
531
+ self.assertNotIn("```html", decorated)
532
+
533
+ def test_outlet_archive_button_NOT_triggered_by_browser_tool_alone(self):
534
+ """Archive button is meaningless without files — must stay gated on file URLs even
535
+ when a browser tool ran."""
536
+ f = self._primed_filter()
537
+ content = self._tool_call_details(name="screenshot")
538
+ body = {"messages": [{"role": "assistant", "content": content}]}
539
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
540
+ self.assertNotIn("archive", out["messages"][0]["content"].lower())
541
+
542
+ def test_outlet_does_not_trigger_on_freetext_keyword(self):
543
+ """Regression guard for false-positive scoping: assistant free text mentioning
544
+ a browser tool must NOT receive preview decoration."""
545
+ f = self._primed_filter()
546
+ body = {"messages": [{"role": "assistant", "content": "Use playwright to click the button."}]}
547
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
548
+ self.assertNotIn("<iframe", out["messages"][0]["content"])
549
+ self.assertNotIn("[🖥️ Open preview]", out["messages"][0]["content"])
550
+
551
+ def test_outlet_browser_tool_trigger_is_idempotent(self):
552
+ """Repeated outlet() calls on browser-tool-triggered content must not duplicate the button."""
553
+ f = self._primed_filter()
554
+ content = self._tool_call_details(name="playwright")
555
+ body = {"messages": [{"role": "assistant", "content": content}]}
556
+ out1 = f.outlet(body, __metadata__={"chat_id": "abc"})
557
+ out2 = f.outlet(out1, __metadata__={"chat_id": "abc"})
558
+ self.assertEqual(out1["messages"][0]["content"], out2["messages"][0]["content"])
559
+ self.assertEqual(
560
+ out2["messages"][0]["content"].count("[🖥️ Open preview]"), 1
561
+ )
562
+
563
+
564
+ class OutletWithoutCache(unittest.TestCase):
565
+ """outlet() must not invent a public URL. When the cache is empty (outlet
566
+ invoked on a re-rendered old message after a server restart, or before
567
+ inlet() ran), decoration is skipped — broken links are worse than no links.
568
+ """
569
+
570
+ def test_outlet_skips_all_decoration_when_cache_empty(self):
571
+ f = _make_filter()
572
+ link = "http://localhost:8081/files/abc/report.pdf"
573
+ original = f"assistant said: {link}"
574
+ body = {"messages": [{"role": "assistant", "content": original}]}
575
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
576
+ self.assertEqual(
577
+ out["messages"][0]["content"],
578
+ original,
579
+ "Empty cache must leave the message untouched — no iframe, button, or archive",
580
+ )
581
+
582
+ def test_outlet_falls_back_to_email_keyed_cache_when_user_missing(self):
583
+ """When inlet() cached under (chat_id, "alice@x") and outlet() is later
584
+ invoked without __user__ (e.g. on a re-render path), the filter must
585
+ still find the cached public_url by scanning same-chat entries. Previously
586
+ outlet() only probed (chat_id, user_email) and (chat_id, ""), missing
587
+ the email-keyed entry and skipping all decoration."""
588
+ f = _make_filter()
589
+ _prime_cache(f, "abc", user_email="alice@example.com")
590
+ # Assistant already contains a file link and a browser-tool details block
591
+ # — either trigger must fire once outlet() finds the cached URL.
592
+ link = "http://localhost:8081/files/abc/report.pdf"
593
+ body = {"messages": [{"role": "assistant", "content": f"see {link}"}]}
594
+ # Note: no __user__ passed
595
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
596
+ decorated = out["messages"][0]["content"]
597
+ self.assertIn(
598
+ "[🖥️ Open preview](http://localhost:8081/preview/abc)", decorated
599
+ )
600
+ self.assertIn(
601
+ "[📦 Download all files as archive](http://localhost:8081/files/abc/archive)",
602
+ decorated,
603
+ )
604
+
605
+ def test_outlet_skips_when_cache_has_different_chat_id(self):
606
+ """Cache for chat-X must not decorate a message sent in chat-Y."""
607
+ f = _make_filter()
608
+ _prime_cache(f, "other-chat")
609
+ link = "http://localhost:8081/files/abc/report.pdf"
610
+ original = f"assistant said: {link}"
611
+ body = {"messages": [{"role": "assistant", "content": original}]}
612
+ out = f.outlet(body, __metadata__={"chat_id": "abc"})
613
+ self.assertEqual(out["messages"][0]["content"], original)
614
+
615
+
616
+ class ValveSchema(unittest.TestCase):
617
+ """Filter owns exactly one URL Valve (ORCHESTRATOR_URL). FILE_SERVER_URL and
618
+ SYSTEM_PROMPT_URL are gone — the public URL is owned by the server and returned
619
+ via the X-Public-Base-URL response header on /system-prompt."""
620
+
621
+ def test_only_orchestrator_url_valve_exists(self):
622
+ valve_fields = set(computer_link_filter.Filter.Valves.model_fields.keys())
623
+ self.assertIn("ORCHESTRATOR_URL", valve_fields)
624
+ self.assertNotIn("FILE_SERVER_URL", valve_fields)
625
+ self.assertNotIn("SYSTEM_PROMPT_URL", valve_fields)
626
+
627
+
628
+ class DocstringDriftGuard(unittest.TestCase):
629
+ """Covers DOCS-03 — every Field on Filter.Valves is listed in the module VALVES: docstring."""
630
+
631
+ def test_every_valve_is_documented_in_docstring(self):
632
+ doc = computer_link_filter.__doc__ or ""
633
+ self.assertIn("VALVES:", doc, "Module docstring must contain a VALVES: block")
634
+ # Extract everything under VALVES: to the next blank-line-separated section
635
+ # (or end of docstring). Simple split is sufficient — drift guard only.
636
+ after_marker = doc.split("VALVES:", 1)[1]
637
+ for field_name in computer_link_filter.Filter.Valves.model_fields:
638
+ self.assertIn(
639
+ field_name,
640
+ after_marker,
641
+ f"Valve {field_name!r} is defined on Filter.Valves but missing "
642
+ f"from the VALVES: docstring block — update the docstring "
643
+ f"in computer_link_filter.py to list it.",
644
+ )
645
+
646
+
647
+ if __name__ == "__main__":
648
+ unittest.main()