node-app-build 6.4.0

Mini app developer CLI: scaffold, validate, package node-app-* Debian packages
# AGENTS.md — coding agent onboarding for {{name}}

> **You are a coding agent working in `node-app-{{name}}`.**
> This app was scaffolded by `node-app new --profile bun-fullstack`. It is a
> **platform-loaded Bun app with an embedded UI bundle** — the platform's
> `BunLoader` owns the backend lifecycle, and the React+Vite UI in `ui/`
> is mounted same-origin inside an iframe at
> `/api/v2/node-apps/{{name}}/ui/`.

## What this means for you

- **No `Bun.serve()`, no own HTTP listener.** Inbound capability invocations
  arrive via Unix Socket IPC through `@econ-v1/app-sdk`'s `runNodeApp(...)`.
- **No own systemd unit.** The platform starts/stops this app.
- **Eligible for the shared Bun runtime.** When enabled per-app at install
  time (`shared_runtime_enabled: true`), the platform runs this app as a
  `worker_threads.Worker` inside a shared `bun-runtime-host` process instead
  of its own `bun run` child. The SDK's module-globals are realm-isolated
  per-Worker so this is transparent to your code.
- **UI is served by the platform, not by you.** Build with `bun --cwd ui build`,
  ship `ui/dist/` via the .deb, and the platform's reverse-proxy mounts it
  at `/api/v2/node-apps/{{name}}/ui/`. Same-origin → no CORS, no token
  flapping.
- **Shared deps externalized at build time.** `@anthropic-ai/sdk`, `openai`,
  `@google/genai`, `@mistralai/mistralai`, `zod` live in
  `/usr/lib/node/shared/node_modules` and are resolved at runtime via
  `NODE_PATH`. Your bundled `dist/index.js` inlines everything else.

## First files to open

| File | Why |
|---|---|
| `src/index.ts` | the capability handler |
| `ui/src/App.tsx` | iframe handshake (postMessage `ready``auth_token`) |
| `manifest.json` | declare your `capabilities.requires` / `capabilities.provides`; `has_ui: true`, `ui_path: "ui/dist"` |
| `package.json` | add deps you need (keep `@econ-v1/app-sdk` pinned) |
| `ui/package.json` | UI deps (React 19 + Vite 6 pre-installed) |
| `node-app.toml` | per-app dev config |

## UI ↔ backend handshake

The iframe is same-origin. The handshake is:

1. UI mounts → posts `{ type: 'ready' }` to `window.parent`.
2. Platform replies with `{ type: 'auth_token', token, expires_at }`.
3. UI uses the token as `Authorization: Bearer ${token}` for any backend
   fetches (`/api/v2/node-apps/{{name}}/api/...`).
4. Platform refreshes by posting another `auth_token` before expiry.

The default `ui/src/App.tsx` wires this up — copy the pattern when adding
your own routes.

## Useful commands

```bash
node-app audit            # check this app against the blueprint
node-app dev              # hot-reload dev loop against a local monorepo
node-app build            # bundle dist/index.js + ui/dist (phase 3 of #868)
node-app package          # build .deb (phase 3 of #868)
```

## Don'ts

- Don't add `bun build --compile` — incompatible with the shared runtime.
- Don't bundle wholesale `node_modules/` into the .deb; rely on
  `SHARED_EXTERNALS` resolution + manifest `nodeApp.privateRuntime`.
- Don't change `app_type` to `"standalone"` unless this app legitimately
  needs its own systemd unit (LCD daemons, OTA controllers, etc.).
- Don't fetch cross-origin from the UI. Backend requests must use the
  same-origin `/api/v2/node-apps/{{name}}/api/...` path; the platform's
  reverse-proxy handles the rest.