@mcptoolshop/style-dataset-lab 3.4.0 → 3.5.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 (48) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.es.md +111 -59
  3. package/README.fr.md +117 -67
  4. package/README.hi.md +109 -70
  5. package/README.it.md +111 -60
  6. package/README.ja.md +116 -76
  7. package/README.md +58 -2
  8. package/README.pt-BR.md +119 -64
  9. package/README.zh.md +108 -67
  10. package/bin/sdlab.js +50 -1
  11. package/lib/adapters/ai-toolkit.js +187 -30
  12. package/lib/adapters/comfyui-workflows.js +84 -9
  13. package/lib/asset-ingest.js +575 -0
  14. package/lib/asset-masks.js +323 -0
  15. package/lib/asset-source.js +1176 -0
  16. package/lib/canon-author.js +350 -0
  17. package/lib/canon-review-gate.js +205 -0
  18. package/lib/colab-launcher-guards.js +285 -0
  19. package/lib/export.js +1 -1
  20. package/lib/freeze-gate.js +3 -0
  21. package/lib/generated-provenance.js +178 -5
  22. package/lib/lane1-render.js +668 -0
  23. package/lib/memory-store.js +663 -0
  24. package/lib/model-licences.js +305 -0
  25. package/lib/npy-meta.js +130 -0
  26. package/lib/png-meta.js +512 -0
  27. package/lib/records.js +54 -8
  28. package/lib/reingest-selected.js +15 -1
  29. package/lib/reingest.js +27 -3
  30. package/lib/run-manifest.js +59 -11
  31. package/lib/train-runs.js +214 -0
  32. package/lib/verifier.js +3 -0
  33. package/lib/verify-chain.js +367 -0
  34. package/package.json +2 -2
  35. package/scripts/asset-ingest.js +160 -0
  36. package/scripts/canon-draft.js +82 -0
  37. package/scripts/canon-freeze.js +4 -0
  38. package/scripts/colab/REAL_SERVICE_ASSUMPTIONS.md +14 -0
  39. package/scripts/colab/ai-toolkit-override-py315.txt +45 -0
  40. package/scripts/colab/colab_window.sh +587 -0
  41. package/scripts/colab/lane1_render.py +970 -0
  42. package/scripts/colab/launch_guards.py +564 -0
  43. package/scripts/colab/launch_lane1_stub.sh +309 -0
  44. package/scripts/colab/launch_lane3_stub.sh +366 -0
  45. package/scripts/colab/remote_runner.py +59 -0
  46. package/scripts/doctor.js +1 -0
  47. package/scripts/generate.js +38 -12
  48. package/scripts/verify.js +67 -0
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env bash
2
+ # Stub launch kit for Lane 1 (render) Colab CPU high-mem window (Part C, PR #45).
3
+ # Checks pinned input sha256s (including itself) before opening, holds /tmp/colab-window-open.lock,
4
+ # opens CPU --high-mem, runs CPython 3.15.1 uv venv + pinned ComfyUI --cpu + comfy-headless + 64x64
5
+ # fixture render + stamp read-back + kill + resume, enforces live launch_guards.py (wall, cap,
6
+ # vitals/DEAD HARNESS, host-RAM), scrubs VM-side logs before tarring, downloads result tar, merges
7
+ # VM facts/balances/rate/downloaded shas into window.json, and stops its own session in an EXIT trap.
8
+ set -euo pipefail
9
+
10
+ PINNED_UV_VERSION="0.13.5"
11
+ PINNED_PYTHON_VERSION="3.15.1"
12
+ PINNED_PYTORCH_INDEX_URL="https://download.pytorch.org/whl/cu130"
13
+ PINNED_COMFYUI_REPO="https://github.com/comfyanonymous/ComfyUI.git"
14
+ PINNED_COMFYUI_COMMIT="158419f3a0017c2ce123484b14b6c527716d6ec8"
15
+ PINNED_COMFY_HEADLESS_SPEC="comfy-headless==3.1.0"
16
+ PINNED_LANE1_RENDER_SHA="75c3b356644287e2c57efd4c3b89a5bde4a6ae4705438a98c383af392e114013"
17
+ PINNED_LAUNCH_GUARDS_SHA="8e664906e6deb45b498a0193e916d2d424f7e9b5839f39e63ea07f81a4682a78"
18
+ PINNED_REMOTE_RUNNER_SHA="7d4fc25401c1504910615f1aaea4418a6694196530380c11c9a9a25b02f83537"
19
+ PINNED_COLAB_WINDOW_SHA="9e956d486dbeb0fbd37e61f30edcc12f13d2565cacba8be4c6bffeb13ec82ed3"
20
+ PINNED_SELF_SHA="51de88a27b640a8f7620c1eb0095ae1238c49991ce0c99e9334862b56ec07f35"
21
+
22
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
+ SELF_SH="${SCRIPT_DIR}/launch_lane1_stub.sh"
24
+ # shellcheck source=scripts/colab/colab_window.sh
25
+ source "${SCRIPT_DIR}/colab_window.sh"
26
+
27
+ CARD="${CARD:-CPU-high-mem}"
28
+ SESSION="sdlab-lane1-stub-$(date -u +%Y%m%dT%H%M%SZ)"
29
+ OUT_DIR=""
30
+ CAP="${CAP:-0.05}"
31
+ WALL_S="${WALL_S:-900}"
32
+ GRACE_S="${GRACE_S:-60}"
33
+ MARGIN="${MARGIN:-0.01}"
34
+
35
+ while [ "$#" -gt 0 ]; do
36
+ case "$1" in
37
+ --card) CARD="$2"; shift 2 ;;
38
+ --session) SESSION="$2"; shift 2 ;;
39
+ --out-dir) OUT_DIR="$2"; shift 2 ;;
40
+ --cap) CAP="$2"; shift 2 ;;
41
+ --wall-s) WALL_S="$2"; shift 2 ;;
42
+ --grace-s) GRACE_S="$2"; shift 2 ;;
43
+ --margin) MARGIN="$2"; shift 2 ;;
44
+ *)
45
+ echo "REFUSED: unknown argument: $1" >&2
46
+ exit 2
47
+ ;;
48
+ esac
49
+ done
50
+
51
+ if [ -z "${OUT_DIR}" ]; then
52
+ OUT_DIR="/tmp/${SESSION}"
53
+ fi
54
+ colab_refuse_nonempty_out_dir "${OUT_DIR}"
55
+ mkdir -p "${OUT_DIR}"
56
+
57
+ PY_BIN="$(colab_resolve_python)"
58
+ GUARDS_PY="${SCRIPT_DIR}/launch_guards.py"
59
+ LANE1_PY="${SCRIPT_DIR}/lane1_render.py"
60
+ RUNNER_PY="${SCRIPT_DIR}/remote_runner.py"
61
+ WINDOW_SH="${SCRIPT_DIR}/colab_window.sh"
62
+
63
+ colab_guard_fake_cli_if_test
64
+
65
+ EXP_LANE1_SHA="${EXPECTED_LANE1_RENDER_SHA:-${PINNED_LANE1_RENDER_SHA}}"
66
+ EXP_GUARDS_SHA="${EXPECTED_LAUNCH_GUARDS_SHA:-${PINNED_LAUNCH_GUARDS_SHA}}"
67
+ EXP_RUNNER_SHA="${EXPECTED_REMOTE_RUNNER_SHA:-${PINNED_REMOTE_RUNNER_SHA}}"
68
+ EXP_WINDOW_SHA="${EXPECTED_COLAB_WINDOW_SHA:-${PINNED_COLAB_WINDOW_SHA}}"
69
+ EXP_SELF_SHA="${EXPECTED_LAUNCH_LANE1_STUB_SHA:-${PINNED_SELF_SHA}}"
70
+
71
+ SHAS_JSON="$(colab_verify_input_shas "${PY_BIN}" "${GUARDS_PY}" \
72
+ "${LANE1_PY}=${EXP_LANE1_SHA}" \
73
+ "${GUARDS_PY}=${EXP_GUARDS_SHA}" \
74
+ "${RUNNER_PY}=${EXP_RUNNER_SHA}" \
75
+ "${WINDOW_SH}=${EXP_WINDOW_SHA}" \
76
+ "${SELF_SH}=${EXP_SELF_SHA}")"
77
+
78
+ OPENED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
79
+ START_EPOCH="$(date -u +%s)"
80
+ SESSION_OPENED=0
81
+ WALL_WATCHER_PID=""
82
+ EXEC_PID=""
83
+
84
+ cleanup_on_exit() {
85
+ local rc=$?
86
+ trap - EXIT TERM INT
87
+ if [ -n "${EXEC_PID}" ]; then
88
+ pkill -P "${EXEC_PID}" 2>/dev/null || true
89
+ kill -9 "${EXEC_PID}" 2>/dev/null || true
90
+ fi
91
+ if [ -n "${WALL_WATCHER_PID}" ]; then
92
+ pkill -P "${WALL_WATCHER_PID}" 2>/dev/null || true
93
+ kill -9 "${WALL_WATCHER_PID}" 2>/dev/null || true
94
+ fi
95
+ if [ "${SESSION_OPENED}" -eq 1 ]; then
96
+ colab_stop_and_log_closing "${PY_BIN}" "${SESSION}" "${OUT_DIR}"
97
+ fi
98
+ local closed_at
99
+ closed_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
100
+ colab_write_window_and_outcome \
101
+ "${PY_BIN}" "${OUT_DIR}" "render" "${SESSION}" "${CARD}" \
102
+ "${CAP}" "${WALL_S}" "${GRACE_S}" "${MARGIN}" \
103
+ "${OPENED_AT}" "${closed_at}" "${rc}" "${SHAS_JSON}" || true
104
+ colab_scrub_all_logs "${PY_BIN}" "${GUARDS_PY}" "${OUT_DIR}" || true
105
+ exit "${rc}"
106
+ }
107
+ trap cleanup_on_exit EXIT
108
+ trap 'exit 143' TERM INT
109
+
110
+ # Pre-open cap and host-RAM check via launch_guards.py
111
+ colab_check_cap_and_ram "${PY_BIN}" "${GUARDS_PY}" "stub" "0" "${WALL_S}" "${GRACE_S}" "${CAP}" "${MARGIN}" "${OUT_DIR}"
112
+
113
+ # Independent wall watcher (cannot be blocked by a child timeout)
114
+ WALL_INT="${WALL_S%.*}"
115
+ GRACE_INT="${GRACE_S%.*}"
116
+ WALL_LIMIT_S="$(( WALL_INT > GRACE_INT ? WALL_INT - GRACE_INT : 1 ))"
117
+ MAIN_PID="$$"
118
+ (
119
+ exec >/dev/null 2>&1 </dev/null
120
+ sleep "${WALL_LIMIT_S}"
121
+ echo "WALL_GUARD_TRIPPED after ${WALL_LIMIT_S}s" >> "${OUT_DIR}/guards.log"
122
+ kill -TERM "${MAIN_PID}" 2>/dev/null || true
123
+ ) &
124
+ WALL_WATCHER_PID="$!"
125
+
126
+ # Hold /tmp/colab-window-open.lock while opening with colab new (--high-mem default)
127
+ SESSION_OPENED=1
128
+ colab_open_session_locked "${SESSION}" "${CARD}" "${OUT_DIR}"
129
+ colab_scrub_all_logs "${PY_BIN}" "${GUARDS_PY}" "${OUT_DIR}"
130
+
131
+ REMOTE_RUNNER="${OUT_DIR}/remote_lane1_stub.sh"
132
+ cat > "${REMOTE_RUNNER}" << REMOTEEOF
133
+ #!/usr/bin/env bash
134
+ set -euo pipefail
135
+ export CUDA_VISIBLE_DEVICES=""
136
+ mkdir -p /content/lane1_out
137
+
138
+ : "\${PINNED_UV_VERSION:?missing PINNED_UV_VERSION}"
139
+ : "\${PINNED_PYTHON_VERSION:?missing PINNED_PYTHON_VERSION}"
140
+ : "\${PINNED_PYTORCH_INDEX_URL:?missing PINNED_PYTORCH_INDEX_URL}"
141
+ : "\${PINNED_COMFYUI_REPO:?missing PINNED_COMFYUI_REPO}"
142
+ : "\${PINNED_COMFYUI_COMMIT:?missing PINNED_COMFYUI_COMMIT}"
143
+ : "\${PINNED_COMFY_HEADLESS_SPEC:?missing PINNED_COMFY_HEADLESS_SPEC}"
144
+
145
+ echo "setup" > /content/lane1_out/phase.txt
146
+ : > /content/lane1_out/setup.log
147
+ (
148
+ while true; do
149
+ {
150
+ echo "=== VITALS \$(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
151
+ if [ -f /sys/fs/cgroup/memory.current ]; then
152
+ echo "CGROUP_MEM_CURRENT: \$(cat /sys/fs/cgroup/memory.current)" || true
153
+ fi
154
+ if [ -f /sys/fs/cgroup/memory.max ]; then
155
+ echo "CGROUP_MEM_MAX: \$(cat /sys/fs/cgroup/memory.max)" || true
156
+ fi
157
+ if [ -f /sys/fs/cgroup/memory.events ]; then
158
+ sed 's/^/CGROUP_MEM: /' /sys/fs/cgroup/memory.events || true
159
+ fi
160
+ grep -E '^(MemTotal|MemAvailable|AnonPages):' /proc/meminfo | sed 's/^/MEM: /' || true
161
+ SETUP_SZ="\$(wc -c < /content/lane1_out/setup.log 2>/dev/null | tr -d '[:space:]' || echo 0)"
162
+ echo "SETUP_BYTES: \${SETUP_SZ:-0}"
163
+ echo "PROC:"
164
+ ps -ef | grep -E "comfy|main\.py|lane1_render\.py" | grep -v grep || true
165
+ CUR_PHASE="\$(cat /content/lane1_out/phase.txt 2>/dev/null || echo setup)"
166
+ printf 'HARNESS: {"phase": "%s"}\n' "\${CUR_PHASE}"
167
+ } | tee -a /content/lane1_out/vitals.log
168
+ sleep 15
169
+ done
170
+ ) &
171
+ VITALS_PID="\$!"
172
+ trap 'kill "\${VITALS_PID}" 2>/dev/null || true' EXIT
173
+
174
+ {
175
+ unset UV_SYSTEM_PYTHON
176
+ export UV_PYTHON_INSTALL_DIR="/content/.sdlab_python_315/install"
177
+ pip install --quiet "uv==\${PINNED_UV_VERSION}"
178
+ uv python install "\${PINNED_PYTHON_VERSION}"
179
+ uv venv --python "\${PINNED_PYTHON_VERSION}" --seed /content/venv-lane1
180
+ /content/venv-lane1/bin/python -c '
181
+ import sys
182
+ if not (sys.version.startswith("3.15.1") and sys.prefix.startswith("/content/venv-lane")):
183
+ sys.stderr.write(f"REFUSED: VENV_PYTHON_MISMATCH version={sys.version!r} prefix={sys.prefix!r}\n")
184
+ raise SystemExit(1)
185
+ '
186
+ /content/venv-lane1/bin/python -c 'import sys; assert sys.version.startswith("3.15.1"), sys.version; assert sys.prefix.startswith("/content/venv-lane"), sys.prefix'
187
+ } 2>&1 | tee -a /content/lane1_out/setup.log
188
+ {
189
+ unset UV_SYSTEM_PYTHON
190
+ export UV_PYTHON_INSTALL_DIR="/content/.sdlab_python_315/install"
191
+ if [ ! -d /content/ComfyUI ]; then
192
+ git clone "\${PINNED_COMFYUI_REPO}" /content/ComfyUI
193
+ fi
194
+ git -C /content/ComfyUI fetch --depth 1 origin "\${PINNED_COMFYUI_COMMIT}" || true
195
+ git -C /content/ComfyUI checkout "\${PINNED_COMFYUI_COMMIT}"
196
+ ACTUAL_COMFYUI_COMMIT="\$(git -C /content/ComfyUI rev-parse HEAD)"
197
+ if [ "\${ACTUAL_COMFYUI_COMMIT}" != "\${PINNED_COMFYUI_COMMIT}" ]; then
198
+ echo "REFUSED: ComfyUI commit mismatch: got \${ACTUAL_COMFYUI_COMMIT}, expected \${PINNED_COMFYUI_COMMIT}" >&2
199
+ exit 1
200
+ fi
201
+
202
+ uv pip install --python /content/venv-lane1/bin/python \
203
+ -r /content/ComfyUI/requirements.txt \
204
+ --extra-index-url "\${PINNED_PYTORCH_INDEX_URL}" \
205
+ --index-strategy unsafe-best-match
206
+ uv pip install --python /content/venv-lane1/bin/python "\${PINNED_COMFY_HEADLESS_SPEC}"
207
+ /content/venv-lane1/bin/python -c '
208
+ import importlib
209
+ import sys
210
+
211
+ REQUIRED = ("comfy_headless", "torch", "safetensors", "PIL", "yaml", "aiohttp")
212
+ for mod_name in REQUIRED:
213
+ try:
214
+ mod = importlib.import_module(mod_name)
215
+ except Exception as exc:
216
+ sys.stderr.write(f"REFUSED: VENV_IMPORT_FAILED {mod_name}: {exc}\n")
217
+ raise SystemExit(1)
218
+ mod_file = getattr(mod, "__file__", None) or ""
219
+ print(f"VENV_IMPORT {mod_name} {mod_file}", flush=True)
220
+ if not mod_file.startswith("/content/venv-lane1/"):
221
+ sys.stderr.write(f"REFUSED: VENV_IMPORT_OUTSIDE_VENV {mod_name}: {mod_file}\n")
222
+ raise SystemExit(1)
223
+ from comfy_headless import ComfyClient
224
+ assert ComfyClient is not None
225
+ '
226
+ } 2>&1 | tee -a /content/lane1_out/setup.log
227
+
228
+ echo "run" > /content/lane1_out/phase.txt
229
+ /content/venv-lane1/bin/python /content/ComfyUI/main.py --cpu --listen 127.0.0.1 --port 8188 --disable-auto-launch \
230
+ > /content/lane1_out/comfyui.log 2>&1 &
231
+ COMFY_PID="\$!"
232
+
233
+ for _ in \$(seq 1 60); do
234
+ if curl -sf http://127.0.0.1:8188/system_stats >/dev/null 2>&1; then
235
+ break
236
+ fi
237
+ sleep 1
238
+ done
239
+
240
+ cat > /content/lane1_out/jobs.jsonl << 'JOBSEOF'
241
+ {"job_id":"stub_01","run_id":"run_stub_01","template_id":"stub_fixture_64","seed":101,"width":64,"height":64,"canon_sha":"a881cb5ba656e0618080d2269498fda7f36ac93a","entry_id":"ships/brig","entry_sha256":"sha256:1111111111111111111111111111111111111111111111111111111111111111","workflow_sha":"sha256:2222222222222222222222222222222222222222222222222222222222222222"}
242
+ {"job_id":"stub_02","run_id":"run_stub_01","template_id":"stub_fixture_64","seed":102,"width":64,"height":64,"canon_sha":"a881cb5ba656e0618080d2269498fda7f36ac93a","entry_id":"ships/brig","entry_sha256":"sha256:1111111111111111111111111111111111111111111111111111111111111111","workflow_sha":"sha256:2222222222222222222222222222222222222222222222222222222222222222"}
243
+ JOBSEOF
244
+
245
+ set +e
246
+ /content/venv-lane1/bin/python /content/lane1_render.py \
247
+ --cpu --bootstrap \
248
+ --comfy-pid "\${COMFY_PID}" \
249
+ --comfyui-commit "\${PINNED_COMFYUI_COMMIT}" \
250
+ --window-json /content/lane1_out/vm_window.json \
251
+ --jobs /content/lane1_out/jobs.jsonl \
252
+ --renders /content/lane1_out/renders.jsonl \
253
+ --out-dir /content/lane1_out/pngs \
254
+ --copy-back-dir /content/lane1_out/copy_back \
255
+ --copy-back-every 1 \
256
+ --kill-after 1
257
+ RC1=\$?
258
+ set -e
259
+ if [ "\${RC1}" -ne 75 ]; then
260
+ echo "Expected exit 75 on deliberate kill-after=1, got \${RC1}" >&2
261
+ exit 1
262
+ fi
263
+
264
+ SHA_BEFORE="\$(sha256sum /content/lane1_out/pngs/stub_01.png | awk '{print \$1}')"
265
+
266
+ /content/venv-lane1/bin/python /content/lane1_render.py \
267
+ --cpu \
268
+ --jobs /content/lane1_out/jobs.jsonl \
269
+ --renders /content/lane1_out/renders.jsonl \
270
+ --out-dir /content/lane1_out/pngs \
271
+ --copy-back-dir /content/lane1_out/copy_back \
272
+ --copy-back-every 1
273
+
274
+ SHA_AFTER="\$(sha256sum /content/lane1_out/pngs/stub_01.png | awk '{print \$1}')"
275
+ SHA_COPY="\$(sha256sum /content/lane1_out/copy_back/stub_01.png | awk '{print \$1}')"
276
+ if [ "\${SHA_BEFORE}" != "\${SHA_AFTER}" ] || [ "\${SHA_BEFORE}" != "\${SHA_COPY}" ]; then
277
+ echo "Resume hash mismatch: before=\${SHA_BEFORE} after=\${SHA_AFTER} copy=\${SHA_COPY}" >&2
278
+ exit 1
279
+ fi
280
+
281
+ kill "\${COMFY_PID}" 2>/dev/null || true
282
+ while IFS= read -r -d '' f; do
283
+ /content/venv-lane1/bin/python /content/launch_guards.py --scrub-file "\${f}" </dev/null || true
284
+ done < <(find /content/lane1_out -type f \( -name "*.log" -o -name "*.json" -o -name "*.txt" \) -print0 2>/dev/null || true)
285
+ tar -czf /content/lane1_stub_bundle.tar.gz -C /content/lane1_out .
286
+ REMOTEEOF
287
+
288
+ colab upload -s "${SESSION}" "${LANE1_PY}" /content/lane1_render.py </dev/null >> "${OUT_DIR}/colab.log" 2>&1
289
+ colab upload -s "${SESSION}" "${GUARDS_PY}" /content/launch_guards.py </dev/null >> "${OUT_DIR}/colab.log" 2>&1
290
+ colab upload -s "${SESSION}" "${REMOTE_RUNNER}" /content/remote_lane1_stub.sh </dev/null >> "${OUT_DIR}/colab.log" 2>&1
291
+
292
+ colab_run_exec_with_live_guards \
293
+ "${PY_BIN}" "${GUARDS_PY}" "${SESSION}" "${RUNNER_PY}" \
294
+ "${START_EPOCH}" "${WALL_S}" "${GRACE_S}" "${CAP}" "${MARGIN}" "${OUT_DIR}" \
295
+ "SDLAB_REMOTE_SCRIPT=/content/remote_lane1_stub.sh" \
296
+ "PINNED_UV_VERSION=${PINNED_UV_VERSION}" \
297
+ "PINNED_PYTHON_VERSION=${PINNED_PYTHON_VERSION}" \
298
+ "PINNED_PYTORCH_INDEX_URL=${PINNED_PYTORCH_INDEX_URL}" \
299
+ "PINNED_COMFYUI_REPO=${PINNED_COMFYUI_REPO}" \
300
+ "PINNED_COMFYUI_COMMIT=${PINNED_COMFYUI_COMMIT}" \
301
+ "PINNED_COMFY_HEADLESS_SPEC=${PINNED_COMFY_HEADLESS_SPEC}"
302
+
303
+ # Download result tar before EXIT trap stops the session
304
+ colab download -s "${SESSION}" /content/lane1_stub_bundle.tar.gz "${OUT_DIR}/lane1_stub_bundle.tar.gz" </dev/null >> "${OUT_DIR}/colab.log" 2>&1
305
+ if [ ! -s "${OUT_DIR}/lane1_stub_bundle.tar.gz" ]; then
306
+ echo "REFUSED: downloaded bundle ${OUT_DIR}/lane1_stub_bundle.tar.gz is missing or empty" >&2
307
+ exit 1
308
+ fi
309
+ colab_scrub_all_logs "${PY_BIN}" "${GUARDS_PY}" "${OUT_DIR}"
@@ -0,0 +1,366 @@
1
+ #!/usr/bin/env bash
2
+ # Stub launch kit for Lane 3 (train) Colab CPU high-mem window (Part C, PR #47).
3
+ # Checks pinned input sha256s (including itself) before opening, holds /tmp/colab-window-open.lock,
4
+ # opens CPU --high-mem, runs CPython 3.15.1 uv venv + pinned ostris/ai-toolkit install with
5
+ # av/torchcodec filtered + ai-toolkit-override-py315.txt + PyTorch cp315 wheel index (proving
6
+ # sentencepiece==0.2.1 and wheels) + generates synth_job.yaml for a 1-step CPU training run on the
7
+ # synthetic package + verifies the .safetensors checkpoint exists and records its path in window.json,
8
+ # enforces live launch_guards.py (wall, cap, vitals/DEAD HARNESS, host-RAM), scrubs VM-side logs
9
+ # before tarring, downloads result tar, and stops its own session in an EXIT trap on every exit path.
10
+ set -euo pipefail
11
+
12
+ PINNED_UV_VERSION="0.13.5"
13
+ PINNED_PYTHON_VERSION="3.15.1"
14
+ PINNED_PYTORCH_INDEX_URL="https://download.pytorch.org/whl/cu130"
15
+ PINNED_AI_TOOLKIT_REPO="https://github.com/ostris/ai-toolkit.git"
16
+ PINNED_AI_TOOLKIT_COMMIT="73ebae1334d2c98089f1b90f8cb19257825f891a"
17
+ PINNED_OVERRIDE_SHA="42eab7931fb6c9b9fec187ded6668bd4ee00bd95dadb4c31e0dc940cffc9e3a9"
18
+ PINNED_LAUNCH_GUARDS_SHA="8e664906e6deb45b498a0193e916d2d424f7e9b5839f39e63ea07f81a4682a78"
19
+ PINNED_REMOTE_RUNNER_SHA="7d4fc25401c1504910615f1aaea4418a6694196530380c11c9a9a25b02f83537"
20
+ PINNED_COLAB_WINDOW_SHA="9e956d486dbeb0fbd37e61f30edcc12f13d2565cacba8be4c6bffeb13ec82ed3"
21
+ PINNED_SELF_SHA="67c27c3d227eeead8593c58a36d802c01b45beb53821fb2bcddf1838a36a2866"
22
+
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+ SELF_SH="${SCRIPT_DIR}/launch_lane3_stub.sh"
25
+ # shellcheck source=scripts/colab/colab_window.sh
26
+ source "${SCRIPT_DIR}/colab_window.sh"
27
+
28
+ CARD="${CARD:-CPU-high-mem}"
29
+ SESSION="sdlab-lane3-stub-$(date -u +%Y%m%dT%H%M%SZ)"
30
+ OUT_DIR=""
31
+ CAP="${CAP:-0.05}"
32
+ WALL_S="${WALL_S:-900}"
33
+ GRACE_S="${GRACE_S:-60}"
34
+ MARGIN="${MARGIN:-0.01}"
35
+
36
+ while [ "$#" -gt 0 ]; do
37
+ case "$1" in
38
+ --card) CARD="$2"; shift 2 ;;
39
+ --session) SESSION="$2"; shift 2 ;;
40
+ --out-dir) OUT_DIR="$2"; shift 2 ;;
41
+ --cap) CAP="$2"; shift 2 ;;
42
+ --wall-s) WALL_S="$2"; shift 2 ;;
43
+ --grace-s) GRACE_S="$2"; shift 2 ;;
44
+ --margin) MARGIN="$2"; shift 2 ;;
45
+ *)
46
+ echo "REFUSED: unknown argument: $1" >&2
47
+ exit 2
48
+ ;;
49
+ esac
50
+ done
51
+
52
+ if [ -z "${OUT_DIR}" ]; then
53
+ OUT_DIR="/tmp/${SESSION}"
54
+ fi
55
+ colab_refuse_nonempty_out_dir "${OUT_DIR}"
56
+ mkdir -p "${OUT_DIR}"
57
+
58
+ PY_BIN="$(colab_resolve_python)"
59
+ GUARDS_PY="${SCRIPT_DIR}/launch_guards.py"
60
+ OVERRIDE_TXT="${SCRIPT_DIR}/ai-toolkit-override-py315.txt"
61
+ RUNNER_PY="${SCRIPT_DIR}/remote_runner.py"
62
+ WINDOW_SH="${SCRIPT_DIR}/colab_window.sh"
63
+
64
+ colab_guard_fake_cli_if_test
65
+
66
+ EXP_OVERRIDE_SHA="${EXPECTED_OVERRIDE_SHA:-${PINNED_OVERRIDE_SHA}}"
67
+ EXP_GUARDS_SHA="${EXPECTED_LAUNCH_GUARDS_SHA:-${PINNED_LAUNCH_GUARDS_SHA}}"
68
+ EXP_RUNNER_SHA="${EXPECTED_REMOTE_RUNNER_SHA:-${PINNED_REMOTE_RUNNER_SHA}}"
69
+ EXP_WINDOW_SHA="${EXPECTED_COLAB_WINDOW_SHA:-${PINNED_COLAB_WINDOW_SHA}}"
70
+ EXP_SELF_SHA="${EXPECTED_LAUNCH_LANE3_STUB_SHA:-${PINNED_SELF_SHA}}"
71
+
72
+ SHAS_JSON="$(colab_verify_input_shas "${PY_BIN}" "${GUARDS_PY}" \
73
+ "${OVERRIDE_TXT}=${EXP_OVERRIDE_SHA}" \
74
+ "${GUARDS_PY}=${EXP_GUARDS_SHA}" \
75
+ "${RUNNER_PY}=${EXP_RUNNER_SHA}" \
76
+ "${WINDOW_SH}=${EXP_WINDOW_SHA}" \
77
+ "${SELF_SH}=${EXP_SELF_SHA}")"
78
+
79
+ OPENED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
80
+ START_EPOCH="$(date -u +%s)"
81
+ SESSION_OPENED=0
82
+ WALL_WATCHER_PID=""
83
+ EXEC_PID=""
84
+
85
+ cleanup_on_exit() {
86
+ local rc=$?
87
+ trap - EXIT TERM INT
88
+ if [ -n "${EXEC_PID}" ]; then
89
+ pkill -P "${EXEC_PID}" 2>/dev/null || true
90
+ kill -9 "${EXEC_PID}" 2>/dev/null || true
91
+ fi
92
+ if [ -n "${WALL_WATCHER_PID}" ]; then
93
+ pkill -P "${WALL_WATCHER_PID}" 2>/dev/null || true
94
+ kill -9 "${WALL_WATCHER_PID}" 2>/dev/null || true
95
+ fi
96
+ if [ "${SESSION_OPENED}" -eq 1 ]; then
97
+ colab_stop_and_log_closing "${PY_BIN}" "${SESSION}" "${OUT_DIR}"
98
+ fi
99
+ local closed_at
100
+ closed_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
101
+ colab_write_window_and_outcome \
102
+ "${PY_BIN}" "${OUT_DIR}" "train" "${SESSION}" "${CARD}" \
103
+ "${CAP}" "${WALL_S}" "${GRACE_S}" "${MARGIN}" \
104
+ "${OPENED_AT}" "${closed_at}" "${rc}" "${SHAS_JSON}" || true
105
+ colab_scrub_all_logs "${PY_BIN}" "${GUARDS_PY}" "${OUT_DIR}" || true
106
+ exit "${rc}"
107
+ }
108
+ trap cleanup_on_exit EXIT
109
+ trap 'exit 143' TERM INT
110
+
111
+ # Pre-open cap and host-RAM check via launch_guards.py
112
+ colab_check_cap_and_ram "${PY_BIN}" "${GUARDS_PY}" "stub" "0" "${WALL_S}" "${GRACE_S}" "${CAP}" "${MARGIN}" "${OUT_DIR}"
113
+
114
+ # Independent wall watcher (cannot be blocked by a child timeout)
115
+ WALL_INT="${WALL_S%.*}"
116
+ GRACE_INT="${GRACE_S%.*}"
117
+ WALL_LIMIT_S="$(( WALL_INT > GRACE_INT ? WALL_INT - GRACE_INT : 1 ))"
118
+ MAIN_PID="$$"
119
+ (
120
+ exec >/dev/null 2>&1 </dev/null
121
+ sleep "${WALL_LIMIT_S}"
122
+ echo "WALL_GUARD_TRIPPED after ${WALL_LIMIT_S}s" >> "${OUT_DIR}/guards.log"
123
+ kill -TERM "${MAIN_PID}" 2>/dev/null || true
124
+ ) &
125
+ WALL_WATCHER_PID="$!"
126
+
127
+ # Hold /tmp/colab-window-open.lock while opening with colab new (--high-mem default)
128
+ SESSION_OPENED=1
129
+ colab_open_session_locked "${SESSION}" "${CARD}" "${OUT_DIR}"
130
+ colab_scrub_all_logs "${PY_BIN}" "${GUARDS_PY}" "${OUT_DIR}"
131
+
132
+ REMOTE_RUNNER="${OUT_DIR}/remote_lane3_stub.sh"
133
+ cat > "${REMOTE_RUNNER}" << REMOTEEOF
134
+ #!/usr/bin/env bash
135
+ set -euo pipefail
136
+ export CUDA_VISIBLE_DEVICES=""
137
+ mkdir -p /content/lane3_out /content/lane3_out/checkpoints
138
+
139
+ : "\${PINNED_UV_VERSION:?missing PINNED_UV_VERSION}"
140
+ : "\${PINNED_PYTHON_VERSION:?missing PINNED_PYTHON_VERSION}"
141
+ : "\${PINNED_PYTORCH_INDEX_URL:?missing PINNED_PYTORCH_INDEX_URL}"
142
+ : "\${PINNED_AI_TOOLKIT_REPO:?missing PINNED_AI_TOOLKIT_REPO}"
143
+ : "\${PINNED_AI_TOOLKIT_COMMIT:?missing PINNED_AI_TOOLKIT_COMMIT}"
144
+
145
+ echo "setup" > /content/lane3_out/phase.txt
146
+ : > /content/lane3_out/setup.log
147
+ (
148
+ while true; do
149
+ {
150
+ echo "=== VITALS \$(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
151
+ if [ -f /sys/fs/cgroup/memory.current ]; then
152
+ echo "CGROUP_MEM_CURRENT: \$(cat /sys/fs/cgroup/memory.current)" || true
153
+ fi
154
+ if [ -f /sys/fs/cgroup/memory.max ]; then
155
+ echo "CGROUP_MEM_MAX: \$(cat /sys/fs/cgroup/memory.max)" || true
156
+ fi
157
+ if [ -f /sys/fs/cgroup/memory.events ]; then
158
+ sed 's/^/CGROUP_MEM: /' /sys/fs/cgroup/memory.events || true
159
+ fi
160
+ grep -E '^(MemTotal|MemAvailable|AnonPages):' /proc/meminfo | sed 's/^/MEM: /' || true
161
+ SETUP_SZ="\$(wc -c < /content/lane3_out/setup.log 2>/dev/null | tr -d '[:space:]' || echo 0)"
162
+ echo "SETUP_BYTES: \${SETUP_SZ:-0}"
163
+ echo "PROC:"
164
+ ps -ef | grep -E "ai-toolkit|run\.py" | grep -v grep || true
165
+ CUR_PHASE="\$(cat /content/lane3_out/phase.txt 2>/dev/null || echo setup)"
166
+ printf 'HARNESS: {"phase": "%s"}\n' "\${CUR_PHASE}"
167
+ } | tee -a /content/lane3_out/vitals.log
168
+ sleep 15
169
+ done
170
+ ) &
171
+ VITALS_PID="\$!"
172
+ trap 'kill "\${VITALS_PID}" 2>/dev/null || true' EXIT
173
+
174
+ OVERRIDE_SHA="\$(sha256sum /content/ai-toolkit-override-py315.txt | awk '{print \$1}')"
175
+ echo "\${OVERRIDE_SHA} /content/ai-toolkit-override-py315.txt" > /content/lane3_out/override.sha256
176
+
177
+ {
178
+ unset UV_SYSTEM_PYTHON
179
+ export UV_PYTHON_INSTALL_DIR="/content/.sdlab_python_315/install"
180
+ if command -v apt-get >/dev/null 2>&1; then
181
+ apt-get update -qq && apt-get install -y -qq cmake pkg-config build-essential libsentencepiece-dev || true
182
+ fi
183
+ pip install --quiet "uv==\${PINNED_UV_VERSION}"
184
+ uv python install "\${PINNED_PYTHON_VERSION}"
185
+ uv venv --python "\${PINNED_PYTHON_VERSION}" --seed /content/venv-lane3
186
+ /content/venv-lane3/bin/python -c '
187
+ import sys
188
+ if not (sys.version.startswith("3.15.1") and sys.prefix.startswith("/content/venv-lane")):
189
+ sys.stderr.write(f"REFUSED: VENV_PYTHON_MISMATCH version={sys.version!r} prefix={sys.prefix!r}\n")
190
+ raise SystemExit(1)
191
+ '
192
+ /content/venv-lane3/bin/python -c 'import sys; assert sys.version.startswith("3.15.1"), sys.version; assert sys.prefix.startswith("/content/venv-lane"), sys.prefix'
193
+ } 2>&1 | tee -a /content/lane3_out/setup.log
194
+ {
195
+ unset UV_SYSTEM_PYTHON
196
+ export UV_PYTHON_INSTALL_DIR="/content/.sdlab_python_315/install"
197
+ if [ ! -d /content/ai-toolkit ]; then
198
+ git clone "\${PINNED_AI_TOOLKIT_REPO}" /content/ai-toolkit
199
+ fi
200
+ git -C /content/ai-toolkit fetch --depth 1 origin "\${PINNED_AI_TOOLKIT_COMMIT}" || true
201
+ git -C /content/ai-toolkit checkout "\${PINNED_AI_TOOLKIT_COMMIT}"
202
+ ACTUAL_AI_TOOLKIT_COMMIT="\$(git -C /content/ai-toolkit rev-parse HEAD)"
203
+ if [ "\${ACTUAL_AI_TOOLKIT_COMMIT}" != "\${PINNED_AI_TOOLKIT_COMMIT}" ]; then
204
+ echo "REFUSED: ai-toolkit commit mismatch: got \${ACTUAL_AI_TOOLKIT_COMMIT}, expected \${PINNED_AI_TOOLKIT_COMMIT}" >&2
205
+ exit 1
206
+ fi
207
+
208
+ grep -Ev '^(av|torchcodec)([=<>!~[:space:]]|\$)' /content/ai-toolkit/requirements.txt \
209
+ > /content/lane3_out/requirements.filtered.txt
210
+
211
+ uv pip install --python /content/venv-lane3/bin/python \
212
+ -r /content/lane3_out/requirements.filtered.txt \
213
+ --override /content/ai-toolkit-override-py315.txt \
214
+ --extra-index-url "\${PINNED_PYTORCH_INDEX_URL}" \
215
+ --index-strategy unsafe-best-match \
216
+ 2>&1 | tee /content/lane3_out/wheels.log
217
+
218
+ /content/venv-lane3/bin/python -c '
219
+ import importlib
220
+ import sys
221
+
222
+ REQUIRED = ("sentencepiece", "torch", "safetensors", "yaml", "oyaml", "PIL")
223
+ for mod_name in REQUIRED:
224
+ try:
225
+ mod = importlib.import_module(mod_name)
226
+ except Exception as exc:
227
+ sys.stderr.write(f"REFUSED: VENV_IMPORT_FAILED {mod_name}: {exc}\n")
228
+ raise SystemExit(1)
229
+ mod_file = getattr(mod, "__file__", None) or ""
230
+ print(f"VENV_IMPORT {mod_name} {mod_file}", flush=True)
231
+ if not mod_file.startswith("/content/venv-lane3/"):
232
+ sys.stderr.write(f"REFUSED: VENV_IMPORT_OUTSIDE_VENV {mod_name}: {mod_file}\n")
233
+ raise SystemExit(1)
234
+ import sentencepiece as spm
235
+ print("SENTENCEPIECE_OK", spm.__version__, flush=True)
236
+ ' | tee /content/lane3_out/sentencepiece.txt
237
+ } 2>&1 | tee -a /content/lane3_out/setup.log
238
+
239
+ /content/venv-lane3/bin/python - << 'SYNTHEOF'
240
+ import hashlib
241
+ import json
242
+ import struct
243
+ import zlib
244
+ from pathlib import Path
245
+
246
+ pkg = Path("/content/lane3_out/synth_pkg")
247
+ pkg.mkdir(parents=True, exist_ok=True)
248
+
249
+ def make_png(seed_b: int) -> bytes:
250
+ sig = b"\x89PNG\r\n\x1a\n"
251
+ ihdr_d = struct.pack(">IIBBBBB", 64, 64, 8, 2, 0, 0, 0)
252
+ ihdr = struct.pack(">I", 13) + b"IHDR" + ihdr_d + struct.pack(">I", zlib.crc32(b"IHDR" + ihdr_d) & 0xFFFFFFFF)
253
+ row = bytes([0] + [seed_b & 0xFF, (seed_b * 3) & 0xFF, (seed_b * 7) & 0xFF] * 64)
254
+ idat_d = zlib.compress(row * 64)
255
+ idat = struct.pack(">I", len(idat_d)) + b"IDAT" + idat_d + struct.pack(">I", zlib.crc32(b"IDAT" + idat_d) & 0xFFFFFFFF)
256
+ iend = struct.pack(">I", 0) + b"IEND" + struct.pack(">I", zlib.crc32(b"IEND") & 0xFFFFFFFF)
257
+ return sig + ihdr + idat + iend
258
+
259
+ items = []
260
+ for idx in (1, 2):
261
+ img_b = make_png(idx * 17)
262
+ img_p = pkg / f"sample_{idx:02d}.png"
263
+ txt_p = pkg / f"sample_{idx:02d}.txt"
264
+ img_p.write_bytes(img_b)
265
+ txt_p.write_text(f"portlight ship synthetic fixture {idx}\n", encoding="utf-8")
266
+ items.append({"image": img_p.name, "sha256": hashlib.sha256(img_b).hexdigest()})
267
+
268
+ (pkg / "dataset_manifest.json").write_text(json.dumps({"seed": 42, "items": items}, indent=2) + "\n", encoding="utf-8")
269
+ SYNTHEOF
270
+
271
+ cat > /content/lane3_out/synth_job.yaml << 'YAMLEOF'
272
+ job: extension
273
+ config:
274
+ name: synth_lora
275
+ process:
276
+ - type: sd_trainer
277
+ training_folder: /content/lane3_out/checkpoints
278
+ device: cpu
279
+ network:
280
+ type: lora
281
+ linear: 4
282
+ linear_alpha: 4
283
+ save:
284
+ dtype: float32
285
+ save_every: 1
286
+ max_step_saves_to_keep: 1
287
+ datasets:
288
+ - folder_path: /content/lane3_out/synth_pkg
289
+ caption_ext: txt
290
+ resolution: [64, 64]
291
+ train:
292
+ batch_size: 1
293
+ steps: 1
294
+ lr: 0.0001
295
+ optimizer: adamw
296
+ dtype: fp32
297
+ YAMLEOF
298
+
299
+ echo "run" > /content/lane3_out/phase.txt
300
+ /content/venv-lane3/bin/python /content/ai-toolkit/run.py /content/lane3_out/synth_job.yaml \
301
+ > /content/lane3_out/train.log 2>&1
302
+
303
+ CHECKPOINT_PATH="/content/lane3_out/checkpoints/synth_lora.safetensors"
304
+ if [ ! -s "\${CHECKPOINT_PATH}" ]; then
305
+ FOUND_CKPT="\$(find /content/lane3_out/checkpoints -name '*.safetensors' -type f | head -n 1 || true)"
306
+ if [ -n "\${FOUND_CKPT}" ] && [ -s "\${FOUND_CKPT}" ]; then
307
+ cp "\${FOUND_CKPT}" "\${CHECKPOINT_PATH}"
308
+ else
309
+ echo "REFUSED: expected non-empty .safetensors checkpoint at \${CHECKPOINT_PATH}" >&2
310
+ exit 1
311
+ fi
312
+ fi
313
+
314
+ /content/venv-lane3/bin/python - "\${PINNED_AI_TOOLKIT_COMMIT}" "\${OVERRIDE_SHA}" "\${CHECKPOINT_PATH}" << 'WINPYEOF'
315
+ import hashlib
316
+ import json
317
+ import subprocess
318
+ import sys
319
+ from pathlib import Path
320
+
321
+ ai_commit = sys.argv[1]
322
+ override_sha = sys.argv[2]
323
+ ckpt_path = sys.argv[3]
324
+ sys_py = subprocess.run(["/usr/bin/python3", "--version"], capture_output=True, text=True, check=False).stdout.strip().split()[-1]
325
+ uv_ver = subprocess.run(["uv", "--version"], capture_output=True, text=True, check=False).stdout.strip()
326
+ py_sha = "sha256:" + hashlib.sha256(Path(sys.executable).read_bytes()).hexdigest()
327
+ rec = {
328
+ "system_python": sys_py,
329
+ "python": sys.version.split()[0],
330
+ "python_sha256": py_sha,
331
+ "uv": uv_ver,
332
+ "ai_toolkit_commit": ai_commit,
333
+ "override_sha256": f"sha256:{override_sha}",
334
+ "checkpoint_path": ckpt_path,
335
+ }
336
+ Path("/content/lane3_out/vm_window.json").write_text(json.dumps(rec, indent=2) + "\n", encoding="utf-8")
337
+ print("WINDOW_JSON " + json.dumps(rec), flush=True)
338
+ WINPYEOF
339
+
340
+ while IFS= read -r -d '' f; do
341
+ /content/venv-lane3/bin/python /content/launch_guards.py --scrub-file "\${f}" </dev/null || true
342
+ done < <(find /content/lane3_out -type f \( -name "*.log" -o -name "*.json" -o -name "*.txt" \) -print0 2>/dev/null || true)
343
+ tar -czf /content/lane3_stub_bundle.tar.gz -C /content/lane3_out .
344
+ REMOTEEOF
345
+
346
+ colab upload -s "${SESSION}" "${OVERRIDE_TXT}" /content/ai-toolkit-override-py315.txt </dev/null >> "${OUT_DIR}/colab.log" 2>&1
347
+ colab upload -s "${SESSION}" "${GUARDS_PY}" /content/launch_guards.py </dev/null >> "${OUT_DIR}/colab.log" 2>&1
348
+ colab upload -s "${SESSION}" "${REMOTE_RUNNER}" /content/remote_lane3_stub.sh </dev/null >> "${OUT_DIR}/colab.log" 2>&1
349
+
350
+ colab_run_exec_with_live_guards \
351
+ "${PY_BIN}" "${GUARDS_PY}" "${SESSION}" "${RUNNER_PY}" \
352
+ "${START_EPOCH}" "${WALL_S}" "${GRACE_S}" "${CAP}" "${MARGIN}" "${OUT_DIR}" \
353
+ "SDLAB_REMOTE_SCRIPT=/content/remote_lane3_stub.sh" \
354
+ "PINNED_UV_VERSION=${PINNED_UV_VERSION}" \
355
+ "PINNED_PYTHON_VERSION=${PINNED_PYTHON_VERSION}" \
356
+ "PINNED_PYTORCH_INDEX_URL=${PINNED_PYTORCH_INDEX_URL}" \
357
+ "PINNED_AI_TOOLKIT_REPO=${PINNED_AI_TOOLKIT_REPO}" \
358
+ "PINNED_AI_TOOLKIT_COMMIT=${PINNED_AI_TOOLKIT_COMMIT}"
359
+
360
+ # Download result tar before EXIT trap stops the session
361
+ colab download -s "${SESSION}" /content/lane3_stub_bundle.tar.gz "${OUT_DIR}/lane3_stub_bundle.tar.gz" </dev/null >> "${OUT_DIR}/colab.log" 2>&1
362
+ if [ ! -s "${OUT_DIR}/lane3_stub_bundle.tar.gz" ]; then
363
+ echo "REFUSED: downloaded bundle ${OUT_DIR}/lane3_stub_bundle.tar.gz is missing or empty" >&2
364
+ exit 1
365
+ fi
366
+ colab_scrub_all_logs "${PY_BIN}" "${GUARDS_PY}" "${OUT_DIR}"