# Models

## Error

```json
{
  "type": "object",
  "properties": {
    "error": {
      "type": "string"
    }
  },
  "required": [
    "error"
  ]
}
```

## FingerprintEnvelope

```json
{
  "type": "object",
  "properties": {
    "fingerprint": {
      "$ref": "#/components/schemas/Fingerprint"
    }
  },
  "required": [
    "fingerprint"
  ]
}
```

## Fingerprint

```json
{
  "type": "object",
  "description": "Нативный FingerprintConfig. Документированные подобъекты; наиболее\nраспространённые; объект открытый и сохраняется эндпоинтами создания\nдословно, так что любое поле здесь будет передано без изменений.\n",
  "additionalProperties": true,
  "properties": {
    "name": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    },
    "seed": {
      "type": "integer",
      "description": "0 = режим только переопределений (типичный случай)."
    },
    "navigator": {
      "type": "object",
      "additionalProperties": true,
      "properties": {
        "user_agent": {
          "type": "string"
        },
        "platform": {
          "type": "string",
          "example": "Win32"
        },
        "hardware_concurrency": {
          "type": "integer",
          "example": 12
        },
        "device_memory": {
          "type": "integer",
          "enum": [
            8,
            16,
            32
          ],
          "example": 16
        },
        "language": {
          "type": "string"
        },
        "do_not_track": {
          "type": "boolean"
        }
      }
    },
    "screen": {
      "type": "object",
      "additionalProperties": true,
      "properties": {
        "width": {
          "type": "integer"
        },
        "height": {
          "type": "integer"
        },
        "avail_width": {
          "type": "integer"
        },
        "avail_height": {
          "type": "integer"
        },
        "color_depth": {
          "type": "integer",
          "example": 24
        },
        "device_pixel_ratio": {
          "type": "number",
          "example": 2
        },
        "color_gamut": {
          "type": "string",
          "enum": [
            "",
            "srgb",
            "p3",
            "rec2020"
          ]
        },
        "refresh_rate": {
          "type": "integer",
          "minimum": 24,
          "maximum": 480,
          "example": 60,
          "description": "Частота обновления заявленного экрана. Если поле не указано, используется значение 60. См. поле `refresh_rate` в запросах создания и редактирования."
        }
      }
    },
    "client_hints": {
      "type": "object",
      "description": "Семейство Sec-CH-UA (в browser.vision называется `hints`).",
      "additionalProperties": true,
      "properties": {
        "platform": {
          "type": "string"
        },
        "platform_version": {
          "type": "string"
        },
        "architecture": {
          "type": "string",
          "example": "x86"
        },
        "bitness": {
          "type": "string",
          "example": "64"
        },
        "mobile": {
          "type": "boolean"
        }
      }
    },
    "webgl": {
      "type": "object",
      "additionalProperties": true,
      "properties": {
        "unmasked_vendor": {
          "type": "string"
        },
        "unmasked_renderer": {
          "type": "string"
        }
      }
    },
    "webgpu": {
      "type": "object",
      "additionalProperties": true
    },
    "webrtc": {
      "type": "string",
      "description": "Режим обработки WebRTC.",
      "enum": [
        "auto",
        "tcp_only",
        "block"
      ]
    },
    "noise": {
      "$ref": "#/components/schemas/NoisePrefs"
    },
    "network": {
      "type": "object",
      "properties": {
        "blocked_ports": {
          "type": "array",
          "items": {
            "type": "integer"
          },
          "description": "Защита от сканирования портов локальной сети (ports_protection)."
        }
      }
    },
    "timezone": {
      "type": "string",
      "description": "Имя по IANA или \"auto\"; будет определено через привязанный прокси при запуске."
    }
  }
}
```

## NoisePrefs

```json
{
  "type": "object",
  "description": "Настройки антифингерпринт-шума по векторам. Каждый блок: `{enabled, seed}`\n(+ специфичные параметры). `enabled:false` = сквозная передача.\n",
  "properties": {
    "canvas": {
      "$ref": "#/components/schemas/NoiseBlock"
    },
    "webgl": {
      "allOf": [
        {
          "$ref": "#/components/schemas/NoiseBlock"
        }
      ],
      "properties": {
        "intensity": {
          "type": "number"
        }
      }
    },
    "audio": {
      "allOf": [
        {
          "$ref": "#/components/schemas/NoiseBlock"
        }
      ],
      "properties": {
        "intensity": {
          "type": "number"
        }
      }
    },
    "client_rects": {
      "allOf": [
        {
          "$ref": "#/components/schemas/NoiseBlock"
        }
      ],
      "properties": {
        "max_offset": {
          "type": "number"
        }
      }
    },
    "sensors": {
      "$ref": "#/components/schemas/NoiseBlock"
    },
    "fonts": {
      "allOf": [
        {
          "$ref": "#/components/schemas/NoiseBlock"
        }
      ],
      "description": "`enabled:true` скрывает небольшое детерминированное подмножество\nнесущественных шрифтов для каждого профиля (остальные реальные шрифты\nхоста проходят насквозь).\n"
    }
  }
}
```

## NoiseBlock

```json
{
  "type": "object",
  "properties": {
    "enabled": {
      "type": "boolean"
    },
    "seed": {
      "type": "integer",
      "description": "Энтропия на профиль; 0 = вычислить автоматически."
    }
  }
}
```

## CreateProfileRequest

```json
{
  "type": "object",
  "required": [
    "fingerprint"
  ],
  "properties": {
    "name": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    },
    "proxy_id": {
      "type": "string",
      "description": "Привязать прокси из хранилища (по id)."
    },
    "proxy": {
      "type": "string",
      "description": "Привязать прокси строкой (`host:port:user:pass` или\n`scheme://user:pass@host:port`). Для **постоянного** профиля прокси\nдобавляется в хранилище (дедупликация по эндпоинту) и проходит\nполный тест (UDP + гео), затем привязывается по id; отображается\nв списке прокси, результат QUIC/WebRTC кешируется. Для **временного**\nпрофиля прокси прикрепляется инлайн (не сохраняется). Игнорируется,\nесли задан `proxy_id`.\n",
      "example": "socks5://user:pass@1.2.3.4:1080"
    },
    "folder": {
      "type": "string",
      "description": "Тег папки (игнорируется для /folders/{folder}/profiles; там приоритет у пути)."
    },
    "color": {
      "type": "string",
      "description": "Акцентный цвет значка окна профиля и метки с его названием в адресной строке. Если поле не указано, цвет значка определяется по имени, а метка сохраняет цвет панели инструментов браузера.",
      "example": "#2FCB80"
    },
    "extensions": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Идентификаторы расширений из библиотеки (`GET /extensions`), загружаемых при запуске. Если идентификатор отсутствует в библиотеке, возвращается ошибка 400. Профиль без такого расширения не запускается автоматически."
    },
    "refresh_rate": {
      "type": "integer",
      "minimum": 24,
      "maximum": 480,
      "example": 60,
      "description": "Заявленная частота обновления дисплея, записанная в `screen.refresh_rate`. Ни один веб-API не сообщает об этом; страница измеряет его по времени requestAnimationFrame, поэтому его исключение не является нейтральным; «Отсутствие» означает 60, о чем сообщает большинство машин, а не о том, что делает собственный экран хоста. Кадры можно только замедлять, поэтому эффективная скорость равна ближайшему целому разделению панели хоста на этом или меньшем уровне."
    },
    "fingerprint": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Fingerprint"
        }
      ],
      "description": "Сохраняется дословно. Получите из /fingerprint/new и при необходимости отредактируйте."
    }
  }
}
```

## EditProfileRequest

```json
{
  "type": "object",
  "description": "Все поля необязательны; изменяется только переданное.",
  "properties": {
    "name": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    },
    "folder": {
      "type": "string",
      "description": "\"\" убирает профиль из папки (переводит в «Все»)."
    },
    "proxy_id": {
      "type": "string",
      "description": "\"\" отвязывает прокси."
    },
    "proxy": {
      "type": "string",
      "description": "Строка прокси; сохраняется (дедупликация) + полный тест, затем привязывается."
    },
    "color": {
      "type": "string",
      "description": "`#rrggbb`; пустая строка возвращает цвет, вычисленный по названию."
    },
    "extensions": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Заменяет весь список; `[]` ничего не загружает."
    },
    "refresh_rate": {
      "type": "integer",
      "minimum": 24,
      "maximum": 480,
      "example": 60,
      "description": "Заявленная частота обновления дисплея применяется ПОСЛЕ `fingerprint` и записывается в `screen.refresh_rate`. Ни один веб-API не сообщает об этом; страница измеряет его по времени requestAnimationFrame, поэтому его исключение не является нейтральным; «Отсутствие» означает 60, о чем сообщает большинство машин, а не о том, что делает собственный экран хоста. Кадры можно только замедлять, поэтому эффективная скорость равна ближайшему целому разделению панели хоста на этом или меньшем уровне."
    },
    "fingerprint": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Fingerprint"
        }
      ],
      "description": "При наличии ЗАМЕНЯЕТ сохранённый отпечаток дословно."
    }
  }
}
```

## TempProfileRequest

```json
{
  "type": "object",
  "properties": {
    "fingerprint_id": {
      "type": "string",
      "description": "Запись библиотеки, на которой основывается профиль; при отсутствии; случайный под `platform`."
    },
    "platform": {
      "type": "string",
      "enum": [
        "Windows",
        "macOS",
        "Linux"
      ],
      "description": "Используется для выбора случайного отпечатка, если `fingerprint_id` не указан; при отсутствии обоих; ОС хоста."
    },
    "proxy": {
      "type": "string",
      "description": "Инлайн-строка прокси (не сохраняется в хранилище)."
    },
    "name": {
      "type": "string"
    },
    "folder": {
      "type": "string"
    },
    "refresh_rate": {
      "type": "integer",
      "minimum": 24,
      "maximum": 480,
      "example": 60,
      "description": "Заявленная частота обновления дисплея, записанная в `screen.refresh_rate`. Ни один веб-API не сообщает об этом; страница измеряет его по времени requestAnimationFrame, поэтому его исключение не является нейтральным; «Отсутствие» означает 60, о чем сообщает большинство машин, а не о том, что делает собственный экран хоста. Кадры можно только замедлять, поэтому эффективная скорость равна ближайшему целому разделению панели хоста на этом или меньшем уровне."
    },
    "noise": {
      "type": "object",
      "description": "Шум отпечатка для каждого вектора. Пропущенные векторы сохраняют значения по умолчанию (отключены). Можно комбинировать два формата: сокращение `{\"canvas\": true, \"webgl\": true}`, которое также применяет интенсивность пользовательского интерфейса по умолчанию к `webgl` и `client_rects`, и полный объект, такой как `{\"canvas\": {\"enabled\": true, \"seed\": 42}}`, соответствующий формату, возвращаемому `GET /profiles/{id}`.\n\nПоля объединяются с существующими настройками векторов. Если `seed` не указан или равен нулю, стабильное значение создается на основе идентификатора профиля.",
      "additionalProperties": {
        "oneOf": [
          {
            "type": "boolean"
          },
          {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "seed": {
                "type": "integer",
                "format": "int64"
              },
              "intensity": {
                "type": "number",
                "description": "Только `webgl`."
              },
              "max_offset": {
                "type": "number",
                "description": "Только `client_rects`."
              }
            }
          }
        ]
      },
      "properties": {
        "canvas": {},
        "webgl": {},
        "audio": {},
        "client_rects": {},
        "sensors": {},
        "fonts": {}
      },
      "example": {
        "canvas": true,
        "webgl": {
          "enabled": true,
          "intensity": 0.0005
        },
        "fonts": true
      }
    }
  }
}
```

## Extension

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Передайте это в `extensions` профиля."
    },
    "name": {
      "type": "string"
    },
    "version": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "size_bytes": {
      "type": "integer",
      "format": "int64"
    },
    "added_at": {
      "type": "string",
      "description": "`@<unix_secs>`."
    },
    "icon": {
      "type": "string",
      "nullable": true,
      "description": "Самый большой значок, объявленный в манифесте, как URL-адрес `data:`. Значение null для конечной точки листинга и для расширений, которые не поставляются."
    }
  }
}
```

## AddExtensionRequest

```json
{
  "type": "object",
  "description": "Ровно один из `url` или `path`.",
  "properties": {
    "url": {
      "type": "string",
      "description": "Страница Интернет-магазина Chrome, простой 32-значный идентификатор расширения или прямая ссылка на `.crx`/`.zip`.",
      "example": "https://chromewebstore.google.com/detail/rabby-wallet/acmacodkjbdgmoleebolmdjonilkdbch"
    },
    "path": {
      "type": "string",
      "description": "Локальная `.crx`, `.zip` или распакованная папка на компьютере лаунчера."
    }
  }
}
```

## Bookmark

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "title": {
      "type": "string"
    },
    "url": {
      "type": "string"
    },
    "folder": {
      "type": "string",
      "description": "папка лаунчера; пустой означает каждый профиль."
    }
  }
}
```

## BookmarkRequest

```json
{
  "type": "object",
  "required": [
    "url"
  ],
  "properties": {
    "id": {
      "type": "string",
      "description": "Представлять обновления, которые добавляются в закладки; отсутствие создает его."
    },
    "url": {
      "type": "string",
      "description": "Голый хост подойдет; Добавлен `https://`.",
      "example": "facebook.com/ads/manager"
    },
    "title": {
      "type": "string",
      "description": "Бланк использует сам адрес."
    },
    "folder": {
      "type": "string",
      "description": "Пусто (или отсутствует) означает каждый профиль."
    }
  }
}
```

## TrashEntry

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "folder": {
      "type": "string"
    },
    "deleted_at": {
      "type": "integer",
      "format": "int64",
      "description": "Unix-секунды."
    },
    "expires_at": {
      "type": "integer",
      "format": "int64",
      "description": "Unix-секунды; очистка при запуске удаляет все, что находится за ней."
    },
    "size_bytes": {
      "type": "integer",
      "format": "int64",
      "description": "Архив, в котором хранится аккаунт, а не кэши."
    }
  }
}
```

## ProfileMeta

```json
{
  "type": "object",
  "description": "Метаданные профиля в лаунчере (возвращаются при создании).",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    },
    "proxy_id": {
      "type": "string",
      "nullable": true
    },
    "last_launched_at": {
      "type": "string",
      "nullable": true,
      "description": "ISO-8601 или null."
    },
    "created_at": {
      "type": "string",
      "nullable": true
    },
    "pinned": {
      "type": "boolean"
    },
    "folder": {
      "type": "string"
    },
    "color": {
      "type": "string",
      "nullable": true,
      "description": "Акцент на значке как `#rrggbb`; `null` означает, что оно получено из имени, что и делает браузер, когда ничего не выбрано."
    },
    "extensions": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Идентификаторы библиотек расширений, загружаемые при запуске."
    }
  }
}
```

## ProfileListItem

```json
{
  "allOf": [
    {
      "$ref": "#/components/schemas/ProfileMeta"
    },
    {
      "type": "object",
      "properties": {
        "running": {
          "type": "boolean"
        },
        "pid": {
          "type": "integer",
          "format": "int64",
          "nullable": true
        },
        "cdp": {
          "nullable": true,
          "allOf": [
            {
              "$ref": "#/components/schemas/CdpInfo"
            }
          ]
        }
      }
    }
  ]
}
```

## StoredProfile

```json
{
  "type": "object",
  "description": "Полный профиль на диске: оболочка `_meta` плюс развёрнутый\nFingerprintConfig (все поля `Fingerprint` на верхнем уровне).\n`running`/`cdp` присутствуют только во время работы профиля.\n",
  "additionalProperties": true,
  "properties": {
    "_meta": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "format": "uuid"
        },
        "proxy_id": {
          "type": "string",
          "nullable": true
        },
        "inline_proxy": {
          "nullable": true,
          "allOf": [
            {
              "$ref": "#/components/schemas/ProxyEntry"
            }
          ]
        },
        "last_launched_at": {
          "type": "string",
          "nullable": true
        },
        "created_at": {
          "type": "string",
          "nullable": true
        },
        "pinned": {
          "type": "boolean"
        },
        "folder": {
          "type": "string"
        },
        "temporary": {
          "type": "boolean"
        },
        "rev": {
          "type": "integer",
          "description": "Каждая запись увеличивает номер версии. Редактор рабочего стола передает номер версии, на котором был открыт профиль, поэтому сохранение поверх изменения, сделанного через этот API, отклоняется, а не отменяется автоматически. Изменения через API не отклоняются: профиль читается и записывается обратно за один шаг."
        }
      }
    },
    "running": {
      "type": "boolean"
    },
    "cdp": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/CdpInfo"
        }
      ]
    }
  }
}
```

## CdpInfo

```json
{
  "type": "object",
  "description": "CDP-эндпоинт запущенного профиля.",
  "properties": {
    "port": {
      "type": "integer",
      "description": "Порт remote-debugging"
    },
    "http_url": {
      "type": "string",
      "example": "http://127.0.0.1:53217",
      "description": "REST-база (/json, /json/version)."
    },
    "web_socket_debugger_url": {
      "type": "string",
      "example": "ws://127.0.0.1:53217/devtools/browser/ab12-...",
      "description": "WS-эндпоинт уровня браузера для Puppeteer/Playwright `connect`."
    }
  }
}
```

## RunningProfile

```json
{
  "type": "object",
  "properties": {
    "profile_id": {
      "type": "string"
    },
    "pid": {
      "type": "integer",
      "format": "int64"
    },
    "cdp": {
      "nullable": true,
      "allOf": [
        {
          "$ref": "#/components/schemas/CdpInfo"
        }
      ]
    }
  }
}
```

## LibraryEntry

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "label": {
      "type": "string"
    },
    "platform": {
      "type": "string",
      "enum": [
        "Windows",
        "macOS",
        "Linux"
      ]
    },
    "chrome": {
      "type": "string",
      "description": "Строка версии Chrome."
    },
    "gpu": {
      "type": "string"
    },
    "builtin": {
      "type": "boolean",
      "description": "Входит в поставляемый стартовый набор."
    }
  }
}
```

## AddProxyRequest

```json
{
  "type": "object",
  "description": "Строка прокси ИЛИ явные поля (строка имеет приоритет).",
  "properties": {
    "proxy": {
      "type": "string",
      "description": "`scheme://user:pass@host:port` или `host:port:user:pass`."
    },
    "kind": {
      "type": "string",
      "enum": [
        "socks5",
        "http",
        "https"
      ]
    },
    "host": {
      "type": "string"
    },
    "port": {
      "type": "integer"
    },
    "username": {
      "type": "string"
    },
    "password": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "country": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    }
  }
}
```

## ProxySummary

```json
{
  "type": "object",
  "description": "Сохранённый прокси без учётных данных.",
  "properties": {
    "id": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "kind": {
      "type": "string",
      "enum": [
        "socks5",
        "http",
        "https"
      ]
    },
    "host": {
      "type": "string"
    },
    "port": {
      "type": "integer"
    },
    "country": {
      "type": "string",
      "description": "Информационный тег (например"
    }
  }
}
```

## Cookie

```json
{
  "type": "object",
  "description": "Cookie в удобном для инструментов виде (совместим с форматами\nEditThisCookie / Puppeteer; `httpOnly`/`sameSite` принимаются\nкак псевдонимы при импорте).\n",
  "required": [
    "domain",
    "name",
    "value"
  ],
  "properties": {
    "domain": {
      "type": "string",
      "description": "Хост cookie (Chromium `host_key`), например \".example.com\"."
    },
    "name": {
      "type": "string"
    },
    "value": {
      "type": "string"
    },
    "path": {
      "type": "string",
      "default": "/"
    },
    "expires": {
      "type": "number",
      "nullable": true,
      "description": "Unix-секунды; null/отсутствует = сессионный cookie."
    },
    "secure": {
      "type": "boolean",
      "default": false
    },
    "http_only": {
      "type": "boolean",
      "default": false
    },
    "same_site": {
      "type": "string",
      "enum": [
        "Strict",
        "Lax",
        "None",
        "unspecified"
      ],
      "description": "Регистронезависимо при импорте."
    }
  }
}
```

## ProxyEntry

```json
{
  "type": "object",
  "description": "Полная запись прокси (как встроено в `_meta.inline_proxy`).",
  "properties": {
    "id": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "kind": {
      "type": "string",
      "enum": [
        "socks5",
        "http",
        "https"
      ]
    },
    "host": {
      "type": "string"
    },
    "port": {
      "type": "integer"
    },
    "username": {
      "type": "string"
    },
    "password": {
      "type": "string"
    },
    "country": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    }
  }
}
```

## AutomationBranch

```json
{
  "description": "Ветка после выполнения шага. В простых случаях используется строка, при передаче значения - объект.",
  "oneOf": [
    {
      "type": "string",
      "enum": [
        "next",
        "stop",
        "endpass"
      ],
      "description": "`next`: перейти к следующему шагу. `stop`: завершить весь запуск профиля. `endpass`: завершить текущий проход и начать следующий с первого шага."
    },
    {
      "type": "object",
      "properties": {
        "goto": {
          "type": "string",
          "description": "Идентификатор блока для перехода. Если блока нет, используется `next`."
        }
      }
    },
    {
      "type": "object",
      "properties": {
        "retry": {
          "type": "integer",
          "description": "Сколько дополнительных раз повторить этот шаг, затем перейти к `next`."
        }
      }
    }
  ]
}
```

## AutomationBlock

```json
{
  "type": "object",
  "description": "Один шаг автоматизации. `kind` и `params` расширяемы, поэтому модули могут добавлять новые типы шагов.",
  "properties": {
    "id": {
      "type": "string"
    },
    "kind": {
      "type": "string",
      "description": "Встроенный тип палитры (`goto`, `click`, `type`, `profile.create`, `var.set`, ...) или `module:<module id>:<block>`.\n\nВ мобильном профиле обычные действия используют сенсорный ввод: `click` выполняет касание, `rightClick` - долгое нажатие, а `scroll` и `swipe` - жесты. `hover` недоступен, поскольку на мобильном устройстве нет указателя. Следующие шаги доступны только на мобильных профилях и отклоняются на компьютерах:\n\n| Тип | Параметры | Действие |\n| --- | --- | --- |\n| `touch.tap` | `selector` или `x`+`y`, `tapCount` (1-3), `timeout` | Коснуться цели. |\n| `touch.longPress` | `selector` или `x`+`y`, `timeout` | Нажать и удерживать. |\n| `touch.swipe` | `selector` или `x`+`y`, `dx`, `dy`, `timeout` | Провести пальцем. |\n| `touch.drag` | `selector`, `to`, `holdMs`, `timeout` | Перетащить элемент. |\n| `touch.pinch` | `scale`, `selector` или `x`+`y` | Масштабировать двумя пальцами. Значение выше 1 уменьшает масштаб. |\n| `screen.rotate` | `angle` (0/90/180/270), `turnMs`, `into` | Повернуть устройство. `into` сохраняет новый `screen.orientation.type`. |",
      "example": "click"
    },
    "label": {
      "type": "string"
    },
    "params": {
      "type": "object",
      "additionalProperties": true,
      "description": "Собственные поля шага, как их определяет палитра."
    },
    "enabled": {
      "type": "boolean",
      "default": true
    },
    "x": {
      "type": "number",
      "description": "Положение на холсте. Только для макета, выполнение идет по веткам."
    },
    "y": {
      "type": "number"
    },
    "on_done": {
      "$ref": "#/components/schemas/AutomationBranch"
    },
    "on_fail": {
      "allOf": [
        {
          "$ref": "#/components/schemas/AutomationBranch"
        }
      ],
      "description": "По умолчанию `stop`. Если продолжить после неудачного шага, действия могут выполняться не на той странице."
    },
    "secrets": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Имена параметров очищаются при экспорте."
    }
  }
}
```

## AutomationRunSettings

```json
{
  "type": "object",
  "properties": {
    "threads": {
      "type": "integer",
      "default": 1,
      "description": "Максимальное число одновременно работающих браузеров: от 1 до 64."
    },
    "loops": {
      "type": "integer",
      "default": 1,
      "description": "Число проходов по блокам. 0 означает запуск до истечения `hours`."
    },
    "hours": {
      "type": "number",
      "description": "Используется только при `loops: 0`."
    },
    "start": {
      "type": "string",
      "description": "Идентификатор блока, с которого нужно начать; пустой означает первый."
    },
    "profiles": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Для совместимости с проектами, сохраненными до появления профилей-блоков. При задании каждый профиль запускается в отдельном потоке."
    }
  }
}
```

## AutomationProject

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "notes": {
      "type": "string"
    },
    "blocks": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/AutomationBlock"
      }
    },
    "run": {
      "$ref": "#/components/schemas/AutomationRunSettings"
    },
    "created_at": {
      "type": "integer",
      "description": "Unix-секунды."
    },
    "updated_at": {
      "type": "integer",
      "description": "Unix-секунды."
    }
  }
}
```

## AutomationBundle

```json
{
  "type": "object",
  "description": "Пакет проекта для переноса на другую машину.",
  "properties": {
    "format": {
      "type": "integer",
      "example": 1,
      "description": "Импорт отклоняет неизвестную версию формата."
    },
    "exported_at": {
      "type": "integer",
      "description": "Unix-секунды."
    },
    "project": {
      "$ref": "#/components/schemas/AutomationProject"
    },
    "modules": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Идентификаторы модулей, необходимых проекту."
    },
    "module_files": {
      "type": "array",
      "description": "Файлы этих модулей переносятся внутри пакета, чтобы импортированный проект мог работать там, где он находится. Модуль, отсутствующий на диске во время экспорта, здесь не указан, но все равно назван в `modules`. При импорте модуль, уже установленный под тем же идентификатором, сохраняется как есть, а не перезаписывается.",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "wasm": {
            "type": "string",
            "description": "Файл .wasm, заархивированный gzip, а затем base64."
          }
        }
      }
    },
    "needs": {
      "type": "array",
      "description": "Поля, очищенные при экспорте и запрашиваемые снова при импорте.",
      "items": {
        "type": "object",
        "properties": {
          "block_id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "params": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
```

## AutomationWorker

```json
{
  "type": "object",
  "description": "Один браузер в рамках запуска.",
  "properties": {
    "profile_id": {
      "type": "string"
    },
    "profile_name": {
      "type": "string"
    },
    "pass": {
      "type": "integer",
      "description": "Нумерация с 1; значение 0 означает, что первый проход еще не начался."
    },
    "step": {
      "type": "integer"
    },
    "steps_total": {
      "type": "integer"
    },
    "status": {
      "type": "string",
      "enum": [
        "queued",
        "starting",
        "running",
        "done",
        "failed",
        "stopped"
      ]
    },
    "note": {
      "type": "string"
    }
  }
}
```

## AutomationRun

```json
{
  "type": "object",
  "properties": {
    "project_id": {
      "type": "string",
      "format": "uuid"
    },
    "project_name": {
      "type": "string"
    },
    "started_at": {
      "type": "integer",
      "description": "Unix-секунды."
    },
    "running": {
      "type": "boolean"
    },
    "workers": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/AutomationWorker"
      }
    },
    "log": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Последние записи, показанные в окне запусков. Полный журнал хранится в `automation-runs/`."
    }
  }
}
```

## AutomationModule

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Имя файла без расширения и префикс для типов блоков модуля."
    },
    "name": {
      "type": "string"
    },
    "path": {
      "type": "string"
    },
    "blocks": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": true
      },
      "description": "Дескрипторы блоков имеют ту же форму, что и встроенная палитра."
    },
    "error": {
      "type": "string",
      "description": "Пусто при успешной загрузке модуля, иначе содержит причину ошибки."
    }
  }
}
```
