Interactive Terminal Process

Overview

A process type that gives the user an interactive IPython session with access to the project's storage context. Unlike regular processes, the "run" phase is a human-driven PTY session rather than an automated script. The initializer and finalizer are identical to all other process types — only the middle is different.

The session is exposed in the frontend as an xterm.js terminal widget connected over WebSocket.

Design Decisions

Architecture

Process Runner (new process type)

A new process type InteractiveProcess in docker/base-runner/nagelfluh_processes/:

class InteractiveProcess:
    def schema(self):
        return {}  # no parameters needed

    def run(self, storage_context, parameters):
        import IPython
        IPython.start_ipython(argv=[], user_ns={
            "fs": storage_context.fs,
            "base": storage_context.base_path,
        })

The runner calls initialize(), then run() (which blocks on the IPython PTY until the user exits), then finalize(). No changes to the runner's init/finalize logic.

The pre-seeded namespace:

fs    # fsspec AbstractFileSystem pointed at project storage
base  # str — base path for this process, e.g. "s3://nagelfluh-project-abc/processes/proc-id/"

Writing outputs is just normal fsspec usage:

with fs.open(base + "result.xyz", "wb") as f:
    f.write(data)
# exit() → finalizer picks up result.xyz and registers it as an output dataset

Backend (WebSocket PTY proxy)

New endpoint, e.g. WS /ws/pty/{process_id}, that:

  1. Locates the running Kubernetes pod for the process
  2. Opens a kubectl exec PTY connection to it (or equivalent API call)
  3. Proxies raw bytes between the WebSocket client and the PTY

This is analogous to the existing /ws/logs endpoint but bidirectional.

The process stays in "running" state for the lifetime of the session. On PTY close, the runner finalizes and the process transitions to "completed" as normal.

Frontend (xterm.js widget)

A new widget TerminalView in frontend/src/widgets/TerminalView/:

Registered in frontend/src/App.js alongside the other widgets. Displayed when the active process is of type interactive.

Implementation Steps

  1. Runner: Add InteractiveProcess process type; ensure start_ipython receives correct user_ns and blocks until exit.
  2. Backend: Add WS /ws/pty/{process_id} endpoint with kubectl exec PTY proxy.
  3. Frontend: Add TerminalView widget with xterm.js, wired to the PTY WebSocket.
  4. Registration: Verify finalizer correctly scans and registers files written under base — this should already work if other process types use the same finalizer.

Non-goals