@tianshu-ai/tianshu 0.43.2 → 0.43.3

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.43.2",
3
+ "version": "0.43.3",
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",
@@ -32,6 +32,7 @@
32
32
  "plugins/**/skills/**",
33
33
  "plugins/**/agent-seeds/**",
34
34
  "plugins/**/templates/**",
35
+ "plugins/**/sandbox-image/**",
35
36
  "README.md",
36
37
  "LICENSE"
37
38
  ],
@@ -0,0 +1,177 @@
1
+ # syntax=docker/dockerfile:1
2
+ #
3
+ # Tianshu OpenCode sandbox image.
4
+ #
5
+ # Extends the OpenShell community base (Ubuntu + Node 22 + uv/python +
6
+ # claude/gh/codex CLIs + the default sandbox policy) and PREINSTALLS,
7
+ # for the sandbox user, everything an OpenCode worker needs:
8
+ # - opencode-ai pinned to a version with the websearch tool +
9
+ # OPENCODE_ENABLE_PARALLEL flag (the base image ships an older
10
+ # opencode that lacks these), installed to a user-writable prefix
11
+ # so the sandbox user runs it without root/EACCES.
12
+ # - oh-my-openagent (omo) + its (large) dependency tree, warmed into
13
+ # opencode's plugin cache so a task run does NOT download ~500 MB
14
+ # of deps on every start.
15
+ #
16
+ # Why preinstall: the worker used to install these at task time, but
17
+ # omo pulls a huge dep tree over the deny-by-default egress (one host
18
+ # approved at a time), which is slow and repeats per isolated task.
19
+ # Baking it into the image = one build, instant startup, no per-task
20
+ # network install.
21
+ #
22
+ # Build (from this dir):
23
+ # docker build -t tianshu/opencode-sandbox:latest .
24
+ # Then point the openshell plugin at it:
25
+ # plugins.openshell.config.fromImage = "tianshu/opencode-sandbox:latest"
26
+ #
27
+ # The openshell runner passes this to `openshell sandbox create --from`.
28
+
29
+ ARG BASE_IMAGE=ghcr.io/nvidia/openshell-community/sandboxes/base:latest
30
+ FROM ${BASE_IMAGE}
31
+
32
+ # Versions pinned for reproducibility. Keep OPENCODE_VERSION in sync
33
+ # with OPENCODE_VERSION in plugins/workboard/src/worker/opencode-worker.ts.
34
+ ARG OPENCODE_VERSION=1.17.13
35
+ # Pin an EXACT omo version (never @latest). opencode re-resolves an
36
+ # unpinned plugin against npm on every startup, which stalls through
37
+ # the openshell egress proxy and hangs plugin-load; a pinned version
38
+ # warmed here matches what the worker requests (OMO_VERSION in
39
+ # opencode-worker.ts) so opencode uses the cached package and skips
40
+ # the npm resolve. Keep in sync with OMO_VERSION in the worker.
41
+ ARG OMO_PACKAGE=oh-my-openagent@4.15.1
42
+
43
+ USER root
44
+ # --- 0. System packages + dev tools --------------------------------
45
+ # P0: daily dev essentials
46
+ RUN apt-get update -qq && apt-get install -y --no-install-recommends \
47
+ jq tree wget zip unzip ripgrep fd-find fzf bat htop sqlite3 \
48
+ && rm -rf /var/lib/apt/lists/*
49
+
50
+ # P1: audio/video + Remotion rendering
51
+ RUN apt-get update -qq && apt-get install -y --no-install-recommends \
52
+ ffmpeg sox \
53
+ chromium-browser \
54
+ libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
55
+ libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 \
56
+ libgbm1 libasound2t64 libpangocairo-1.0-0 libgtk-3-0 \
57
+ fonts-noto-cjk fonts-noto-color-emoji fonts-liberation \
58
+ && rm -rf /var/lib/apt/lists/*
59
+
60
+ # P1: document processing (PDF / OCR — no LibreOffice for now)
61
+ RUN apt-get update -qq && apt-get install -y --no-install-recommends \
62
+ pandoc wkhtmltopdf ghostscript poppler-utils \
63
+ tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng \
64
+ imagemagick libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev librsvg2-dev \
65
+ default-jre-headless \
66
+ && rm -rf /var/lib/apt/lists/*
67
+
68
+ # Node global tools (P0 + P1)
69
+ RUN npm install -g \
70
+ tsx typescript \
71
+ eslint prettier \
72
+ @remotion/cli sharp puppeteer playwright \
73
+ pdf-lib exceljs docx pdfkit mammoth
74
+
75
+ # Playwright browsers (Chromium only, reuse system deps above)
76
+ RUN npx playwright install --with-deps chromium
77
+
78
+ # Python libraries (P0 + P1 + P2)
79
+ RUN pip install --no-cache-dir \
80
+ pyyaml python-dotenv jinja2 requests aiohttp beautifulsoup4 \
81
+ numpy scipy pandas polars matplotlib seaborn Pillow plotly \
82
+ pydub edge-tts gtts \
83
+ openpyxl xlsxwriter python-docx python-pptx \
84
+ pymupdf pdfplumber PyPDF2 reportlab weasyprint \
85
+ markdown camelot-py[cv] tabula-py \
86
+ httpie playwright
87
+
88
+ # yq (YAML processor) — binary install
89
+ RUN curl -sL "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_$(dpkg --print-architecture)" \
90
+ -o /usr/local/bin/yq && chmod +x /usr/local/bin/yq
91
+
92
+ # --- 1. Upgrade opencode (root global) -----------------------------
93
+ # The base image preinstalls an older opencode at /usr/lib/node_modules.
94
+ # Overwrite it with our pinned version so `opencode` on PATH is the
95
+ # right one for every user (no per-task user-prefix install needed).
96
+ USER root
97
+ # strace: for debugging the opencode/omo plugin-load deadlock in the
98
+ # openshell gRPC-exec tunnel (lets us trace exactly which syscall the
99
+ # hung opencode.exe blocks on). Small, harmless to ship.
100
+ RUN apt-get update -qq && apt-get install -y -qq strace >/dev/null 2>&1 || true
101
+ RUN npm install -g opencode-ai@${OPENCODE_VERSION} \
102
+ && opencode --version
103
+
104
+ # --- 2. Warm the omo plugin cache for the sandbox user -------------
105
+ # opencode auto-installs npm `plugin` entries via Bun into
106
+ # $XDG_CACHE_HOME/opencode/node_modules on first run. We pre-warm that
107
+ # cache as the `sandbox` user (HOME=/sandbox) by launching opencode
108
+ # once with an opencode.json that lists the plugin, so the heavy
109
+ # dependency download happens at build time, not per task.
110
+ #
111
+ # The warm run is offline-friendly: it only needs the npm/GitHub
112
+ # fetches for the plugin install; we give it a throwaway config with
113
+ # no provider so it installs the plugin then exits. A non-zero exit
114
+ # from the (provider-less) run is fine — we only care that the plugin
115
+ # + deps got cached.
116
+ USER sandbox
117
+ WORKDIR /sandbox
118
+ RUN mkdir -p /sandbox/.config/opencode /sandbox/.warmup/.opencode
119
+ # The warmup config lists the plugin AND ships an omo config that
120
+ # looks like a real task's, so the warm run triggers not just the
121
+ # plugin DEP install but also omo's one-time FIRST-INIT PROVISIONING
122
+ # (codegraph binary + ast-grep + ~/.omo state). That provisioning,
123
+ # if left to the first real task, stalls under the openshell gRPC
124
+ # exec tunnel (observed: omo hangs at first init in a fresh sandbox);
125
+ # doing it at build time bakes the provisioned ~/.omo + caches into
126
+ # the image so every runtime start is already "hot" and skips it.
127
+ RUN cat > /sandbox/.warmup/opencode.json <<'JSON'
128
+ {
129
+ "$schema": "https://opencode.ai/config.json",
130
+ "snapshot": false,
131
+ "autoupdate": false,
132
+ "plugin": ["oh-my-openagent@4.15.1"],
133
+ "permission": { "*": "allow" }
134
+ }
135
+ JSON
136
+ # NOTE: mirror the RUNTIME omo config here (esp. disabled_mcps:["lsp"])
137
+ # so the warm run does the SAME first-init as a real task — with the
138
+ # lsp-daemon MCP disabled. Otherwise the warm run leaves lsp-daemon
139
+ # state that differs from runtime and the first real task still pays
140
+ # (and hangs on) the cold first-init. Keep in sync with buildOmoConfig
141
+ # in plugins/workboard/src/worker/opencode-worker.ts.
142
+ RUN cat > /sandbox/.warmup/.opencode/oh-my-opencode.jsonc <<'JSON'
143
+ {
144
+ "agents": { "sisyphus": { "model": "warmup/none" } },
145
+ "model_fallback": false,
146
+ "runtime_fallback": false,
147
+ "telemetry": false,
148
+ "disabled_mcps": ["lsp"]
149
+ }
150
+ JSON
151
+ # Run opencode once with the omo config active. It installs the
152
+ # plugin deps AND kicks omo's first-init provisioning. No model is
153
+ # reachable, so it exits non-zero after provisioning — that's fine,
154
+ # we only want the cached deps + provisioned ~/.omo state.
155
+ RUN cd /sandbox/.warmup \
156
+ && (OPENCODE_DISABLE_LSP_DOWNLOAD=1 OPENCODE_CONFIG=./opencode.json \
157
+ timeout 600 opencode run "noop" --format json < /dev/null \
158
+ > /sandbox/.warmup/warm.log 2>&1 || true) \
159
+ && echo "opencode plugin cache warmed:" \
160
+ && du -sh /sandbox/.cache/opencode 2>/dev/null || true \
161
+ && echo "omo provisioned state:" \
162
+ && du -sh /sandbox/.omo 2>/dev/null || true
163
+
164
+ # Drop the throwaway warmup workdir + config; KEEP the warmed caches
165
+ # (~/.cache/opencode) and omo's provisioned state (~/.omo). BUT also
166
+ # drop the codegraph LOCK dir the warm run left behind
167
+ # (~/.omo/codegraph/locks/codegraph-buildkitsandbox.lock, named after
168
+ # the build container): baking a stale lock into the image makes
169
+ # omo's codegraph init contend/wait on a lock from a process that no
170
+ # longer exists, which intermittently hangs the first task in a fresh
171
+ # sandbox. Keep the provisioned codegraph data, drop only the locks.
172
+ RUN rm -rf /sandbox/.warmup /sandbox/.config/opencode/opencode.json \
173
+ && rm -rf /sandbox/.omo/codegraph/locks /sandbox/.omo/*/locks \
174
+ && find /sandbox/.omo -name '*.lock' -exec rm -rf {} + 2>/dev/null || true
175
+
176
+ USER sandbox
177
+ ENTRYPOINT ["/bin/bash"]