一键导入
jhub-deploy
Use when a user asks Pi to build and deploy arbitrary web apps through JupyterHub named servers using direct Hub API calls (no nebari_app_* tools).
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when a user asks Pi to build and deploy arbitrary web apps through JupyterHub named servers using direct Hub API calls (no nebari_app_* tools).
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Convert documents and files to Markdown using markitdown. Use when converting PDF, Word (.docx), PowerPoint (.pptx), Excel (.xlsx, .xls), HTML, CSV, JSON, XML, images (with EXIF/OCR), audio (with transcription), ZIP archives, YouTube URLs, or EPubs to Markdown format for LLM processing or text analysis.
Allows to interact with web pages by performing actions such as clicking buttons, filling out forms, and navigating links. It works by remote controlling Google Chrome or Chromium browsers using the Chrome DevTools Protocol (CDP). When Claude needs to browse the web, it can use this skill to do so.
Use when Pi needs to troubleshoot logs/metrics/traces or verify LGTM (Grafana/Loki/Mimir/Tempo) access in local k3s, cloud Kubernetes, or any generic K8s setup.
| name | jhub-deploy |
| description | Use when a user asks Pi to build and deploy arbitrary web apps through JupyterHub named servers using direct Hub API calls (no nebari_app_* tools). |
Use this skill when the user wants agents to:
This skill piggybacks on JupyterHub named servers.
Resolve Hub API context from runtime:
API_URL="${NEBARI_HUB_API_URL:-${JUPYTERHUB_API_URL:-}}"
API_TOKEN="${NEBARI_HUB_API_TOKEN:-${JUPYTERHUB_API_TOKEN:-}}"
HUB_USER="${JUPYTERHUB_USER:-${NB_USER:-}}"
[ -n "$API_URL" ] || echo "Missing API_URL"
[ -n "$API_TOKEN" ] || echo "Missing API_TOKEN"
[ -n "$HUB_USER" ] || echo "Missing HUB_USER"
Validate token can read own user:
curl -fsS -H "Authorization: token $API_TOKEN" \
"$API_URL/users/$HUB_USER?include_stopped_servers=true" >/dev/null
If this fails, stop and report missing scopes/credentials.
Before selecting command/image strategy, verify required runtime dependencies for the target app.
Examples:
pdftotext, ffmpeg, etc.)If required binaries are missing in target image/runtime:
/home/jovyan/...)Critical rule: use source-on-home mode only if shared storage between build context and target named server is confirmed.
execs runtimeDo not prefer framework-native templates. Treat all apps as arbitrary command-based runtimes.
Before using source-on-home mode, verify whether target named server shares storage with where artifacts were built.
Minimum checks:
Failure message should explicitly say source-on-home was rejected due to non-shared/unknown storage topology.
profile_image is empty)profile_image: "" can resolve to a broken default image.
Required behavior:
profile_image is empty.JUPYTER_IMAGE_SPEC from current runtime, elseImagePullBackOff/ErrImagePull), retry once with resolved known-good image.Assume deployment supports a command-based user_options contract for named servers.
Preferred payload shape:
{
"profile": "<optional-profile>",
"app": {
"cwd": "/home/jovyan/apps/myapp",
"command": "<runtime command with {port} placeholder>",
"env": {"KEY": "VALUE"}
},
"profile_image": "<optional-image>"
}
If your Hub uses a different key shape, adapt payload to the deployment’s configured spawner hooks.
To avoid brittle spawn failures, default to this pattern:
app.command.{port} in the final runtime invocation.python3 -c exec(__import__('base64').b64decode('<BASE64_PY>').decode()) {port}
Where <BASE64_PY> decodes to Python that:
os.execv(...) to start the long-running server process.Avoid by default:
bash -c strings,&&, pipes, heredocs),Before making the spawn API call, validate that app.command is actually runnable in the target server.
Hard-fail guard:
app.command references a local binary path (for example /home/jovyan/apps/.../bin/...) andRequired behavior:
Minimum remediation message should explain:
Large embedded payloads in app.command can fail with argument-length errors.
Required rule:
app.command small (target: <= 4KB; hard-stop around 8KB).app.command.Practical payload thresholds:
APP_NAME="my-app"
PAYLOAD_FILE="/tmp/${APP_NAME}-spawn.json"
cat > "$PAYLOAD_FILE" <<'JSON'
{
"profile": "",
"app": {
"cwd": "/home/jovyan/apps/my-app",
"command": "python3 -c exec(__import__('base64').b64decode('<BASE64_PY>').decode()) {port}",
"env": {
"NODE_ENV": "production"
}
},
"profile_image": ""
}
JSON
curl -sS -X POST \
-H "Authorization: token $API_TOKEN" \
-H "Content-Type: application/json" \
"$API_URL/users/$HUB_USER/servers/$APP_NAME" \
--data-binary @"$PAYLOAD_FILE"
Expected: HTTP 201 or 202.
Check server status:
curl -sS -H "Authorization: token $API_TOKEN" \
"$API_URL/users/$HUB_USER?include_stopped_servers=true"
Canonical URL:
/user/<username>/<app-name>/Pending URL:
/hub/spawn-pending/<username>/<app-name>Never report "deployed" until all checks below pass. If any check fails, report deployment as failed (or verification-incomplete), include diagnostics, and stop claiming success.
Required checks:
Hub readiness check (CLI)
ready=true and not pending.Interim/proxy failure check (CLI)
/hub/spawn-pending/.../_temp/jhub-app-proxy/...Mandatory browser smoke check (HEADLESS BROWSER)
pi-browser-smoke from inside the Pi runtime.Internal-first example:
INTERNAL_BASE_URL="${NEBARI_BROWSER_INTERNAL_BASE_URL:-http://proxy-public}"
EXTERNAL_BASE_URL="${NEBARI_BROWSER_BASE_URL:-}"
APP_PATH="/user/${HUB_USER}/${APP_NAME}/"
pi-browser-smoke \
--base-url "$INTERNAL_BASE_URL" \
--app-path "$APP_PATH" \
--username "$HUB_USER" \
--hub-api-url "$API_URL" \
--hub-api-token "$API_TOKEN" \
--timeout-seconds 120 \
--screenshot "/tmp/${APP_NAME}-smoke-internal.png"
Optional external follow-up:
if [ -n "$EXTERNAL_BASE_URL" ] && [ "$EXTERNAL_BASE_URL" != "$INTERNAL_BASE_URL" ]; then
pi-browser-smoke \
--base-url "$EXTERNAL_BASE_URL" \
--app-path "$APP_PATH" \
--username "$HUB_USER" \
--hub-api-url "$API_URL" \
--hub-api-token "$API_TOKEN" \
--timeout-seconds 120 \
--screenshot "/tmp/${APP_NAME}-smoke-external.png"
fi
Notes:
Browser smoke pass criteria (minimum):
/user/<username>/<app-name>/ path,/hub/spawn-pending/ or /_temp/jhub-app-proxy/,2xx/3xx),document.body.External auth-path classification rule:
external_auth_path_issue (not app-runtime failure).Diagnostic hint:
No-browser fallback policy
pi-browser-smoke is unavailable or cannot run, return verification_incomplete and do not claim deployment success.After restarts/redeploys, verify you are testing the new process (not stale binaries):
ps, readlink, --version),If identity/port checks fail, treat verification as incomplete and re-run deployment/startup.
Stop (keep definition):
curl -sS -X DELETE \
-H "Authorization: token $API_TOKEN" \
"$API_URL/users/$HUB_USER/servers/$APP_NAME?remove=false"
Delete (remove server entry):
curl -sS -X DELETE \
-H "Authorization: token $API_TOKEN" \
"$API_URL/users/$HUB_USER/servers/$APP_NAME?remove=true"
Before claiming success for SPA-style apps, check subpath compatibility for Hub-routed paths (/user/<u>/<app>/...).
Checklist:
/assets/...), prefer relative (./assets/...)/api/...), prefer relative (./api/...)base: './' unless app requires a different explicit subpathDo not depend on temporary pod-local HTTP file servers for cross-pod artifact delivery.
Note:
Preferred order:
Examples of arbitrary commands (adjust as needed):
npm run start -- --host 0.0.0.0 --port {port}node server.js --port {port}./bin/server --port {port}java -jar app.jar --server.port={port}pending, ready, stopped, state).kubectl get events -A --sort-by=.lastTimestamp | tail -n 150
Common causes:
app.command (prefer Python-base64 wrapper)ImagePullBackOff / ErrImagePull:
likely bad/unresolvable image; retry with known-good resolved image.ready=true but browser smoke blank/near-empty UI:
likely SPA subpath/base path issue.argument list too long:
payload too large in app.command; move payload to env/file/artifact.fork/exec ... no such file or directory:
target server missing artifacts/binary; storage is isolated or bootstrap missing.Always report:
profile_image was empty)passed / external_auth_path_issue / failed),