@bobfrankston/msgapidefs 0.1.36 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,8 +13,8 @@ TypeScript definitions for the **msgapi** JavaScript API used by msga, msger, an
13
13
  msgx (workspace root)
14
14
  ├── msgapidefs - TypeScript type definitions for window.msgapi (this package)
15
15
  ├── msgcommon - Shared CLI parsing, templates, pinning utilities
16
- ├── msger - Rust/wry webview host (Windows, Linux)
17
- ├── msgview - Electron webview host (Windows, Linux, Mac, Pi)
16
+ ├── msger - Rust/wry webview host (Windows, Linux, Mac, Pi) — PRIMARY
17
+ ├── msgview - Electron webview host (desktop) SEMI-RETIRED, see below
18
18
  ├── msga - .NET MAUI webview host (Android, Windows)
19
19
  ├── build - Release build orchestration script (build/build.ts)
20
20
  └── md - Shared documentation
@@ -61,14 +61,41 @@ window.close(); // Instead of msgapi.close() if not passing result
61
61
  | **Windows** | ✅ | ✅ | ✅ |
62
62
  | **Linux** | ✅ | ✅ | ❌ |
63
63
  | **macOS** | 🔧 wry supports it | ✅ | 🔧 Mac Catalyst |
64
- | **Raspberry Pi** | ⚠️ runs but no display | ✅ | ❌ |
64
+ | **Raspberry Pi** | | ✅ | ❌ |
65
65
  | **Android** | ❌ | ❌ | ✅ (primary) |
66
66
  | **iOS** | ❌ | ❌ | ❌ |
67
67
 
68
- > **msger** uses wry (Rust webview). Desktop platforms (Win/Linux/Mac) are mature. msger can cross-compile and run on the Pi but does not display properly (wry/GTK rendering issues) not a priority to fix.
69
- > **msgview** runs wherever Electron doesdesktop only, no mobile.
68
+ > **msger** uses wry (Rust webview) and is the **primary host**. Desktop platforms (Win/Linux/Mac) are mature. The Pi display bug ("runs but renders shredded scanlines") was WebKitGTK's DMABUF renderer on vc4/v3d — msger now sets `WEBKIT_DISABLE_DMABUF_RENDERER=1` automatically on Raspberry Pi hardware (both the JS launcher and the binary), and the Pi renders correctly as of 2026-07-31.
69
+ > **msgview** is **semi-retired** as of 2026-07-31 see below.
70
70
  > **msga** targets Android primarily; Windows for dev/testing. **iOS** would be the most realistic path — MAUI supports iOS natively, so msga could target it with an iOS build profile, Apple developer account, and platform-specific conditional code (`#if IOS`) for permissions and APIs. No current plans.
71
71
 
72
+ ### msgview is semi-retired (2026-07-31)
73
+
74
+ msger + msga cover every platform, so the Electron host has no platform to itself.
75
+ **Do not do feature-parity work on msgview.** New features land in msger (and msga
76
+ where it makes sense); msgview catches up only on demand, if a real need appears.
77
+
78
+ **As of 2026-08-01 msgview has no capability msger lacks.** The two things that
79
+ used to be Electron-only are both settled:
80
+
81
+ - **Unpacked Chrome extensions** (`-extension`) — not how add-ins are done here.
82
+ **`window.msgapi` (this package) plus injected init scripts is the add-in
83
+ mechanism**: `initScript` / `initScriptPath` inject app JS into every document
84
+ before page scripts run, with the full msgapi surface available — host-native,
85
+ works in msger and msga, no Chromium required. If something genuinely needs
86
+ Chrome extension APIs, run it in Chrome.
87
+ - **`-render` off Windows** — ported. msger now captures on Linux/Pi through
88
+ WebKitGTK's snapshot API (see the render row above), verified on a Pi 5 for both
89
+ PNG and JPEG output.
90
+
91
+ Engine currency is *not* a reason: a feature probe on Pi 5 / WebKitGTK 2.50.6
92
+ (2026-07-31) passed 23 of 24 modern checks — `:has()`, CSS nesting, container
93
+ queries, subgrid, `backdrop-filter`, `color-mix`, `oklch`, Popover, View
94
+ Transitions, `Object.groupBy`, `Promise.withResolvers`, `Intl.Segmenter`, WebGL2,
95
+ OffscreenCanvas — failing only the RegExp `v` flag. The WebKitGTK user-agent token
96
+ (`Version/60.5`) is a WebKitGTK version string, not a Safari generation; it says
97
+ nothing about capability.
98
+
72
99
  ## Implementation Status
73
100
 
74
101
  ✅ = Implemented | ⚠️ = Partial | ❌ = Not implemented
@@ -79,6 +106,8 @@ window.close(); // Instead of msgapi.close() if not passing result
79
106
  | `version` | ❌ | ❌ | ✅ | `window.msgapi.version` string |
80
107
  | `setLogging(value?)` | ❌ | ❌ | ✅ | Returns previous state |
81
108
  | `setAutoUpdate(value?)` | ❌ | ❌ | ✅ | `'auto'`/`'check'`/`'off'`; msga Android only |
109
+ | **Diagnostics** | | | | **`window.msgapi.diag.*`** |
110
+ | `diag.getExitReasons(max?)` | ❌ | ❌ | ✅ | Why the previous run exited; msga Android 11+ only, `[]` elsewhere |
82
111
  | **Window Control** | | | | **`window.msgapi.*`** |
83
112
  | `toggleFullscreen()` | ❌ | ✅ | ✅ | msger: via `window.msgapi` (msger-api.js) |
84
113
  | `setFullscreen(bool)` | ✅ | ✅ | ✅ | |
@@ -123,9 +152,9 @@ window.close(); // Instead of msgapi.close() if not passing result
123
152
  | `http.fetch()` | ✅ | ✅ | ✅ | Native HTTP — bypasses CORS/mixed-content |
124
153
  | **Host Launcher Features** | | | | *Not part of `window.msgapi`; CLI / launcher-side* |
125
154
  | Markdown rendering (`.md`/`.markdown` auto-render) | ✅ | ✅ | ❌ | Local `file://` URL → rendered HTML via `msgcommon/markdown` (marked + light/dark CSS). msga could match it in `MainPage.xaml.cs` URL interception — not done yet. |
126
- | Unpacked Chrome extension loading (`-extension <dir>`) | ✅ | ❌ | ❌ | msgview-only via `session.extensions.loadExtension`. wry has no public extension API; WebKitGTK has none at all. msga would need a separate Android/WebView2 plumbing not planned. |
155
+ | Unpacked Chrome extension loading (`-extension <dir>`) | ✅ | ❌ | ❌ | msgview-only via `session.extensions.loadExtension`. wry has no public extension API; WebKitGTK has none at all. **Not the add-in mechanism here** — app add-ins are `window.msgapi` + injected init scripts (`initScript` / `initScriptPath`), which work in msger and msga without Chromium. Chrome-extension-specific needs go to Chrome. |
127
156
  | Headless mode (`-noshow` without `-save`) | ✅ | ✅ | ❌ | msger creates the WebView invisible (`with_visible(false)`) but still loads HTML and runs scripts — an embedded `<script>` can query the engine and `window.ipc.postMessage` a result back. Combined with `-result <fieldname>` for clean stdout, this lets msger act as a headless CSS/JS resolver (`contrast-color()`, `getComputedStyle()`, `CSS.supports()`). msgview matches with `BrowserWindow({show:false})` + `backgroundThrottling:false`. |
128
- | Render to bitmap (`-render [file]`, `render` option) | ✅ | ✅ | ❌ | Headless page→image capture: the window is never shown; the page loads, settles (`renderDelay`, default 100ms after `load` + two rAFs), is screenshotted, and the host exits. `-render <file>` writes the image (format from extension: `.png`/`.jpg`/`.bmp`) and the JSON result carries `render: {path, width, height, format}`; bare `-render` (or API `render: true`) returns it as `render: {data (base64), width, height, format}`. Library API: `showMessageBox({render: "shot.png" \| true, renderDelay?, renderFormat?})`; a failed capture rejects (msger) or sets `renderError` in the result. Both hosts fall back to capturing whatever rendered if the page hasn't loaded within the timeout (default 30s in render mode). msger: WebView2 DevTools `Page.captureScreenshot` **Windows only** for now; msgview: `webContents.capturePage()`all Electron platforms. Width/height are physical pixels (include DPI scale). |
157
+ | Render to bitmap (`-render [file]`, `render` option) | ✅ | ✅ | ❌ | Headless page→image capture: the window is never shown; the page loads, settles (`renderDelay`, default 100ms after `load` + two rAFs), is screenshotted, and the host exits. `-render <file>` writes the image (format from extension: `.png`/`.jpg`/`.bmp`) and the JSON result carries `render: {path, width, height, format}`; bare `-render` (or API `render: true`) returns it as `render: {data (base64), width, height, format}`. Library API: `showMessageBox({render: "shot.png" \| true, renderDelay?, renderFormat?})`; a failed capture rejects (msger) or sets `renderError` in the result. Both hosts fall back to capturing whatever rendered if the page hasn't loaded within the timeout (default 30s in render mode). msger: WebView2 DevTools `Page.captureScreenshot` on Windows, `webkit_web_view_get_snapshot()` cairo PNG on Linux/Pi (2026-08-01); msgview: `webContents.capturePage()`. Both msger paths produce the same base64 PNG, so format transcoding and file writing are shared. **Linux caveat:** WebKitGTK cannot snapshot a window that was never mapped, so on Linux the render window IS mapped made invisible by going fully transparent and skipping the taskbar, since Wayland has no way to park a window off-screen. Width/height are physical pixels (include DPI scale) and cover the WebView viewport, not the window frame. |
129
158
 
130
159
  > **msger** now injects `window.msgapi` via `msger-api.js` with window control, UDP, and HTTP. File system and shell are not yet implemented in msger or msgview.
131
160
  >
@@ -155,6 +184,35 @@ See [`msgapi-plan.md`](./msgapi-plan.md) for the full implementation plan and ro
155
184
  - Default: `'auto'` (configurable via `AUTO_UPDATE` in msga.env)
156
185
  - Checks on startup then every `UPDATE_INTERVAL_MINUTES` (default 60)
157
186
 
187
+ ### Diagnostics
188
+
189
+ Why did the previous run exit? Android records the last few process deaths (`ApplicationExitInfo`, Android 11+); the host reads its own history on the next run and exposes it to the web app. This distinguishes crash / ANR / low-memory kill / cached-app freezer — no adb needed. Currently msga Android only; other hosts/platforms return `[]`.
190
+
191
+ - `diag.getExitReasons(max?)` - Previous-run exit records, newest first (default/max clamp: 5/16). Returns `MsgExitInfo[]`:
192
+ - `timestamp` - Exit time, ms since epoch
193
+ - `reason` - `"LowMemory"`, `"Crash"`, `"CrashNative"`, `"Anr"`, `"Freezer"`, `"ExitSelf"`, `"Signaled"`, `"UserRequested"`, `"UserStopped"`, `"DependencyDied"`, `"ExcessiveResourceUsage"`, `"PackageUpdated"`, `"Other"`, ... (Android `ApplicationExitInfo` reason names)
194
+ - `description` - OS-provided detail (may be null), e.g. `"isolated not needed"` = renderer cleanup
195
+ - `importance` - Process state at death (e.g. `"Foreground"`, `"Cached"`) — a `Cached` + `LowMemory` kill is normal Android memory reclaim, not a bug
196
+ - `pss` / `rss` - Memory at exit, KB
197
+ - `processName` - Which process died: the main package name, or `...:sandboxed_process*` = the WebView renderer. **Records cover every process in the package** — renderer deaths are routine cleanup, not the app dying; filter by processName to find real main-process exits
198
+ - `pid` - Process ID at exit
199
+ - `status` - Signal number for `Signaled`, exit code for `ExitSelf`, 0 otherwise
200
+ - `stateSummary` - App-set marker (`ActivityManager.setProcessStateSummary`); msga stamps `start=<ISO time> v=<version>` at each launch so uptime-at-death is computable
201
+ - `trace` - First ~4KB of the ANR/native-crash trace when the OS captured one (`Anr`/`CrashNative` only)
202
+
203
+ ```javascript
204
+ // Exit-history tool in a web app (e.g. houser debug panel)
205
+ const exits = await window.msgapi?.diag?.getExitReasons() ?? [];
206
+ for (const e of exits) {
207
+ console.log(`${new Date(e.timestamp).toLocaleString()} ${e.reason}` +
208
+ ` (${e.importance ?? '?'}, pss=${e.pss ?? '?'}KB) ${e.description ?? ''}`);
209
+ }
210
+ ```
211
+
212
+ Interpreting common reasons: `LowMemory` = killed by the low-memory killer; `Anr` = main thread hung; `Crash`/`CrashNative` = unhandled exception / native crash; `Freezer` = app was frozen (Android 12+ cached-app freezing — UDP listeners silently suspended, app not actually dead); `UserRequested` = swiped away from recents; `PackageUpdated` = replaced by an APK update; `Signaled` = killed by signal (check `description`).
213
+
214
+ msga also logs new exit records via MsgALog at startup when `LOGGING=true` in msga.env (`Previous exit: ...`), deduplicated across runs via a `lastSeenExitTimestamp` preference. See [msga/exit-diagnostics.md](../msga/exit-diagnostics.md) for the full design notes.
215
+
158
216
  ### Window Control
159
217
 
160
218
  - `toggleFullscreen()` - Toggle fullscreen mode
package/msgapi-plan.md CHANGED
@@ -62,6 +62,7 @@ Shared planning document for msgapi across implementations:
62
62
  | setAutoUpdate(value?) | ❌ | ❌ | ✅ | 'auto'/'check'/'off'; defaults to 'off' (web app owns policy) |
63
63
  | installUpdate(url) | ❌ | ❌ | ✅ | Download + install APK; Android only. Web app calls this. |
64
64
  | getNetworkInfo() | ❌ | ❌ | ✅ | Fresh local IP + subnet mask; used by httpudp-client isAddressReachable() |
65
+ | diag.getExitReasons(max?) | ❌ | ❌ | ✅ | Why the previous run exited (Android 11+ ApplicationExitInfo); also MsgALog "Previous exit:" at startup under LOGGING. See [msga/exit-diagnostics.md](../msga/exit-diagnostics.md). Added 2026-07-09 (v1.5.40). |
65
66
  | info | ❌ | ❌ | ✅ | `MsgHostInfo`: host, version, platform, updateUrl |
66
67
  | updateUrl | ❌ | ❌ | ✅ | URL to versions.json — install source for update checks |
67
68
  | version | ❌ | ❌ | ✅ | `window.msgapi.version` = app version string |
package/msgapidefs.ts CHANGED
@@ -65,6 +65,24 @@ export interface MsgApkInfo { // One APK entry in versions.json
65
65
  default?: boolean; // True if this is the default APK
66
66
  }
67
67
 
68
+ // Why the previous app process died — from Android ApplicationExitInfo (OS-recorded, survives process death)
69
+ // NOTE: records cover EVERY process in the package, including the sandboxed WebView renderer
70
+ // (processName contains ":sandboxed_process") — renderer deaths like "isolated not needed" are
71
+ // routine cleanup, not the app dying. Filter/dim by processName to find real main-process exits.
72
+ export interface MsgExitInfo {
73
+ timestamp: number; // Exit time, ms since epoch
74
+ reason: string; // "LowMemory" | "Crash" | "CrashNative" | "Anr" | "Freezer" | "ExitSelf" | "Signaled" | "UserRequested" | "UserStopped" | "DependencyDied" | "InitializationFailure" | "PermissionChange" | "ExcessiveResourceUsage" | "PackageStateChange" | "PackageUpdated" | "Other" | "Unknown"
75
+ description?: string; // OS-provided detail (may be null/absent), e.g. "isolated not needed" for renderer cleanup
76
+ importance?: string; // Process state at death (e.g. "Foreground", "Cached") — cached kills are normal memory reclaim
77
+ pss?: number; // Proportional set size at exit, KB
78
+ rss?: number; // Resident set size at exit, KB
79
+ processName?: string; // Which process died — main package name, or ":sandboxed_process..." = WebView renderer
80
+ pid?: number; // Process ID at exit
81
+ status?: number; // Exit status: signal number for "Signaled", exit code for "ExitSelf", 0 otherwise
82
+ stateSummary?: string; // App-set marker (msga stamps "start=<ISO time> v=<version>" each run → compute uptime at death)
83
+ trace?: string; // First ~4KB of ANR/native-crash trace when the OS captured one (reason "Anr"/"CrashNative")
84
+ }
85
+
68
86
  export interface MsgVersionInfo { // Structure of versions.json
69
87
  version: string; // Latest available version (e.g. "1.5.21") — semver string, compare numerically not lexicographically
70
88
  buildDate: string; // Build timestamp (ISO 8601)
@@ -85,6 +103,14 @@ export interface MsgAPI {
85
103
  installUpdate?(url: string): Promise<void>; // Download + install APK from url. msga Android only; web app owns update policy.
86
104
  getNetworkInfo?(): Promise<{ localIp: string; localMask: string }>; // Current device IP + subnet mask (fresh, not cached)
87
105
 
106
+ // ── Diagnostics ─────────────────────────────────────────────
107
+ // Why did the previous run exit? OS records the last few process deaths;
108
+ // read them on the next run to distinguish crash / ANR / low-memory kill / freezer.
109
+
110
+ diag?: {
111
+ getExitReasons(max?: number): Promise<MsgExitInfo[]>; // Previous-run exit records, newest first (default max 5). msga Android 11+ only; [] elsewhere.
112
+ };
113
+
88
114
  // ── Window Control ──────────────────────────────────────────
89
115
 
90
116
  toggleFullscreen(): void; // Toggle fullscreen mode on/off
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/msgapidefs",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "description": "TypeScript definitions for msgapi JavaScript API (msgview/msger)",
5
5
  "type": "module",
6
6
  "main": "./msgapidefs.js",
@@ -33,6 +33,7 @@
33
33
  "prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
34
34
  "preversion": "git add -A",
35
35
  "release": "npm run prerelease:local && npm version patch && npm publish --quiet",
36
+ "build": "tsc",
36
37
  "postversion": "git push && git push --tags"
37
38
  },
38
39
  "devDependencies": {
package/tsconfig.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
- "compilerOptions": {
2
+ "compilerOptions": {
3
+ "types": ["node"],
3
4
  "target": "ES2020",
4
5
  "module": "ES2020",
5
6
  "lib": ["ES2020", "DOM"],