pulsar
RU

CLI

pulsar <command>

  create <name>  создать новое приложение (--template default|js|qml|windows|tray)
  install        скачать нативный toolchain для этой платформы (--android для APK)
  update         обновить CLI до последнего релиза
  dev            запуск с горячей перезагрузкой (следит за исходниками)
  run            запустить приложение в текущем каталоге
  build          собрать один исполняемый файл (--target desktop|android|portable)
  doctor         проверить toolchain (host, bridge, Qt, Bun)
  version        показать версию
  help           показать справку

Полезные флаги и алиасы:

Команда Флаги
create <name> --template <t>, --dir <path>, --force
install --version <v>|latest, --slim, --android
build --out <dir> / -o <dir>, --slim, --target, --abi
run, dev env PULSAR_APP (каталог приложения), PULSAR_WATCH (dev)
init устаревший алиас create для текущего каталога

pulsar build пишет dist/<name> — один исполняемый файл со встроенным рантаймом Bun, вашим приложением и (если не --slim) рантаймом Qt. Запуск: ./dist/<name>.

Android

На Android QuickJS и QML-хост работают в одном процессе, поэтому код должен быть портативным: добавьте в pulsar.json "targets": ["android"] и "compat": "portable", затем:

pulsar build --target android

Первая сборка сама ставит в PULSAR_HOME всё нужное (один раз, ~2 ГБ): JDK 17, Android SDK/NDK и бандл Qt for Android с host-инструментами. Результат — dist/<name>.apk для arm64-v8a (minSdk 28, targetSdk 35). pulsar install --android делает ту же установку без сборки, а pulsar check --target android отсекает node:/bun: импорты до упаковки.

net.fetch и потоковый net.request работают на Android: транспорт выполняется в нативном хосте через QtNetwork. net.websocket требует модуль Qt WebSockets, которого нет в текущем бандле Qt for Android: на устройстве вызов вернёт ERR_UNSUPPORTED (на десктопе всё работает).

Dev-цикл на Android

pulsar dev --android

Первый запуск собирает dev-APK (applicationId <id>.dev, имя <name> (dev)) и ставит его рядом с production. CLI поднимает сервер бандла, прокидывает его на устройство через adb reverse, следит за каталогом приложения и перезапускает его после правки (1–2 с). Нативные входы (C++ хоста, манифест, CMake, id приложения) хешируются; при их изменении APK пересобирается и переустанавливается. Флаги: --device <serial>, --port <n>, --no-install, --force-install, --no-keep-awake.

Пока он запущен, из другого терминала можно смотреть и нажимать:

pulsar ui                 # дерево UI (JSON)
pulsar state              # снимок store (JSON)
pulsar tap "Добавить"     # нажатие по тексту, пути или id
pulsar type "молоко" --into "Новая задача"
pulsar expect "молоко"    # ждать текст
pulsar shot --out shot.png
pulsar tap --at 540,1200          # координаты (QML-приложения, системный UI)
pulsar swipe 500 1200 500 400 --duration 250
pulsar key back                   # back, home, enter, ... или числовой код
pulsar ui --system                # uiautomator-дамп переднего плана
pulsar eval "6*7"                 # выполнить JS в работающем QuickJS

pulsar shot снимает весь экран через adb; --window снимает только окно приложения по dev-каналу. Семантическим командам нужно дерево JS UI, поэтому они работают для JS-окон, а не для приложений с ui/main.qml. pulsar ui --system показывает дамп переднего окна (удобно для permission-диалогов), а --at/swipe/key работают по координатам.

Флоу-тесты могут использовать host API:

import { connect } from "../../cli/testing.ts"

const app = connect()

await app.tap({ text: "Добавить" })
await app.expect("1 задача")
await app.shot(".pulsar/shots/after.png")

Запуск: bun flows/todo.ts, пока работает pulsar dev --android.

Необработанная ошибка показывает полноэкранный overlay с кнопкой Reload. Пока запущен dev-сервер, стек маппится обратно в ваши .ts-файлы и в терминале, и в overlay.

Правки перезагружают приложение внутри процесса (~200 мс на устройстве): JS-рантайм пересоздаётся, а store заполняется состоянием прошлой сессии. JS-окна сохраняют свой view, QML-окна перезагружаются из ui/main.qml. Так как store сохраняется, изменение начального app.store.set в коде применится только после полного перезапуска (pulsar dev заново).