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 заново).