{
  "openapi": "3.1.0",
  "info": {
    "title": "Tubeless Local Studio API",
    "version": "0.2.2",
    "description": "HTTP contract for the local-only API started by `tubeless ui`. The public tubeless.io site hosts this contract but does not execute pipelines.",
    "license": {
      "name": "MIT",
      "url": "https://github.com/chetmancini/tubeless/blob/main/LICENSE"
    }
  },
  "externalDocs": {
    "description": "Local Studio documentation",
    "url": "https://tubeless.io/docs/studio"
  },
  "servers": [
    {
      "description": "Loopback-only Studio server (default port)",
      "url": "http://127.0.0.1:{port}",
      "variables": {
        "port": {
          "default": "4317",
          "description": "Port printed by `tubeless ui`; it may differ when --port or port 0 is used."
        }
      }
    }
  ],
  "tags": [
    {
      "name": "Observation",
      "description": "Read projected run history."
    },
    {
      "name": "Commands",
      "description": "Inspect, plan, and launch registered commands."
    },
    {
      "name": "Maintenance",
      "description": "Optional cancellation and history maintenance."
    }
  ],
  "paths": {
    "/api/snapshot": {
      "get": {
        "operationId": "getStudioSnapshot",
        "summary": "Read the current projected run snapshot",
        "tags": [
          "Observation"
        ],
        "responses": {
          "200": {
            "description": "Current snapshot with log bodies omitted and live launch IDs included.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/capabilities": {
      "get": {
        "operationId": "getStudioCapabilities",
        "summary": "Read optional Studio capabilities",
        "tags": [
          "Observation"
        ],
        "responses": {
          "200": {
            "description": "Capabilities enabled by the host process.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capabilities"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/api/runs/{runId}": {
      "get": {
        "operationId": "getStudioRun",
        "summary": "Read one recorded run and its events",
        "tags": [
          "Observation"
        ],
        "parameters": [
          {
            "description": "Recorded pipeline run ID.",
            "in": "path",
            "name": "runId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Projected run plus its stored events.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/commands": {
      "get": {
        "operationId": "listStudioCommands",
        "summary": "List explicitly registered commands",
        "tags": [
          "Commands"
        ],
        "responses": {
          "200": {
            "description": "Registered command descriptors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommandList"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/api/commands/{commandId}/plan": {
      "post": {
        "operationId": "planStudioCommand",
        "summary": "Preview selection for a registered command",
        "tags": [
          "Commands"
        ],
        "parameters": [
          {
            "description": "Registered command ID.",
            "in": "path",
            "name": "commandId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Same-origin request guard.",
            "in": "header",
            "name": "x-tubeless-studio-plan",
            "required": true,
            "schema": {
              "const": "1",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Selection-only pipeline plan. No steps have run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "405": {
            "$ref": "#/components/responses/CapabilityNotEnabled"
          },
          "415": {
            "$ref": "#/components/responses/GuardRequired"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/commands/{commandId}/runs": {
      "post": {
        "operationId": "launchStudioCommand",
        "summary": "Launch a registered command",
        "description": "This operation can cause the side effects declared by the selected command.",
        "tags": [
          "Commands"
        ],
        "parameters": [
          {
            "description": "Registered command ID.",
            "in": "path",
            "name": "commandId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Same-origin request guard.",
            "in": "header",
            "name": "x-tubeless-studio-launch",
            "required": true,
            "schema": {
              "const": "1",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LaunchRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Launch accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LaunchAccepted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid values or a launch rejected by command validation.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/LaunchRejected"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/CapabilityNotEnabled"
          },
          "415": {
            "$ref": "#/components/responses/GuardRequired"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/runs/{runId}/cancel": {
      "post": {
        "operationId": "cancelStudioRun",
        "summary": "Cancel a live process-local launch",
        "tags": [
          "Maintenance"
        ],
        "parameters": [
          {
            "description": "Live pipeline run ID.",
            "in": "path",
            "name": "runId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Same-origin request guard.",
            "in": "header",
            "name": "x-tubeless-studio-cancel",
            "required": true,
            "schema": {
              "const": "1",
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Cancellation accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/CapabilityNotEnabled"
          },
          "415": {
            "$ref": "#/components/responses/GuardRequired"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/history": {
      "delete": {
        "operationId": "clearStudioHistory",
        "summary": "Clear all recorded history",
        "tags": [
          "Maintenance"
        ],
        "parameters": [
          {
            "description": "Same-origin request guard.",
            "in": "header",
            "name": "x-tubeless-studio-clear-history",
            "required": true,
            "schema": {
              "const": "1",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "History cleared.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClearResult"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "405": {
            "$ref": "#/components/responses/CapabilityNotEnabled"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "415": {
            "$ref": "#/components/responses/GuardRequired"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "BadRequest": {
        "description": "The request path or JSON body is invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The Host header does not match the local Studio authority.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "The requested command, run, or endpoint does not exist.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "CapabilityNotEnabled": {
        "description": "The host process did not enable this capability.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Conflict": {
        "description": "The request conflicts with live Studio state.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "GuardRequired": {
        "description": "A same-origin request guard or JSON content type is missing.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "InternalError": {
        "description": "The Studio encountered an unexpected local error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "code",
          "error",
          "hint",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code."
          },
          "error": {
            "type": "string",
            "description": "Backward-compatible alias of message."
          },
          "hint": {
            "type": "string",
            "description": "Actionable recovery guidance."
          },
          "message": {
            "type": "string",
            "description": "Human-readable error summary."
          }
        }
      },
      "Capabilities": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "canCancel",
          "canClearHistory"
        ],
        "properties": {
          "canCancel": {
            "type": "boolean"
          },
          "canClearHistory": {
            "type": "boolean"
          }
        }
      },
      "Snapshot": {
        "type": "object",
        "required": [
          "activeRunCount",
          "completedRunCount",
          "liveRunIds",
          "runs"
        ],
        "properties": {
          "activeRunCount": {
            "type": "integer",
            "minimum": 0
          },
          "completedRunCount": {
            "type": "integer",
            "minimum": 0
          },
          "liveRunIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "runs": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        },
        "additionalProperties": true
      },
      "RunDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "events",
          "run"
        ],
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "run": {
            "type": "object"
          }
        }
      },
      "CommandList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "commands"
        ],
        "properties": {
          "commands": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Command"
            }
          }
        }
      },
      "Command": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "canPlan",
          "id",
          "name",
          "parameters"
        ],
        "properties": {
          "canPlan": {
            "type": "boolean"
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "parameters": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "PlanInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "dryRun": {
            "type": "boolean"
          },
          "stepIds": {
            "type": "array",
            "maxItems": 128,
            "items": {
              "type": "string",
              "maxLength": 4096
            }
          },
          "targets": {
            "type": "array",
            "maxItems": 128,
            "items": {
              "type": "string",
              "maxLength": 4096
            }
          }
        }
      },
      "PlanResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "plan"
        ],
        "properties": {
          "plan": {
            "type": "object"
          }
        }
      },
      "LaunchRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "values"
        ],
        "properties": {
          "values": {
            "type": "object",
            "maxProperties": 128,
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "number"
                },
                {
                  "type": "string",
                  "maxLength": 4096
                },
                {
                  "type": "array",
                  "maxItems": 128,
                  "items": {
                    "type": "number"
                  }
                },
                {
                  "type": "array",
                  "maxItems": 128,
                  "items": {
                    "type": "string",
                    "maxLength": 4096
                  }
                }
              ]
            }
          }
        }
      },
      "LaunchAccepted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "accepted",
          "runId"
        ],
        "properties": {
          "accepted": {
            "const": true,
            "type": "boolean"
          },
          "runId": {
            "type": "string"
          }
        }
      },
      "LaunchRejected": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "accepted",
          "code",
          "error",
          "errors",
          "hint",
          "message"
        ],
        "properties": {
          "accepted": {
            "const": false,
            "type": "boolean"
          },
          "code": {
            "const": "launch_rejected",
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "hint": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "CancelAccepted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "cancelled",
          "runId"
        ],
        "properties": {
          "cancelled": {
            "const": true,
            "type": "boolean"
          },
          "runId": {
            "type": "string"
          }
        }
      },
      "ClearResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "cleared",
          "eventCount",
          "runCount"
        ],
        "properties": {
          "cleared": {
            "const": true,
            "type": "boolean"
          },
          "eventCount": {
            "type": "integer",
            "minimum": 0
          },
          "runCount": {
            "type": "integer",
            "minimum": 0
          }
        }
      }
    }
  },
  "x-tubeless-hosted-api": false,
  "x-tubeless-spec-url": "https://tubeless.io/openapi.json"
}
