Руководство по CLI
Облако из терминала
CLI BusinessStack (os) говорит с облаком БизнесОС теми же маршрутами, что и консоль. Всё, что делает кнопка, делает команда или curl.
Одни и те же маршруты
У облака нет отдельного CLI: кабинет обслуживает те же маршруты (/api/v1/cloud/*, /api/v1/marketplace/*), с которыми работает CLI BusinessStack, поэтому команды работают без изменений, как только OS_CLOUD_URL указывает сюда. Действия консоли шлют запросы на те же маршруты (ADR-0006) — поэтому поддержка может повторить любое действие из интерфейса в терминале.
Установка и адрес облака
npm i -g @objectstack/cli # устанавливает команду `os`
os --version # 18.x
export OS_CLOUD_URL=https://bos.virtuman.com # без этого CLI смотрит в другое облако — задавайте всегда
| Переменная | Смысл |
|---|---|
OS_CLOUD_URL | Адрес кабинета. Его же читает cloud-connection рантайма (окружения получают его от провижионера). |
OS_CLOUD_API_KEY / OS_TOKEN | Bearer для облачных маршрутов; перекрывает сохранённые учётные данные. Для CI. |
OS_CLOUD_TIMEOUT_MS | Таймаут HTTP для publish/install (0 — без ограничения). |
Вход
os cloud login # device flow: печатает адрес подтверждения и ждёт одобрения в браузере
os cloud login --email you@example.ru --password '…' # без диалога; аккаунты через соцсети/SSO — только через браузер
os cloud whoami
os cloud logout
Учётные данные хранятся в ~/.objectstack/cloud.json (для os package publish) и ~/.objectstack/credentials.json (активное окружение для os environments …). --force — войти заново, --no-browser — только напечатать адрес, --json — NDJSON для скриптов.
Всё, что вы делаете, ограничено аккаунтом (организацией), под которым вошли. Операторам сверх того доступны маршруты флота; клиенты видят только свои строки — та же стена, что и в консоли.
Окружения
os environments list
os environments show <id>
# Создать — те же правила тарифа, что и у кнопки в консоли
os environments create --name "Production" --org <accountId> --plan free
os environments create --name "Dev 1" --org <accountId> # второе и следующие — окружения разработки; нужен признак тарифа dev_environments
# Сделать окружение целью следующих команд (--no-remote — не вызывать /activate на сервере;
# у работающего окружения кабинет отвечает на /activate без действия)
os environments switch <id>
# Привязать локально собранное приложение, при необходимости собрав и пересеяв данные
os environments bind <id> --artifact ./dist/objectstack.json --build --reseed
Create отвечает 201 {environment, warnings, durationMs}; дальше окружение разворачивается 2–3 минуты. Отказы приходят как 403 с кодом: DEV_ENV_PLAN_LOCKED, DEV_ENV_LIMIT, PRODUCTION_ENV_LIMIT.
Действия через curl
CLI оборачивает create/list/show/switch/bind. Остальные действия — обычные маршруты; bearer тот, что лежит в ~/.objectstack/cloud.json.
T=$(jq -r .token ~/.objectstack/cloud.json)
B=https://bos.virtuman.com/api/v1/cloud
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/suspend
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/resume
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/archive
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/retry
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/rollback # на версию, где окружение последний раз работало
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/open # → { redirectUrl } подписанный вход в окружение
curl -s -X PATCH -H "Authorization: Bearer $T" $B/environments/<id> -d '{"display_name":"Продакшн"}' -H 'content-type: application/json'
curl -s -X DELETE -H "Authorization: Bearer $T" "$B/environments/<id>" # шаг 1: в архив, данные целы
curl -s -X DELETE -H "Authorization: Bearer $T" "$B/environments/<id>?purge=1" # шаг 2, у архивного: удалить — необратимо
# Домены и вход компании — по id строки
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/domains -d '{"hostname":"crm.example.ru"}' -H 'content-type: application/json'
curl -s -X POST -H "Authorization: Bearer $T" $B/domains/<domainId>/verify
curl -s -X POST -H "Authorization: Bearer $T" $B/sso/<connId>/verify
# Резервные копии: создать сейчас; восстановить в НОВОЕ окружение разработки
curl -s -X POST -H "Authorization: Bearer $T" $B/environments/<id>/backups
curl -s -X POST -H "Authorization: Bearer $T" $B/backups/<backupId>/restore -d '{"display_name":"Копия"}' -H 'content-type: application/json'
Каждый ответ — конверт { success, data | error: { code, message, details } }.
Удаление — в два шага: первый DELETE только архивирует (восстановить можно), необратимо удаляет второй, с ?purge=1, у уже архивного окружения. Продакшн-окружение без force=1 отвечает 409 PRODUCTION_FORCE_REQUIRED на обоих шагах; окружение, которое так и не развернулось (failed), удаляется сразу.
Запускать, останавливать, архивировать, удалять окружение, менять его настройки и домены может владелец или администратор аккаунта либо тот, кто окружение создал; остальным участникам — 403. Смотреть список и открывать окружение может любой участник.
Публикация приложения
cd my-app
os validate && os compile # → dist/objectstack.json
os package publish # создаёт/обновляет пакет в вашем аккаунте; новая версия
os package publish --submit # …и отправляет её на модерацию в каталог
os package publish --icon-url https://… --visibility org # значок ссылкой; org — во все окружения аккаунта
Маршруты за этим: POST /api/v1/cloud/packages, …/packages/:id/versions, …/packages/:id/icon. Действуют признак тарифа publish_apps и лимит max_private_apps; отказ называет их. Версии неизменяемы — повторная публикация той же версии отклоняется. Публикация не устанавливает: окружение забирает приложения само (следующий раздел). Значок — ссылкой (--icon-url); загрузка файла не поддерживается. Публикация изнутри окружения (действие «Опубликовать в реестр») создаёт те же строки версий.
Установка в окружение
Установка говорит с рантаймом, а не с кабинетом: входит в окружение и вызывает его /api/v1/marketplace/install-local, который берёт манифест из кабинета и выполняет installPackage внутри (ADR-0003).
os package install ru.example.crm \
--runtime https://<sub>.bos.virtuman.com --email owner@example.ru --password '…' --version latest
os package install ./dist/objectstack.json --runtime https://<sub>.bos.virtuman.com --email … --password …
# --confirm-global-uniques подтверждает уникальные ограничения на всю установку, которые объявляет приложение
Журнал установок в кабинете (sys_package_installation) пишет обратный вызов cloud-connection рантайма, поэтому вкладка «Установленные приложения» карточки окружения показывает и установки из CLI. Удаление приложения в окружении тоже доходит до кабинета: запись получает статус removed и уходит из «Установленных».
Работа внутри окружения
Окружение — стандартный рантайм BusinessStack. Любая команда, работающая с рантаймом, направляется на https://<sub>.bos.virtuman.com с учётной записью этого окружения:
os data query ru_example_crm.deal --where '{"status":"open"}' --runtime https://<sub>.bos.virtuman.com
os data import ./deals.csv --object ru_example_crm.deal --runtime …
# MCP для внешних AI-агентов (если в окружении включён «Доступ AI-агентов»)
POST https://<sub>.bos.virtuman.com/api/v1/mcp
Маршруты AI (/api/v1/ai/*) обслуживает @businessos/service-ai внутри окружения, расход считает шлюз платформы — ключ провайдера с вашей стороны не нужен.
В CI
# Задание GitLab CI в репозитории приложения: собрать и загрузить версию на каждый тег
# OS_CLOUD_API_KEY — личный ключ osk_ (POST /api/v1/keys), маскированная переменная CI
publish:
image: node:22
rules: [{ if: $CI_COMMIT_TAG }]
script:
- npm ci && npx os validate && npx os compile
- |
curl -sf -X POST "https://bos.virtuman.com/api/v1/artifacts/ru.example.crm/$CI_COMMIT_TAG" \
-H "Authorization: Bearer $OS_CLOUD_API_KEY" -H "content-type: application/json" \
--data-binary @dist/objectstack.json
Маршрут принимает личный ключ osk_ (оператор — в любой пакет, остальные — только в пакет своей организации) или токен самого окружения («Опубликовать в реестр»); общего UPLOAD_TOKEN больше нет. Загруженный артефакт становится опубликованным пре-релизом; оператор снимает is_pre_release в консоли, чтобы сделать его версией по умолчанию. Другой путь — OS_CLOUD_API_KEY и os package publish --submit: тот же результат клиентским маршрутом.
Скрипты оператора (репозиторий)
| Скрипт | Назначение |
|---|---|
node scripts/seed-catalog.mjs | Тарифы и упакованные приложения из artifacts/; идемпотентен |
node scripts/sso-connect.mjs | Зарегистрировать/проверить корпоративный IdP для окружения из терминала |
node scripts/pull-app.mjs | Вытащить приложение из любого окружения в JSON, пригодный для diff |
scripts/sync-skills.sh | Обновить .claude/skills из чекаута фреймворка |
# Чтения по всему парку (токен оператора)
curl -s -H "Authorization: Bearer $T" https://bos.virtuman.com/api/v1/cloud/fleet/overview
curl -s -H "Authorization: Bearer $T" https://bos.virtuman.com/api/v1/cloud/fleet/environments
curl -s -H "Authorization: Bearer $T" https://bos.virtuman.com/api/v1/cloud/fleet/sso
Коды ошибок, которые встретятся
| Код | Смысл |
|---|---|
DEV_ENV_PLAN_LOCKED / DEV_ENV_LIMIT / PRODUCTION_ENV_LIMIT | Ворота тарифа при создании окружения; в details — использовано/лимит |
ENVIRONMENT_NOT_ACTIVE | Действию нужно запущенное окружение |
DOMAIN_PLAN_LOCKED | Свои домены — с тарифа «Команда» |
SSO_NOT_PROVISIONED | Окружение старше секретов SSO — «Развернуть заново» |
PERMISSION_DENIED | Не ваше окружение, либо отказал признак тарифа (publish_apps, лимит приватных приложений) |
UNKNOWN_INSTANCE | Токен шлюза не соответствует ни одному окружению |
MODEL_UNSET (503) | Модель платформенного AI не задана в Setup |
ENVIRONMENT_FROZEN (423) | Окружение заморожено оператором — запуск, пересборка и смена адреса закрыты до разморозки |
SEAT_LIMIT | Все места тарифа заняты (приглашение или AI-место) |
BACKUP_IN_PROGRESS / BACKUP_NOT_CONFIGURED / BACKUP_UNSUPPORTED | Копия уже делается; хранилище копий не настроено; копии только для окружений в кластере (не docker) |
Полный реестр — в apps/control/src/fleet.ts.
Чего в CLI нет
| Пробел | Обход |
|---|---|
| Нет команд для suspend/archive/destroy/open, доменов, SSO, команды | curl по маршрутам выше; тонкая обёртка bos была бы небольшим дополнением |
Нет входа os cloud login для аккаунтов через соцсети/SSO без браузера | один раз пройти device flow; токен переиспользуется |
| Нет операторских команд (смена тарифа, модерация, повтор задания) | действия консоли или прямые маршруты (/api/v1/cloud/fleet/*, маршруты действий) |
| Нет команды резервного копирования/восстановления | curl по маршрутам …/environments/<id>/backups и …/backups/<id>/restore (выше); восстановление поверх существующего окружения — руководство оператора (kubectl + psql) |
| Выгрузка данных окружения целиком | os data по объектам; scripts/pull-app.mjs для метаданных |