Skip to content

Latest commit

 

History

History
317 lines (253 loc) · 7.56 KB

File metadata and controls

317 lines (253 loc) · 7.56 KB

tuispec server Reference

This document describes the implemented tuispec server JSON-RPC behavior.

Goals

  • Run interactive PTY-backed TUI sessions over JSON-RPC 2.0.
  • Keep one active session per server process.
  • Provide launch/input/wait/snapshot APIs plus utility methods for batching, notifications, and JSONL recording/replay.

Transport

  • Framing: newline-delimited JSON objects (one JSON-RPC message per line).
  • Input: stdin.
  • Output: stdout.
  • Mode: request/response plus optional server notifications.

CLI

tuispec server --artifact-dir PATH [--cols N] [--rows N] [--timeout-seconds N] [--ambiguity-mode fail|first-visible|last-visible]

Defaults:

  • cols = 134
  • rows = 40
  • timeout-seconds = 5
  • ambiguity-mode = fail

Session Model

  • Single active session at a time.
  • Initialize with initialize.
  • Non-lifecycle methods require an active session unless noted.

Coordinate Rules

  • Row/column coordinates in server APIs are 1-based.
  • 0 is reserved as a wildcard for row/column range selectors in currentView filters (entireRow, entireCol, and region/rows wildcard forms).
  • Selector at/within.rect coordinates must be >= 1.

Method Surface

Lifecycle

  • initialize

    • params:
      • name (optional, default "session")
      • optional run overrides:
        • timeoutSeconds
        • terminalCols
        • terminalRows
        • ambiguityMode (fail|first|first-visible|last|last-visible)
        • snapshotTheme
        • updateSnapshots
    • result:
      • sessionName
      • artifactRoot
      • rows
      • cols
      • version
  • launch

    • params:
      • command (required)
      • args (optional, default [])
      • env (optional object of string -> string|null)
        • string value: set/override
        • null: unset inherited env var
      • cwd (optional)
      • readySelector (optional selector)
      • readyTimeoutMs (optional)
      • readyPollIntervalMs (optional)
    • result:
      • ok

Input / Wait

  • sendKey

    • params: key
  • sendText

    • params: text
  • sendLine

    • params:
      • text
      • optional expectAfter selector
      • optional timeoutMs
      • optional pollIntervalMs
      • optional ambiguityMode
    • result: ok
  • waitForText

    • params:
      • selector
      • optional timeoutMs
      • optional pollIntervalMs
      • optional ambiguityMode
    • result: ok
  • waitUntil

    • params:
      • pattern (regex-like pattern over full currentView.text)
      • optional timeoutMs
      • optional pollIntervalMs
    • result: ok
  • waitForStable

    • params:
      • debounceMs (required): viewport must be unchanged for this many milliseconds
      • optional timeoutMs: overall timeout (defaults to session timeoutSeconds × 1000)
      • optional pollIntervalMs: poll interval (default 100)
    • result: ok
    • Polls the viewport at pollIntervalMs intervals and returns once the visible text has remained identical for at least debounceMs consecutive milliseconds. Use this instead of fixed sleeps to wait for TUI output to settle.

View / Snapshot

  • currentView

    • optional params (choose exactly one filter mode):
      • rows: { "start": Int, "end": Int }
      • region: { "col": Int, "row": Int, "width": Int, "height": Int }
      • entireRow: Int
      • entireCol: Int
    • result:
      • text
      • rows
      • cols
  • dumpView

    • params:
      • name
      • optional format: ansi|png|both (default ansi)
      • optional render overrides for PNG mode:
        • theme
        • font
        • rows
        • cols
    • result:
      • snapshotPath (canonical)
      • metaPath (canonical)
      • artifactRoot (canonical)
      • optional pngPath
  • renderView

    • params:
      • name
      • optional render overrides: theme, font, rows, cols
    • result:
      • snapshotPath
      • metaPath
      • pngPath
      • artifactRoot
  • diffView

    • params:
      • leftPath
      • rightPath
      • optional mode: text|styled (default text)
    • result:
      • changed
      • changedLines
      • summary
  • expectSnapshot

    • params: name
    • result:
      • ok
      • actualPath
      • baselinePath
      • baselineExists

Assertions

  • expectVisible

    • params: selector
    • result: ok
  • expectNotVisible

    • params: selector
    • result: ok

Notifications

  • viewSubscribe

    • params (optional):
      • debounceMs (default 100)
      • includeText (default false)
    • result:
      • ok
      • debounceMs
      • includeText
  • viewUnsubscribe

    • params: none
    • result: ok

When subscribed, the server emits JSON-RPC notifications:

{"jsonrpc":"2.0","method":"view.changed","params":{"rows":40,"cols":134}}

If includeText=true, notification params also include text.

Batch

  • batch
    • params:
      • steps: array of { "method": Text, "params": Value }
    • execution model:
      • sequential
      • non-atomic
      • stop on first failure
    • result:
      • on success: { "ok": true, "completed": N, "results": [...] }
      • on failure: { "ok": false, "completed": N, "results": [...], "errorStep": K, "error": {...} }

Recording / Replay (JSONL)

  • recording.start

    • params:
      • path (required): output JSONL path
      • frameIntervalMs (optional, default 200): viewport sampling interval in milliseconds. Set to 0 to disable frame capture. Default 200ms = 5 Hz.
    • result: { "ok": true, "path": FilePath, "frameIntervalMs": Int }
  • recording.stop

    • params: none
    • result: { "ok": true }
  • recording.status

    • params: none
    • result: { "active": Bool, "path": FilePath|null }
  • replay

    • params:
      • path
      • optional speed: as-fast-as-possible|real-time
    • result:
      • ok
      • replayedRequests
      • path
      • speed

Recording files are JSONL with one event per line and include:

  • timestampMicros
  • direction (request|response|notification|frame|frame-delta)
  • line (raw JSON-RPC line, viewport text, or delta payload)

When frameIntervalMs > 0 (the default), the server spawns a background thread that samples the viewport at the configured rate. Only frames that differ from the previous sample are written. Two frame event types are used:

  • frame — full keyframe, emitted roughly every second. line contains the complete rendered viewport text.
  • frame-delta — compact delta, emitted between keyframes. line contains a JSON array of [lineIndex, "new line text"] pairs for each changed line.

The replay CLI reconstructs full frames by applying deltas to the most recent keyframe.

Server Utility

  • server.ping

    • result:
      • pong: true
      • version
  • server.shutdown

    • result:
      • shuttingDown: true
    • then hard-shutdown:
      • send SIGKILL to active child process group
      • exit process immediately

Selector Encoding

  • exact:
    • {"type":"exact","text":"Ready"}
  • regex:
    • {"type":"regex","pattern":"Ready|Done"}
  • at (1-based):
    • {"type":"at","col":10,"row":2}
  • within (1-based origin):
    • {"type":"within","rect":{"col":1,"row":1,"width":40,"height":10},"selector":{...}}
  • nth:
    • {"type":"nth","index":1,"selector":{...}}

Error Contract

JSON-RPC errors:

  • parse failures: pARSE_ERROR
  • invalid request: iNVALID_REQUEST
  • unknown method: mETHOD_NOT_FOUND
  • bad params: iNVALID_PARAMS

Server-domain codes:

  • -32001 no active session
  • -32002 session already started
  • -32004 method failed

Signal Behavior

On SIGHUP, server does hard-shutdown:

  • send SIGKILL to active child process group
  • close recording handle
  • exit immediately