@tianshu-ai/tianshu 0.3.12 → 0.3.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tianshu-ai/tianshu",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
4
4
  "description": "An open AI agent platform with a sidecar browser. Built in public.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/tianshu-ai/tianshu",
@@ -31,6 +31,7 @@
31
31
  "plugins/**/manifest.json",
32
32
  "plugins/**/skills/**",
33
33
  "plugins/**/agent-seeds/**",
34
+ "plugins/**/templates/**",
34
35
  "README.md",
35
36
  "LICENSE"
36
37
  ],
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "./manifest.json": "./manifest.json"
19
19
  },
20
- "files": ["dist", "src", "skills", "manifest.json"],
20
+ "files": ["dist", "src", "skills", "templates", "manifest.json"],
21
21
  "scripts": {
22
22
  "build": "tsc",
23
23
  "dev": "tsc --watch --preserveWatchOutput",
@@ -0,0 +1,105 @@
1
+ # Browser-enabled Sandboxfile (CloakBrowser + Playwright MCP + noVNC).
2
+ #
3
+ # Verified end-to-end on Apple Silicon arm64: build → publish →
4
+ # reset → all 6 supervisord processes RUNNING → CDP / MCP / noVNC
5
+ # all reachable. Build cost: ~3-5 min on cache hit, ~10 min cold
6
+ # (apt + Node + LibreOffice + 200 MB CloakBrowser tarball).
7
+ #
8
+ # After build & use, supervisord must be started inside the live
9
+ # VM. N+5.3 will do that automatically; today the user runs:
10
+ # supervisord -c /etc/supervisor/conf.d/browser.conf
11
+ #
12
+ # Notes on the painful bits this template encodes:
13
+ # - dbus is intentionally NOT a supervisor program: dbus-x11 in
14
+ # debian:slim is missing /usr/share/dbus-1/system.conf and
15
+ # chromium headless doesn't need a system bus anyway.
16
+ # - chromium pinned to /tmp/chrome-profile so a stale profile
17
+ # lock from the build VM can't block startup. Snapshots will
18
+ # not preserve /tmp.
19
+ # - chromium binds CDP to 0.0.0.0 so port-forward picks it up.
20
+ # - Playwright MCP listens on 0.0.0.0; default ::1 was IPv6-only
21
+ # and unreachable from the IPv4-only host port-forward.
22
+ # - All multi-line config files (supervisord conf) go through
23
+ # base64 because the v0 Sandboxfile parser is single-line only
24
+ # and bash single-quoted heredocs don't carry \n correctly.
25
+
26
+ image: python:3.12-slim
27
+ cpus: 2
28
+ memory_mib: 8192
29
+
30
+ exec:
31
+ # APT 国内源(在其他地区可改成上游 deb.debian.org)
32
+ - sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources
33
+ - apt-get update -qq
34
+
35
+ # 一行装完(无 dbus)
36
+ # x11-xserver-utils → xrandr (resize Xvfb framebuffer at runtime)
37
+ # wmctrl → fit chromium window to the new framebuffer
38
+ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl ca-certificates xz-utils sudo fontconfig fonts-noto-cjk fonts-noto-color-emoji libreoffice-writer libreoffice-calc libreoffice-impress xvfb x11vnc fluxbox supervisor x11-xserver-utils wmctrl libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libgbm1 libxss1 libasound2 libxshmfence1 libxkbcommon0 libpango-1.0-0 libxcomposite1 libxdamage1 libxrandr2
39
+
40
+ # Node 22 LTS — 多源 fallback。nodejs.org 主源,失败后依次走
41
+ # npmmirror 镜像、cnpmjs 镜像。每个源走到身为止 (--retry 3),
42
+ # 并且 --connect-timeout 30 避免 TCP 建连后被 RST 时挂死。
43
+ - |
44
+ bash -c '
45
+ set -eu
46
+ VER=v22.20.0
47
+ F=node-${VER}-linux-arm64.tar.xz
48
+ for u in \
49
+ https://nodejs.org/dist/${VER}/${F} \
50
+ https://npmmirror.com/mirrors/node/${VER}/${F} \
51
+ https://registry.npmmirror.com/-/binary/node/${VER}/${F}; do
52
+ echo "[node] trying $u"
53
+ if curl -fsSL --retry 3 --retry-delay 5 --retry-all-errors \
54
+ --connect-timeout 30 --max-time 600 "$u" \
55
+ | tar -xJ -C /usr/local --strip-components=1; then
56
+ echo "[node] installed from $u"
57
+ exit 0
58
+ fi
59
+ echo "[node] failed: $u"
60
+ done
61
+ echo "[node] all sources exhausted" >&2
62
+ exit 1
63
+ '
64
+ - npm config set registry https://registry.npmmirror.com
65
+
66
+ # CloakBrowser 200MB - ghproxy + retry + 落盘解压
67
+ # Tarball is FLAT (no top-level dir); entry binary is `chrome`.
68
+ - bash -c "mkdir -p /opt/cloakbrowser && curl -fsSL --retry 8 --retry-delay 8 --retry-all-errors --connect-timeout 30 -o /tmp/cb.tar.gz https://gh-proxy.com/https://github.com/CloakHQ/CloakBrowser/releases/download/chromium-v145.0.7632.159.7/cloakbrowser-linux-arm64.tar.gz"
69
+ - bash -c "test $(stat -c %s /tmp/cb.tar.gz) -gt 100000000"
70
+ - tar -xzf /tmp/cb.tar.gz -C /opt/cloakbrowser
71
+ - rm -f /tmp/cb.tar.gz
72
+
73
+ # noVNC + websockify
74
+ - bash -c "mkdir -p /opt/novnc && curl -fsSL --retry 5 --retry-delay 3 --retry-all-errors https://gh-proxy.com/https://github.com/novnc/noVNC/archive/refs/tags/v1.5.0.tar.gz | tar -xz -C /opt/novnc --strip-components=1"
75
+ - bash -c "mkdir -p /opt/websockify && curl -fsSL --retry 5 --retry-delay 3 --retry-all-errors https://gh-proxy.com/https://github.com/novnc/websockify/archive/refs/tags/v0.13.0.tar.gz | tar -xz -C /opt/websockify --strip-components=1"
76
+ - ln -sf /opt/novnc/vnc_lite.html /opt/novnc/index.html
77
+
78
+ # Playwright MCP - 提前把 npm 包拉到 npx 缓存,runtime 启动不再下载
79
+ - npx -y @playwright/mcp@latest --help > /dev/null 2>&1
80
+
81
+ # 字体缓存(首次 LibreOffice / chromium CJK 渲染)
82
+ - fc-cache -fv
83
+
84
+ # supervisord 配置(base64 - 多行 conf 通过 v0 sandboxfile parser 的唯一办法)
85
+ - mkdir -p /etc/supervisor/conf.d
86
+ - bash -c "echo W3VuaXhfaHR0cF9zZXJ2ZXJdCmZpbGU9L3Zhci9ydW4vc3VwZXJ2aXNvci5zb2NrCmNobW9kPTA3MDAKCltzdXBlcnZpc29yZF0KbG9nZmlsZT0vdmFyL2xvZy9zdXBlcnZpc29yZC5sb2cKcGlkZmlsZT0vdmFyL3J1bi9zdXBlcnZpc29yZC5waWQKCltycGNpbnRlcmZhY2U6c3VwZXJ2aXNvcl0Kc3VwZXJ2aXNvci5ycGNpbnRlcmZhY2VfZmFjdG9yeT1zdXBlcnZpc29yLnJwY2ludGVyZmFjZTptYWtlX21haW5fcnBjaW50ZXJmYWNlCgpbc3VwZXJ2aXNvcmN0bF0Kc2VydmVydXJsPXVuaXg6Ly8vdmFyL3J1bi9zdXBlcnZpc29yLnNvY2sKCltwcm9ncmFtOnh2ZmJdCmNvbW1hbmQ9L3Vzci9iaW4vWHZmYiA6OTkgLXNjcmVlbiAwIDI0MDB4MTgwMHgyNCAtYWMgLW5vbGlzdGVuIHRjcAphdXRvcmVzdGFydD10cnVlCnByaW9yaXR5PTEwCgpbcHJvZ3JhbTpmbHV4Ym94XQpjb21tYW5kPS91c3IvYmluL2ZsdXhib3gKZW52aXJvbm1lbnQ9RElTUExBWT0iOjk5IgphdXRvcmVzdGFydD10cnVlCnByaW9yaXR5PTIwCgpbcHJvZ3JhbTpjaHJvbWl1bV0KY29tbWFuZD0vb3B0L2Nsb2FrYnJvd3Nlci9jaHJvbWUgLS1yZW1vdGUtZGVidWdnaW5nLXBvcnQ9OTIyMiAtLXJlbW90ZS1kZWJ1Z2dpbmctYWRkcmVzcz0wLjAuMC4wIC0tbm8tc2FuZGJveCAtLWRpc2FibGUtZGV2LXNobS11c2FnZSAtLXVzZXItZGF0YS1kaXI9L3RtcC9jaHJvbWUtcHJvZmlsZSAtLXdpbmRvdy1zaXplPTEyODAsODAwCmVudmlyb25tZW50PURJU1BMQVk9Ijo5OSIKYXV0b3Jlc3RhcnQ9dHJ1ZQpwcmlvcml0eT0zMApzdGFydHNlY3M9OAoKW3Byb2dyYW06cGxheXdyaWdodC1tY3BdCmNvbW1hbmQ9L3Vzci9sb2NhbC9iaW4vbnB4IEBwbGF5d3JpZ2h0L21jcEBsYXRlc3QgLS1ob3N0IDAuMC4wLjAgLS1wb3J0IDMyMDAgLS1hbGxvd2VkLWhvc3RzPSogLS1hbGxvdy11bnJlc3RyaWN0ZWQtZmlsZS1hY2Nlc3MgLS1jYXBzPXBkZix2aXNpb24gLS1icm93c2VyIGNocm9taXVtIC0tY2RwLWVuZHBvaW50IGh0dHA6Ly8xMjcuMC4wLjE6OTIyMgphdXRvcmVzdGFydD10cnVlCnByaW9yaXR5PTQwCnN0YXJ0c2Vjcz04CgpbcHJvZ3JhbTp4MTF2bmNdCmNvbW1hbmQ9L3Vzci9iaW4veDExdm5jIC1kaXNwbGF5IDo5OSAtZm9yZXZlciAtc2hhcmVkIC1ub3B3IC1yZmJwb3J0IDU5MDAKYXV0b3Jlc3RhcnQ9dHJ1ZQpwcmlvcml0eT01MAoKW3Byb2dyYW06bm92bmNdCmNvbW1hbmQ9L29wdC93ZWJzb2NraWZ5L3J1biAtLXdlYj0vb3B0L25vdm5jIDYwODAgbG9jYWxob3N0OjU5MDAKYXV0b3Jlc3RhcnQ9dHJ1ZQpwcmlvcml0eT02MAo= | base64 -d > /etc/supervisor/conf.d/browser.conf"
87
+
88
+ # Build 阶段启动 supervisord 一次,让 verify 步骤能 curl 到端口
89
+ - supervisord -c /etc/supervisor/conf.d/browser.conf
90
+ - sleep 18
91
+
92
+ # === Verify ============================================================
93
+ # 任一 fail 整个 build fail,避免 "build 成功但实际跑不起来"
94
+ - bash -c "node --version && npm --version && /opt/cloakbrowser/chrome --version | head -1"
95
+ # supervisor 没有 FATAL/EXITED/STOPPED
96
+ - bash -c "supervisorctl -c /etc/supervisor/conf.d/browser.conf status; supervisorctl -c /etc/supervisor/conf.d/browser.conf status | grep -E 'FATAL|EXITED|STOPPED' && exit 1 || true"
97
+ # 6 个 program 都 RUNNING
98
+ - bash -c "test $(supervisorctl -c /etc/supervisor/conf.d/browser.conf status | grep -c RUNNING) -eq 6"
99
+ # CDP 9222
100
+ - bash -c "curl -sS --max-time 8 http://127.0.0.1:9222/json/version | grep -q Browser"
101
+ # noVNC 6080
102
+ - bash -c "curl -sS --max-time 8 http://127.0.0.1:6080/vnc_lite.html | grep -qi DOCTYPE"
103
+ # Playwright MCP 3200(HTTP 403/404/200 都说明服务在 listen)
104
+ - bash -c "code=$(curl -sS --max-time 8 -o /dev/null -w '%{http_code}' http://127.0.0.1:3200/); echo MCP HTTP code=$code; test \"$code\" != \"000\""
105
+ - echo OK
@@ -0,0 +1,80 @@
1
+ # Task runner + Browser (incremental layer).
2
+ #
3
+ # Designed to be built ON TOP OF the `task-runner` snapshot via the
4
+ # admin UI's "based on" dropdown:
5
+ #
6
+ # 1. Build the task-runner template first → snapshot
7
+ # `tianshu-<tenant>-build-<id>`
8
+ # 2. Switch the editor to this template
9
+ # 3. Click Build, pick "based on: task-runner build-<id>" in the
10
+ # dropdown
11
+ # 4. The Sandboxfile's `image:` line below is IGNORED in that
12
+ # mode — the snapshot already pins it. Only the apt / curl /
13
+ # supervisord steps below run, layering chromium + Playwright
14
+ # MCP + noVNC on top of the existing task-runner image.
15
+ #
16
+ # Result: a snapshot that has everything task-runner ships
17
+ # (Node + Python + office libs + libreoffice + CN mirrors)
18
+ # AND the browser stack from `browser.yaml`. Bigger than
19
+ # task-runner (~2.5 GB extra for the chromium/CloakBrowser
20
+ # binaries) but you skip re-running the slow apt + pip + libreoffice
21
+ # layers from scratch.
22
+ #
23
+ # If you boot this WITHOUT picking a "based on" snapshot the build
24
+ # will fail at the very first `apt install` step (the base image
25
+ # below is bare python:3.12-slim — it doesn't have any of
26
+ # task-runner's tools yet). That failure is the intended signal
27
+ # that you skipped step 3.
28
+
29
+ # Image is a deliberate placeholder. It only matters when this
30
+ # template is built without a "based on" snapshot, and that's
31
+ # explicitly not how this template is meant to be used.
32
+ image: python:3.12-slim
33
+ cpus: 2
34
+ memory_mib: 8192
35
+
36
+ exec:
37
+ # APT mirrors (no-op outside CN; harmless inside).
38
+ - sed -i 's|deb.debian.org|mirrors.aliyun.com|g; s|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources
39
+ - apt-get update -qq
40
+
41
+ # The browser stack on top of task-runner. We DON'T re-install
42
+ # python3 / sudo / git / fonts / libreoffice — those came from
43
+ # the task-runner snapshot we layered on. xz-utils gets pulled
44
+ # in for the CloakBrowser tarball; everything else is new.
45
+ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xz-utils xvfb x11vnc fluxbox supervisor x11-xserver-utils wmctrl libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libgbm1 libxss1 libasound2 libxshmfence1 libxkbcommon0 libpango-1.0-0 libxcomposite1 libxdamage1 libxrandr2
46
+ - rm -rf /var/lib/apt/lists/*
47
+
48
+ # CloakBrowser binary + noVNC + websockify.
49
+ - bash -c "mkdir -p /opt/cloakbrowser && curl -fsSL --retry 8 --retry-delay 8 --retry-all-errors --connect-timeout 30 -o /tmp/cb.tar.gz https://gh-proxy.com/https://github.com/CloakHQ/CloakBrowser/releases/download/chromium-v145.0.7632.159.7/cloakbrowser-linux-arm64.tar.gz"
50
+ - bash -c "test $(stat -c %s /tmp/cb.tar.gz) -gt 100000000"
51
+ - tar -xzf /tmp/cb.tar.gz -C /opt/cloakbrowser
52
+ - rm -f /tmp/cb.tar.gz
53
+ - bash -c "mkdir -p /opt/novnc && curl -fsSL --retry 5 --retry-delay 3 --retry-all-errors https://gh-proxy.com/https://github.com/novnc/noVNC/archive/refs/tags/v1.5.0.tar.gz | tar -xz -C /opt/novnc --strip-components=1"
54
+ - bash -c "mkdir -p /opt/websockify && curl -fsSL --retry 5 --retry-delay 3 --retry-all-errors https://gh-proxy.com/https://github.com/novnc/websockify/archive/refs/tags/v0.13.0.tar.gz | tar -xz -C /opt/websockify --strip-components=1"
55
+ - ln -sf /opt/novnc/vnc_lite.html /opt/novnc/index.html
56
+
57
+ # Pre-warm the Playwright MCP npx cache.
58
+ - npx -y @playwright/mcp@latest --help > /dev/null 2>&1
59
+
60
+ # supervisord conf (base64'd same as browser.yaml; the parser is
61
+ # single-line only, so multi-line conf round-trips through
62
+ # base64).
63
+ - mkdir -p /etc/supervisor/conf.d
64
+ - bash -c "echo W3VuaXhfaHR0cF9zZXJ2ZXJdCmZpbGU9L3Zhci9ydW4vc3VwZXJ2aXNvci5zb2NrCmNobW9kPTA3MDAKCltzdXBlcnZpc29yZF0KbG9nZmlsZT0vdmFyL2xvZy9zdXBlcnZpc29yZC5sb2cKcGlkZmlsZT0vdmFyL3J1bi9zdXBlcnZpc29yZC5waWQKCltycGNpbnRlcmZhY2U6c3VwZXJ2aXNvcl0Kc3VwZXJ2aXNvci5ycGNpbnRlcmZhY2VfZmFjdG9yeT1zdXBlcnZpc29yLnJwY2ludGVyZmFjZTptYWtlX21haW5fcnBjaW50ZXJmYWNlCgpbc3VwZXJ2aXNvcmN0bF0Kc2VydmVydXJsPXVuaXg6Ly8vdmFyL3J1bi9zdXBlcnZpc29yLnNvY2sKCltwcm9ncmFtOnh2ZmJdCmNvbW1hbmQ9L3Vzci9iaW4vWHZmYiA6OTkgLXNjcmVlbiAwIDI0MDB4MTgwMHgyNCAtYWMgLW5vbGlzdGVuIHRjcAphdXRvcmVzdGFydD10cnVlCnByaW9yaXR5PTEwCgpbcHJvZ3JhbTpmbHV4Ym94XQpjb21tYW5kPS91c3IvYmluL2ZsdXhib3gKZW52aXJvbm1lbnQ9RElTUExBWT0iOjk5IgphdXRvcmVzdGFydD10cnVlCnByaW9yaXR5PTIwCgpbcHJvZ3JhbTpjaHJvbWl1bV0KY29tbWFuZD0vb3B0L2Nsb2FrYnJvd3Nlci9jaHJvbWUgLS1yZW1vdGUtZGVidWdnaW5nLXBvcnQ9OTIyMiAtLXJlbW90ZS1kZWJ1Z2dpbmctYWRkcmVzcz0wLjAuMC4wIC0tbm8tc2FuZGJveCAtLWRpc2FibGUtZGV2LXNobS11c2FnZSAtLXVzZXItZGF0YS1kaXI9L3RtcC9jaHJvbWUtcHJvZmlsZSAtLXdpbmRvdy1zaXplPTEyODAsODAwCmVudmlyb25tZW50PURJU1BMQVk9Ijo5OSIKYXV0b3Jlc3RhcnQ9dHJ1ZQpwcmlvcml0eT0zMApzdGFydHNlY3M9OAoKW3Byb2dyYW06cGxheXdyaWdodC1tY3BdCmNvbW1hbmQ9L3Vzci9sb2NhbC9iaW4vbnB4IEBwbGF5d3JpZ2h0L21jcEBsYXRlc3QgLS1ob3N0IDAuMC4wLjAgLS1wb3J0IDMyMDAgLS1hbGxvd2VkLWhvc3RzPSogLS1hbGxvdy11bnJlc3RyaWN0ZWQtZmlsZS1hY2Nlc3MgLS1jYXBzPXBkZix2aXNpb24gLS1icm93c2VyIGNocm9taXVtIC0tY2RwLWVuZHBvaW50IGh0dHA6Ly8xMjcuMC4wLjE6OTIyMgphdXRvcmVzdGFydD10cnVlCnByaW9yaXR5PTQwCnN0YXJ0c2Vjcz04CgpbcHJvZ3JhbTp4MTF2bmNdCmNvbW1hbmQ9L3Vzci9iaW4veDExdm5jIC1kaXNwbGF5IDo5OSAtZm9yZXZlciAtc2hhcmVkIC1ub3B3IC1yZmJwb3J0IDU5MDAKYXV0b3Jlc3RhcnQ9dHJ1ZQpwcmlvcml0eT01MAoKW3Byb2dyYW06bm92bmNdCmNvbW1hbmQ9L29wdC93ZWJzb2NraWZ5L3J1biAtLXdlYj0vb3B0L25vdm5jIDYwODAgbG9jYWxob3N0OjU5MDAKYXV0b3Jlc3RhcnQ9dHJ1ZQpwcmlvcml0eT02MAo= | base64 -d > /etc/supervisor/conf.d/browser.conf"
65
+
66
+ # Boot supervisord once during build so verify can hit the ports.
67
+ - supervisord -c /etc/supervisor/conf.d/browser.conf
68
+ - sleep 18
69
+
70
+ # === Verify ============================================================
71
+ # Carry-over from task-runner: those tools must still work.
72
+ - bash -c "node --version && python3 --version && pip3 --version && python3 -c 'import pandas, docx, pptx; print(\"py-libs ok\")'"
73
+ # New layer: browser stack alive.
74
+ - bash -c "/opt/cloakbrowser/chrome --version | head -1"
75
+ - bash -c "supervisorctl -c /etc/supervisor/conf.d/browser.conf status; supervisorctl -c /etc/supervisor/conf.d/browser.conf status | grep -E 'FATAL|EXITED|STOPPED' && exit 1 || true"
76
+ - bash -c "test $(supervisorctl -c /etc/supervisor/conf.d/browser.conf status | grep -c RUNNING) -eq 6"
77
+ - bash -c "curl -sS --max-time 8 http://127.0.0.1:9222/json/version | grep -q Browser"
78
+ - bash -c "curl -sS --max-time 8 http://127.0.0.1:6080/vnc_lite.html | grep -qi DOCTYPE"
79
+ - bash -c "code=$(curl -sS --max-time 8 -o /dev/null -w '%{http_code}' http://127.0.0.1:3200/); echo MCP HTTP code=$code; test \"$code\" != \"000\""
80
+ - echo OK
@@ -0,0 +1,80 @@
1
+ # Task runner Sandboxfile.
2
+ #
3
+ # Recommended snapshot for the per-task sandbox pool (PR #141): a
4
+ # Node 22 + Python 3.12 environment with the tools agent tasks
5
+ # typically reach for, but WITHOUT the GUI / browser stack from
6
+ # `browser.yaml`. Faster to boot, smaller RAM footprint, plenty
7
+ # fast for code generation / data analysis / shell scripting
8
+ # tasks that don't need a chromium of their own.
9
+ #
10
+ # Use this as the default Task snapshot in the admin UI; pick
11
+ # `browser.yaml` instead when the workload needs to pilot a
12
+ # chromium (visual scraping, JS-heavy SPAs, screenshots, etc).
13
+ # Anything beyond those two cases — a custom base image, an
14
+ # air-gapped install, etc — means writing your own Sandboxfile
15
+ # and uploading it through the admin UI.
16
+ #
17
+ # Includes vs `browser.yaml`:
18
+ # shared: sudo, curl, ca-certs, fontconfig + CJK + emoji fonts,
19
+ # libreoffice (so docx/xlsx/pptx round-trips work),
20
+ # Node 22 (base image), Python 3.12 + pip + venv
21
+ # added : git, jq, build-essential, less, vim-tiny, zip/unzip
22
+ # added : pip pre-installs so common one-shot tasks don't pay
23
+ # the install latency on every cold start:
24
+ # - data: pandas, numpy, matplotlib, openpyxl
25
+ # - web: requests, beautifulsoup4, lxml, pyyaml
26
+ # - office: python-docx, python-pptx, pypdf, reportlab,
27
+ # markitdown (universal-converter)
28
+ # omitted: chromium, xvfb, x11vnc, fluxbox, supervisor, noVNC,
29
+ # wmctrl, x11-xserver-utils, all the X11/GUI client libs
30
+ # (libnss3, libatk*, libcups2, libgbm1, libxss1,
31
+ # libasound2, libxshmfence1, libxkbcommon0, libpango,
32
+ # libxcomposite1, libxdamage1, libxrandr2)
33
+ #
34
+ # Total image is ~600 MB compressed vs `browser.yaml`'s ~3.2 GB.
35
+ # Use `browser.yaml` instead when the workload genuinely needs to
36
+ # pilot a browser (visual scraping, JS-heavy SPA rendering, etc).
37
+
38
+ image: node:22-slim
39
+ cpus: 4
40
+ memory_mib: 4096
41
+
42
+ exec:
43
+ # APT mirror (China-mainland speed-up; harmless elsewhere — the
44
+ # debian.sources file replacement is a no-op when the original
45
+ # entries are already mirrored at the alibaba endpoint).
46
+ - sed -i 's|deb.debian.org|mirrors.aliyun.com|g; s|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources
47
+ - apt-get update -qq
48
+
49
+ # One apt install. node:22-slim already ships ca-certificates +
50
+ # xz-utils, so we don't repeat them here. Order roughly: shell
51
+ # essentials, dev tools, fonts, libreoffice, python.
52
+ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl sudo git jq build-essential less vim-tiny zip unzip fontconfig fonts-noto-cjk fonts-noto-color-emoji libreoffice-writer libreoffice-calc libreoffice-impress python3 python3-pip python3-venv
53
+ - rm -rf /var/lib/apt/lists/*
54
+
55
+ # Mirror npm + pip to local CDNs. Same rationale as the apt
56
+ # mirror — no-op outside CN, big speed-up inside.
57
+ - npm config set registry https://registry.npmmirror.com
58
+ - pip3 config --global set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
59
+
60
+ # Pre-install common pip libs so one-shot data tasks don't pay
61
+ # cold-start install latency. Three groups:
62
+ # data: pandas, numpy, matplotlib, openpyxl (xlsx via pandas)
63
+ # web: requests, beautifulsoup4, lxml, pyyaml
64
+ # office: python-docx (docx), python-pptx (pptx), pypdf
65
+ # (PDF read), reportlab (PDF write), markitdown
66
+ # (universal converter — docx/xlsx/pptx/pdf/html/csv
67
+ # → markdown)
68
+ # ~250 MB combined; reasonable for the value vs always installing
69
+ # on demand.
70
+ - pip3 install --no-cache-dir --break-system-packages pandas numpy matplotlib requests pyyaml lxml beautifulsoup4 openpyxl python-docx python-pptx pypdf reportlab markitdown
71
+
72
+ # Refresh font cache so matplotlib/PIL render CJK on first call.
73
+ - fc-cache -fv
74
+
75
+ # === Verify ============================================================
76
+ - bash -c "node --version && npm --version && python3 --version && pip3 --version"
77
+ - bash -c "git --version && jq --version | head -1"
78
+ - bash -c "python3 -c 'import pandas, numpy, matplotlib, requests, openpyxl, docx, pptx, pypdf, reportlab; import markitdown; print(\"py-libs ok\")'"
79
+ - bash -c "soffice --version | head -1"
80
+ - echo OK