Orbit as a Galaxy analysis workbench (standalone + Galaxy Interactive Tool)#330
Open
dannon wants to merge 19 commits into
Open
Orbit as a Galaxy analysis workbench (standalone + Galaxy Interactive Tool)#330dannon wants to merge 19 commits into
dannon wants to merge 19 commits into
Conversation
Import initGalaxyPageSync and flushNotebookToGalaxy into the lifecycle, and call them at the appropriate hooks: init resumes the per-history page on launch, and flush guarantees one final push (including any session-summary block) on shutdown.
Live testing against test.galaxyproject.org showed resume-on-launch silently
failed: a fresh container only knows the derived slug (orbit-<historyId>), but
Galaxy's GET /pages/{id} 400s on a slug. List the history's pages, match the
slug, and resume by the real page id. Without this a fresh container also can't
update the existing page -- the next push would try to create a duplicate slug.
Tool XML (scoped api_key + $__galaxy_url__ injection, port 3000, ALLOW_INSECURE trust model, explicit server start command) and a deploy README covering image build, registration, the admin-key-via-job_conf path, a custom-provider option, and the trust model. Claude-Session: https://claude.ai/code/session_015LfwTStrqyxDTCv19PbTkT
The container never booted: web/server.ts imports ./auth.js and ./rpc-guard.js (added by the auth-hardening commit), but the runner stage's COPY list was never updated to include them -> ERR_MODULE_NOT_FOUND /app/web/auth.js at startup. Caught by the Plan 3 container smoke test; verified the image now boots, serves the prebuilt renderer, and the agent responds.
Live GxIT testing on a local Galaxy surfaced two things: Galaxy injects the host TMPDIR into the container (a macOS /var/folders path that tsx can't use -> EACCES), so the command forces TMPDIR=/tmp; and node:22-slim has no uvx, so galaxy-mcp (tool/workflow execution) fails in-container while notebook->Page persistence (direct API) still works. Documented both in the deploy guide.
The prior commit documented that node:22-slim has no Python or uvx, so the agent's Galaxy tool/workflow surface (galaxy-mcp, launched via `uvx galaxy-mcp>=1.8.0`) never started in the container and Galaxy tools silently vanished. Fix it: copy uv from Astral's published image, point its cache, python-install, and tool dirs at a node-owned /opt/uv, and pre-warm `uv tool install galaxy-mcp>=1.8.0` at build so the package and a managed Python are baked in. Verified the server now launches and answers an MCP initialize fully offline (--network none), resolving entirely from the baked cache -- so it works even in a network-locked job container. Notebook->Page persistence was already fine (direct API); this restores the execution half.
Live GxIT testing surfaced a second blocker after uvx: the agent could
connect to galaxy-mcp but couldn't invoke any of its tools. On a fresh
container pi-mcp-adapter never registers the galaxy_*/brc_analytics_* direct
tools -- that needs a warm per-server metadata cache, and the per-user scoped
GALAXY_API_KEY changes the cache's config hash every launch, so it's always
cold. The only path to those servers is then the single `mcp` proxy tool,
which the gate denied by default ("mcp is not available in remote mode").
Allow the `mcp` proxy, but scope it: a tool call (or connect) must target one
of the curated servers (galaxy, brc-analytics), mirroring what ALLOWED_PREFIXES
already permits for direct tools; read-only discovery (search/describe/list/
status/ui-messages) passes through. A call with no server is unverifiable, so
it's denied with a message telling the agent to set it -- which the model then
does on its own. Verified live in the GxIT: the agent retrieved 42 histories
and ran an upload_file_from_url job that landed a dataset, both through the
scoped proxy.
8cb8a86 to
cae0ebe
Compare
make Docker container executable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Runs the existing
LOOM_MODE=remoteOrbit web shell as a per-user agentic analysis workbench, from one image, two ways: standalone (docker run) and as a Galaxy Interactive Tool -- Galaxy launches a per-user container, injects the server URL + a scoped API key, and proxies the user in.The point is that durable artifacts live in Galaxy: tools/workflows run as Galaxy jobs (outputs are history datasets), and the analysis notebook persists as a per-history Galaxy Page that resumes on relaunch. Meant as a proving ground for ideas that can fold back into Galaxy's own assistant, not a replacement.
What's in here
Notebook -> Galaxy Page persistence (
extensions/loom/galaxy-page-sync.ts)LOOM_GALAXY_PAGE_SYNC=auto), brain-side (pi's rpc dispatch is closed, so the shell just sets the env at spawn and drains the brain on shutdown)orbit-<historyId>), current history viamost_recently_usedGET /pages/{id})session-lifecycle.ts; enabled + SIGTERM-drained inweb/server.tsGalaxy Interactive Tool wrapper (
gxit/) -- tool XML (scopedinject="api_key"+$__galaxy_url__,requires_domainentry point) + a deploy guide.Container fixes (
Dockerfile)web/auth.ts+web/rpc-guard.tsin the runner -- theLOOM_MODE=remoteimage didn't actually boot without them (ERR_MODULE_NOT_FOUND)TMPDIR=/tmp(Galaxy injects the host's TMPDIR, whichtsxcan't use)uv+ pre-warmgalaxy-mcpso the Galaxy tool/workflow execution surface loads in-container (node:slimhas no Python/uvx)Remote-mode tool gate (
web/extensions/web-mode-gate.ts) -- on a fresh container thegalaxy_*MCP tools don't register as direct tools (needs a warm per-server cache, which the per-user key invalidates each launch), so the only path is themcpproxy. Allow it, scoped to the curated servers (galaxy / brc-analytics); discovery passes, an unscoped call is denied with a message the agent self-corrects on.Bring-your-own LLM key (
web/llm-credentials.ts,web/server.ts, web shim + renderer) -- when the remote shell starts with no provider key in its env, the renderer prompts for a provider + key, the server holds it in memory for the session (never persisted, logged, or echoed -- only ahasApiKeyboolean crosses to the renderer) and spawns the brain with it. An admin-injected env key still auto-spawns exactly as before. So the workbench can spread without an admin-baked key. (Standard providers; custom/openai-compatibleBYO is a follow-up.)Testing
New unit tests for the page-sync engine, the gate's proxy scoping, the history helper, and the LLM-credential helpers; full suite green, typechecks clean. Verified live as a real Galaxy Interactive Tool (Galaxy 26.1, interactive tools + gx-it-proxy + Docker): the notebook persisted to a per-history Page, and the agent -- running entirely in the container -- invoked Galaxy MCP through the scoped proxy and ran an
upload_file_from_urljob that landed a dataset. The BYO-key flow was verified at the WebSocket-protocol level: no env key -> the server reportshasApiKey:falseand holds off spawning until the key is provided; an admin env key ->hasApiKey:true+ auto-spawn; the key never reaches the logs orconfig.json.Notes
LOOM_MODE=remoteshell; branch is based on an oldermainand will want a rebase. Follow-ups: wire BYO for custom/openai-compatibleproviders (baseUrl + key), and a browser-level eyeball of the BYO overlay.