IndexPackages

native-app-wrapper

Tauri scaffold that packages a website or a bundled CLI as a native desktop + mobile app, configured from a single JSON profile

Documentation

πŸ€– Agent skill β€” npx skills@latest add https://github.com/OpenSourceAGI/dev-tools-starter-agent --skill native-app-wrapper (what it covers)

A Tauri shell that turns one JSON profile into a native desktop (Windows/macOS/Linux) and mobile (Android/iOS) app. It packages one of two things:

  • A website ("mode": "remote") β€” a titled window that loads the site directly, plus the OS-level plumbing a plain webview doesn't get for free: a real app icon on every platform, a Google-OAuth-compatible login handoff, and a fullscreen toggle.
  • A command-line tool ("mode": "local") β€” a bundled HTML frontend backed by the CLI itself, shipped alongside the app binary as a Tauri sidecar, so the app runs with no Node, Python, or other runtime installed on the user's machine. See docs/LOCAL_APPS.md.

packages/about-system-info/native is a working example of the second kind β€” the about-system CLI shipped as an installable desktop app for all three desktop OSes.

What this is not

Not a place for app-specific UI or logic. In remote mode the wrapped site provides all of that; in local mode the app's own dist/ and its sidecar do. This package's job stops at: open a window, register the app's identity (icon, name, bundle id) with each OS, hand a browser-based OAuth login back to that window, and expose the bundled CLI's output to the bundled frontend. Anything more belongs in the app being wrapped.

Quick start

Scaffold a copy of the wrapper for your own app:

cd packages/native-app-wrapper
node bin/cli.js init ../../apps/my-app/native --profile-file ./my-app.json
cd ../../apps/my-app/native
npm install
npm run dev

init copies the wrapper's sources into the target directory, gives that copy your profile as its only identity, regenerates the Tauri config and Rust constants from it, renames the crate, and draws a placeholder icon set so the copy builds immediately. It also leaves out what the profile doesn't use: a desktop-only profile gets no android:build script, an offline CLI app gets no guide to browser OAuth handoffs. Nothing links back to this package afterward β€” the copy is a standalone app you can commit next to the thing it wraps, and updating it later means copying the changed scripts across, not re-running init over your own dist/.

To work on this package itself instead, npm run dev here opens a window on the untouched profiles/example.json (https://example.com), which is what the checked-in src-tauri/tauri.conf.json is generated from.

Layout

native-app-wrapper/
β”œβ”€β”€ bin/cli.js             # init / configure / icons
β”œβ”€β”€ profiles/              # one JSON file = one app identity (see profiles/README.md)
β”‚   └── example.json        #   the template, and this package's own default
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ profile.mjs                   # loads + validates a profile; one definition of the schema
β”‚   β”œβ”€β”€ configure.mjs                 # profile -> tauri.conf.json, generated_config.rs, remote.json
β”‚   β”œβ”€β”€ build-sidecar.mjs             # profile's sidecar.build -> src-tauri/binaries/<name>-<triple>
β”‚   β”œβ”€β”€ generate-icons.mjs            # profile's iconSource -> full src-tauri/icons/ set (Tauri CLI)
β”‚   β”œβ”€β”€ generate-placeholder-icons.mjs # draws a buildable icon set with no artwork and no deps
β”‚   └── lib/png.mjs                   # the PNG/ICO/ICNS writers that make the above possible
β”œβ”€β”€ src-tauri/
β”‚   β”œβ”€β”€ src/lib.rs          # window setup, deep-link OAuth handoff, sidecar bridge, fullscreen toggle
β”‚   β”œβ”€β”€ capabilities/       # Tauri's permission grants (default.json + a generated remote.json)
β”‚   β”œβ”€β”€ icons/              # generated; checked in so a fresh clone builds
β”‚   └── tauri.conf.json     # generated by configure
β”œβ”€β”€ assets/icon-source.png # the 1024px master `iconSource` points at by default
β”œβ”€β”€ dist/                  # the bundled frontend (a placeholder in remote mode; the app in local mode)
└── docs/
    β”œβ”€β”€ BUILDING.md         # local dev/build commands and release builds
    β”œβ”€β”€ LOCAL_APPS.md       # packaging a CLI: the sidecar bridge and the bundled frontend
    β”œβ”€β”€ OAUTH.md            # why login needs a system browser + deep link, and how it works here
    β”œβ”€β”€ MOBILE.md           # Android/iOS host requirements and gen/android, gen/apple
    └── APP_STORES.md       # Microsoft Store, Mac App Store, iOS App Store, Google Play

Why generated config instead of a hand-edited tauri.conf.json

An app's identity shows up in five places that must agree: the Tauri config, the Rust deep-link scheme, the Rust sidecar name, the remote capability's origin scope, and the icon set. Hand-editing means five chances to drift. scripts/configure.mjs writes all of them from the profile, so the profile is the only file anyone edits, and CI can assert the generated files still match it.

Why a window, not a bundled copy of a remote site

In remote mode, tauri.conf.json's main window points its url straight at the profile's site over HTTPS β€” there's no local copy of the app's UI to keep in sync with the real site (build.frontendDist still points at dist/, an unused placeholder Tauri's bundler requires to exist). The tradeoff: the app needs network access to be useful, same as opening the site in a browser tab would. Local mode is the opposite tradeoff β€” everything is bundled, nothing is fetched.


Source: packages/native-app-wrapper/README.md

Last updated on

On this page