@bobfrankston/msgapidefs 0.1.30 → 0.1.31
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 +107 -32
- package/msgapi-plan.md +58 -36
- package/msgapidefs.ts +34 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
# @bobfrankston/msgapidefs
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
> **⚠️ SECURITY:** msgapi gives full native OS access (filesystem, processes, networking). Intended ONLY for trusted apps — native apps written as web pages. Do not expose to untrusted content.
|
|
4
|
+
|
|
5
|
+
> **⚠️ EXPERIMENTAL:** APIs are experimental and subject to change.
|
|
6
|
+
|
|
7
|
+
TypeScript definitions for the **msgapi** JavaScript API used by msga, msger, and msgview. See the Typescript definitions in
|
|
8
|
+
[msgapidefs.ts](msgapidefs.ts) for more info
|
|
9
|
+
|
|
10
|
+
## Workspace Structure
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
msgx (workspace root)
|
|
14
|
+
├── msgapidefs - TypeScript type definitions for window.msgapi (this package)
|
|
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)
|
|
18
|
+
├── msga - .NET MAUI webview host (Android, Windows)
|
|
19
|
+
├── build - Release build orchestration script (build/build.ts)
|
|
20
|
+
└── md - Shared documentation
|
|
21
|
+
```
|
|
5
22
|
|
|
6
23
|
## Installation
|
|
7
24
|
|
|
@@ -35,6 +52,23 @@ localStorage.setItem('key', 'value'); // Instead of msgapi.saveData()
|
|
|
35
52
|
window.close(); // Instead of msgapi.close() if not passing result
|
|
36
53
|
```
|
|
37
54
|
|
|
55
|
+
## Platform Support
|
|
56
|
+
|
|
57
|
+
✅ = Current | 🔧 = Possible (recompile/rebuild needed) | ❌ = Not feasible
|
|
58
|
+
|
|
59
|
+
| Platform | msger (Rust/wry) | msgview (Electron) | msga (.NET MAUI) |
|
|
60
|
+
|----------|:----------------:|:------------------:|:----------------:|
|
|
61
|
+
| **Windows** | ✅ | ✅ | ✅ |
|
|
62
|
+
| **Linux** | ✅ | ✅ | ❌ |
|
|
63
|
+
| **macOS** | 🔧 wry supports it | ✅ | 🔧 Mac Catalyst |
|
|
64
|
+
| **Raspberry Pi** | ⚠️ runs but no display | ✅ | ❌ |
|
|
65
|
+
| **Android** | ❌ | ❌ | ✅ (primary) |
|
|
66
|
+
| **iOS** | ❌ | ❌ | ❌ |
|
|
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 does — desktop only, no mobile.
|
|
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
|
+
|
|
38
72
|
## Implementation Status
|
|
39
73
|
|
|
40
74
|
✅ = Implemented | ⚠️ = Partial | ❌ = Not implemented
|
|
@@ -46,33 +80,41 @@ window.close(); // Instead of msgapi.close() if not passing result
|
|
|
46
80
|
| `setLogging(value?)` | ❌ | ❌ | ✅ | Returns previous state |
|
|
47
81
|
| `setAutoUpdate(value?)` | ❌ | ❌ | ✅ | `'auto'`/`'check'`/`'off'`; msga Android only |
|
|
48
82
|
| **Window Control** | | | | **`window.msgapi.*`** |
|
|
49
|
-
| `toggleFullscreen()` | ❌ |
|
|
50
|
-
| `setFullscreen(bool)` | ✅ |
|
|
51
|
-
| `minimize()` | ✅ |
|
|
52
|
-
| `maximize()` | ✅ |
|
|
53
|
-
| `setSize(w, h)` | ✅ |
|
|
54
|
-
| `setPosition(x, y)` | ✅ |
|
|
55
|
-
| `setAlwaysOnTop(bool)` | ✅ |
|
|
56
|
-
| `close(result?)` | ✅ | ✅ | ✅ |
|
|
83
|
+
| `toggleFullscreen()` | ❌ | ✅ | ✅ | msger: via `window.msgapi` (msger-api.js) |
|
|
84
|
+
| `setFullscreen(bool)` | ✅ | ✅ | ✅ | |
|
|
85
|
+
| `minimize()` | ✅ | ✅ | ✅ | msga: Windows only |
|
|
86
|
+
| `maximize()` | ✅ | ✅ | ✅ | msga: Windows only |
|
|
87
|
+
| `setSize(w, h)` | ✅ | ✅ | ✅ | msga: Windows only |
|
|
88
|
+
| `setPosition(x, y)` | ✅ | ✅ | ✅ | msga: Windows only |
|
|
89
|
+
| `setAlwaysOnTop(bool)` | ✅ | ✅ | ❌ | |
|
|
90
|
+
| `close(result?)` | ✅ | ✅ | ✅ | |
|
|
57
91
|
| `navigate(url)` | ❌ | ❌ | ✅ | msga only |
|
|
58
92
|
| `reload()` | ❌ | ❌ | ✅ | msga only |
|
|
59
93
|
| **File System** | | | | **`window.msgapi.fs.*`** |
|
|
60
|
-
| `fs.selectFile()` |
|
|
61
|
-
| `fs.selectFiles()` |
|
|
62
|
-
| `fs.saveFileAs()` |
|
|
63
|
-
| `fs.selectFolder()` |
|
|
64
|
-
| `fs.read(path)` |
|
|
65
|
-
| `fs.
|
|
66
|
-
| `fs.
|
|
67
|
-
| `fs.
|
|
68
|
-
| `fs.
|
|
94
|
+
| `fs.selectFile()` | ❌ | ❌ | ✅ | msga: full JS bridge wired |
|
|
95
|
+
| `fs.selectFiles()` | ❌ | ❌ | ✅ | msga: full JS bridge wired |
|
|
96
|
+
| `fs.saveFileAs()` | ❌ | ❌ | ✅ | msga: Windows only |
|
|
97
|
+
| `fs.selectFolder()` | ❌ | ❌ | ✅ | msga: Windows only |
|
|
98
|
+
| `fs.read(path)` | ❌ | ❌ | ✅ | msga: binary→base64→TextDecoder |
|
|
99
|
+
| `fs.readAsDataUrl(path)` | ❌ | ❌ | ✅ | msga: auto-detects MIME from extension |
|
|
100
|
+
| `fs.write(path, content)` | ❌ | ❌ | ✅ | |
|
|
101
|
+
| `fs.list(path)` | ❌ | ❌ | ✅ | msga: includes attributes/created on Windows |
|
|
102
|
+
| `fs.exists(path)` | ❌ | ❌ | ✅ | |
|
|
103
|
+
| `fs.delete(path)` | ❌ | ❌ | ✅ | |
|
|
104
|
+
| **Shell** | | | | **`window.msgapi.shell.*`** |
|
|
105
|
+
| `shell.exec()` | ❌ | ❌ | ✅ | Process with stdout/stderr; limited on non-rooted Android |
|
|
106
|
+
| `shell.open()` | ❌ | ❌ | ✅ | Android: Intent.ACTION_VIEW; Windows: ShellExecute |
|
|
107
|
+
| `shell.showInFolder()` | ❌ | ❌ | ❌ | Defined in interface, not yet implemented |
|
|
108
|
+
| `shell.trash()` | ❌ | ❌ | ❌ | Defined in interface, not yet implemented |
|
|
69
109
|
| **UDP Networking** | | | | **`window.msgapi.udp.*`** |
|
|
70
|
-
| `udp.send()` |
|
|
71
|
-
| `udp.listen()` |
|
|
72
|
-
| `udp.sendReceive()` |
|
|
73
|
-
| `udp.broadcast()` |
|
|
110
|
+
| `udp.send()` | ✅ | ✅ | ✅ | Node dgram / Rust UdpSocket / C# UdpClient |
|
|
111
|
+
| `udp.listen()` | ✅ | ✅ | ✅ | Background receive loop in all hosts |
|
|
112
|
+
| `udp.sendReceive()` | ✅ | ✅ | ✅ | Send + wait with timeout |
|
|
113
|
+
| `udp.broadcast()` | ✅ | ✅ | ✅ | 255.255.255.255 broadcast |
|
|
114
|
+
| **HTTP Fetch** | | | | **`window.msgapi.http.*`** |
|
|
115
|
+
| `http.fetch()` | ✅ | ✅ | ✅ | Native HTTP — bypasses CORS/mixed-content |
|
|
74
116
|
|
|
75
|
-
> **msger
|
|
117
|
+
> **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.
|
|
76
118
|
|
|
77
119
|
[httpudp-client](../../homecontrol/utils/udp/httpudp-client/README.md) auto-detects `window.msgapi?.udp` and uses native UDP when available, falling back to the httpudp WebSocket proxy otherwise.
|
|
78
120
|
|
|
@@ -117,14 +159,15 @@ File dialogs (user picks interactively — safe without special permissions):
|
|
|
117
159
|
- `fs.saveFileAs(content, filename?, options?)` - Save file dialog
|
|
118
160
|
- `fs.selectFolder(options?)` - Folder picker
|
|
119
161
|
|
|
120
|
-
Direct path operations (give the webview full filesystem access):
|
|
121
|
-
- `fs.read(path)` - Read file
|
|
162
|
+
Direct path operations (give the webview full filesystem access — msga only currently):
|
|
163
|
+
- `fs.read(path, options?)` - Read file. Options: `{ encoding: 'utf8' | 'base64' | 'binary' }`. Default `'utf8'` returns string; `'base64'` returns raw base64 string (efficient for images); `'binary'` returns `Uint8Array`.
|
|
164
|
+
- `fs.readAsDataUrl(path, mimeType?)` - Read file as data URL (`data:image/jpeg;base64,...`). Auto-detects MIME from extension if omitted.
|
|
122
165
|
- `fs.write(path, content)` - Write file
|
|
123
|
-
- `fs.list(path)` - List directory
|
|
124
|
-
- `fs.exists(path)` - Check if file exists
|
|
125
|
-
- `fs.delete(path)` - Delete file
|
|
166
|
+
- `fs.list(path)` - List directory. Returns `FileInfo[]` with `attributes` (Win32 flags) and `created` on Windows.
|
|
167
|
+
- `fs.exists(path)` - Check if file/directory exists
|
|
168
|
+
- `fs.delete(path)` - Delete file or directory
|
|
126
169
|
|
|
127
|
-
>
|
|
170
|
+
> **Note:** File system operations are currently implemented only in msga. msger and msgview do not yet have `window.msgapi.fs` — these hosts have their own internal fs mechanisms but have not wired them to the msgapi interface.
|
|
128
171
|
|
|
129
172
|
### UDP Networking
|
|
130
173
|
|
|
@@ -135,6 +178,38 @@ Native UDP socket access — no WebSocket proxy needed when running inside a msg
|
|
|
135
178
|
- `udp.sendReceive(host, port, data, timeoutMs?)` - Send and wait for response
|
|
136
179
|
- `udp.broadcast(port, data)` - Broadcast to local network
|
|
137
180
|
|
|
181
|
+
### Shell / Process Execution
|
|
182
|
+
|
|
183
|
+
Native process execution — run commands, open files with default handler. msga only currently.
|
|
184
|
+
|
|
185
|
+
- `shell.exec(command, args?, options?)` - Run external command and capture output. Returns `{ stdout, stderr, exitCode }`. Options: `cwd`, `timeout` (ms), `stdin`, `encoding` (`'utf8'`|`'base64'`). Limited on non-rooted Android.
|
|
186
|
+
- `shell.open(path)` - Open file/URL with system default handler (Windows: ShellExecute, Android: Intent.ACTION_VIEW)
|
|
187
|
+
- `shell.showInFolder(path)` - Select file in Explorer / file manager (not yet implemented)
|
|
188
|
+
- `shell.trash(path)` - Move to Recycle Bin / Trash (not yet implemented)
|
|
189
|
+
|
|
190
|
+
### HTTP Fetch
|
|
191
|
+
|
|
192
|
+
Native HTTP client — bypasses browser CORS and mixed-content restrictions. Use when an HTTPS-served web app needs to reach HTTP endpoints (e.g., local devices on the LAN).
|
|
193
|
+
|
|
194
|
+
- `http.fetch(url, init?)` - Drop-in replacement for browser `fetch()`. Same `RequestInit` params, returns a real `Response` object (`.ok`, `.status`, `.json()`, `.text()`, `.headers`, `.clone()`, etc.)
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// HTTP from an HTTPS page — blocked by browsers, works via msgapi
|
|
198
|
+
const r = await window.msgapi.http.fetch('http://192.168.1.5/api/status');
|
|
199
|
+
const data = await r.json();
|
|
200
|
+
|
|
201
|
+
// POST with headers and body
|
|
202
|
+
const r2 = await window.msgapi.http.fetch('http://192.168.1.5/api/cmd', {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
headers: { 'Content-Type': 'application/json' },
|
|
205
|
+
body: JSON.stringify({ cmd: 'reboot' })
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Platform implementations:** msga uses C# `HttpClient`, msger uses Rust `ureq`, msgview uses Node.js `fetch()`. All return a real `Response` object constructed on the JS side.
|
|
210
|
+
|
|
211
|
+
**Unsupported `init` fields** (silently ignored — not meaningful for native HTTP): `mode`, `credentials`, `cache`, `signal`, `redirect`. `new Headers()` objects are normalized to plain objects.
|
|
212
|
+
|
|
138
213
|
## Examples
|
|
139
214
|
|
|
140
215
|
See [samples.html](samples.html) for interactive examples and demos of all API features!
|
|
@@ -174,9 +249,9 @@ The samples demonstrate:
|
|
|
174
249
|
|
|
175
250
|
### msger (Rust/wry)
|
|
176
251
|
- **Bridge:** JavaScript injected via wry `with_initialization_script()` in `msger-native/src/main.rs`
|
|
177
|
-
- **JS API:** `msger-native/src/msger-api.js` creates `window.
|
|
252
|
+
- **JS API:** `msger-native/src/msger-api.js` creates `window.msgapi` object
|
|
178
253
|
- **Mechanism:** `window.ipc.postMessage(JSON.stringify(...))` to Rust IPC handler
|
|
179
|
-
- **
|
|
254
|
+
- **Capabilities:** Window control, UDP, HTTP fetch via `window.msgapi`
|
|
180
255
|
- **Limitations:** wry 0.47.2 has IPC issues; `close()` uses `window.close()` workaround
|
|
181
256
|
|
|
182
257
|
### msga (.NET MAUI)
|
package/msgapi-plan.md
CHANGED
|
@@ -14,7 +14,7 @@ Shared planning document for msgapi across implementations:
|
|
|
14
14
|
| API | msgview | msger | msga | Notes |
|
|
15
15
|
|-----|---------|-------|------|-------|
|
|
16
16
|
| **Window Control** |
|
|
17
|
-
| toggleFullscreen() |
|
|
17
|
+
| toggleFullscreen() | ❌ | ✅ | ✅ | msga: fire-and-forget JS→C# |
|
|
18
18
|
| setFullscreen(bool) | ✅ | ✅ | ✅ | |
|
|
19
19
|
| minimize() | ✅ | ✅ | ✅ | msga: Windows only |
|
|
20
20
|
| maximize() | ✅ | ✅ | ✅ | msga: Windows only |
|
|
@@ -25,22 +25,30 @@ Shared planning document for msgapi across implementations:
|
|
|
25
25
|
| navigate(url) | ❌ | ❌ | ✅ | msga only |
|
|
26
26
|
| reload() | ❌ | ❌ | ✅ | msga only |
|
|
27
27
|
| **File System** |
|
|
28
|
-
| fs.selectFile() |
|
|
29
|
-
| fs.selectFiles() |
|
|
30
|
-
| fs.saveFileAs() |
|
|
31
|
-
| fs.selectFolder() |
|
|
32
|
-
| fs.read(path) |
|
|
33
|
-
| fs.
|
|
34
|
-
| fs.
|
|
35
|
-
| fs.
|
|
36
|
-
| fs.
|
|
28
|
+
| fs.selectFile() | ❌ | ❌ | ✅ | msga: full JS bridge wired |
|
|
29
|
+
| fs.selectFiles() | ❌ | ❌ | ✅ | msga: full JS bridge wired |
|
|
30
|
+
| fs.saveFileAs() | ❌ | ❌ | ✅ | msga: Windows only |
|
|
31
|
+
| fs.selectFolder() | ❌ | ❌ | ✅ | msga: Windows only |
|
|
32
|
+
| fs.read(path, options?) | ❌ | ❌ | ✅ | encoding: utf8/base64/binary |
|
|
33
|
+
| fs.readAsDataUrl(path) | ❌ | ❌ | ✅ | Auto-detects MIME from extension |
|
|
34
|
+
| fs.write(path) | ❌ | ❌ | ✅ | |
|
|
35
|
+
| fs.list(path) | ❌ | ❌ | ✅ | Includes attributes/created on Windows |
|
|
36
|
+
| fs.exists(path) | ❌ | ❌ | ✅ | |
|
|
37
|
+
| fs.delete(path) | ❌ | ❌ | ✅ | |
|
|
38
|
+
| **Shell** |
|
|
39
|
+
| shell.exec() | ❌ | ❌ | ✅ | Process with stdout/stderr; limited on non-rooted Android |
|
|
40
|
+
| shell.open() | ❌ | ❌ | ✅ | Android: Intent.ACTION_VIEW; Windows: ShellExecute |
|
|
41
|
+
| shell.showInFolder() | ❌ | ❌ | ❌ | Defined in interface, not yet implemented |
|
|
42
|
+
| shell.trash() | ❌ | ❌ | ❌ | Defined in interface, not yet implemented |
|
|
37
43
|
| **UDP** |
|
|
38
|
-
| udp.send() |
|
|
39
|
-
| udp.listen() |
|
|
40
|
-
| udp.sendReceive() |
|
|
41
|
-
| udp.broadcast() |
|
|
44
|
+
| udp.send() | ✅ | ✅ | ✅ | Node dgram / Rust UdpSocket / C# UdpClient |
|
|
45
|
+
| udp.listen() | ✅ | ✅ | ✅ | Background receive loop in all hosts |
|
|
46
|
+
| udp.sendReceive() | ✅ | ✅ | ✅ | Send + wait with timeout |
|
|
47
|
+
| udp.broadcast() | ✅ | ✅ | ✅ | 255.255.255.255 broadcast |
|
|
48
|
+
| **HTTP** |
|
|
49
|
+
| http.fetch() | ✅ | ✅ | ✅ | CORS/mixed-content bypass via native HTTP; returns real Response object |
|
|
42
50
|
| **Configuration** |
|
|
43
|
-
| setLogging(value?) | ❌ | ❌ | ✅ | Returns old state; 'msga'/'msgapi'/'on'/'off'/null |
|
|
51
|
+
| setLogging(value?, name?) | ❌ | ❌ | ✅ | Returns old state; 'msga'/'msgapi'/'on'/'off'/null; name included in log messages/filenames |
|
|
44
52
|
| setAutoUpdate(value?) | ❌ | ❌ | ✅ | 'auto'/'check'/'off'; defaults to 'off' (web app owns policy) |
|
|
45
53
|
| installUpdate(url) | ❌ | ❌ | ✅ | Download + install APK; Android only. Web app calls this. |
|
|
46
54
|
| getNetworkInfo() | ❌ | ❌ | ✅ | Fresh local IP + subnet mask; used by httpudp-client isAddressReachable() |
|
|
@@ -48,7 +56,7 @@ Shared planning document for msgapi across implementations:
|
|
|
48
56
|
| updateUrl | ❌ | ❌ | ✅ | URL to versions.json — install source for update checks |
|
|
49
57
|
| version | ❌ | ❌ | ✅ | `window.msgapi.version` = app version string |
|
|
50
58
|
|
|
51
|
-
Legend: ✅ Implemented |
|
|
59
|
+
Legend: ✅ Implemented | ❌ Not implemented
|
|
52
60
|
|
|
53
61
|
### msga Logging
|
|
54
62
|
|
|
@@ -56,18 +64,20 @@ Two independent log streams, both via `https://rmf39.aaz.lt/logit/`:
|
|
|
56
64
|
|
|
57
65
|
| Stream | Log name | Controlled by | Content |
|
|
58
66
|
|--------|----------|---------------|---------|
|
|
59
|
-
| **C# (MsgALog)** | `logit-msga` | `LOGGING=
|
|
60
|
-
| **JS (_logit)** | `logit-msgapi` | `JS_LOGGING=
|
|
67
|
+
| **C# (MsgALog)** | `logit-msga` | `LOGGING=false` in msga.env | OnNavigated, msgapi:// dispatch, SendAsync, UDP recv |
|
|
68
|
+
| **JS (_logit)** | `logit-msgapi` | `JS_LOGGING=false` in msga.env | msgapi inject, msgapi object creation |
|
|
61
69
|
|
|
62
|
-
**Startup defaults** are
|
|
70
|
+
**Startup defaults** are `false` in `msga.env` — logging is off unless the web app enables it. At **runtime**:
|
|
63
71
|
```javascript
|
|
64
|
-
msgapi.setLogging()
|
|
65
|
-
msgapi.setLogging('
|
|
66
|
-
msgapi.setLogging('
|
|
67
|
-
msgapi.setLogging('
|
|
68
|
-
msgapi.setLogging('
|
|
72
|
+
msgapi.setLogging() // → 'none' (query current, no change)
|
|
73
|
+
msgapi.setLogging('on') // enables both C# and JS logging
|
|
74
|
+
msgapi.setLogging('on', 'test') // enables both; C# logs to logit-msga-test, JS to logit-msgapi-test, 'test' in messages
|
|
75
|
+
msgapi.setLogging('msga') // enables C# only
|
|
76
|
+
msgapi.setLogging('msgapi') // enables JS only
|
|
77
|
+
msgapi.setLogging('off') // disables both
|
|
69
78
|
```
|
|
70
79
|
Always returns previous state as string: `'msga'`, `'msgapi'`, `'msga,msgapi'`, or `'none'`.
|
|
80
|
+
The optional `name` parameter is included in: log file names (`logit-msga-{name}`, `logit-msgapi-{name}`) and C# log message prefix.
|
|
71
81
|
|
|
72
82
|
### msga Implementation Status
|
|
73
83
|
|
|
@@ -95,10 +105,15 @@ interface MsgAPI {
|
|
|
95
105
|
}
|
|
96
106
|
```
|
|
97
107
|
|
|
98
|
-
**Platform implementations**:
|
|
108
|
+
**Platform implementations (UDP)**:
|
|
99
109
|
- **msga** (C#/MAUI): ✅ `System.Net.Sockets.UdpClient` via `UdpService.cs`
|
|
100
|
-
- **msgview** (Electron):
|
|
101
|
-
- **msger** (Rust/wry):
|
|
110
|
+
- **msgview** (Electron): ✅ Node.js `dgram` via `ipcMain.handle` / preload
|
|
111
|
+
- **msger** (Rust/wry): ✅ `std::net::UdpSocket` via `NativeEvent` + event loop
|
|
112
|
+
|
|
113
|
+
**Platform implementations (HTTP fetch)**:
|
|
114
|
+
- **msga** (C#/MAUI): ✅ `HttpClient` — static instance, async `SendAsync`
|
|
115
|
+
- **msgview** (Electron): ✅ Node.js `fetch()` via `ipcMain.handle`
|
|
116
|
+
- **msger** (Rust/wry): ✅ `ureq` crate — blocking in `std::thread::spawn`, results via event loop
|
|
102
117
|
|
|
103
118
|
**httpudp-client integration**: `HttpUdpClient.connect()` detects `window.msgapi?.udp` and uses it directly when available, falling back to the httpudp WebSocket proxy otherwise. Callers never know which path was taken.
|
|
104
119
|
|
|
@@ -225,19 +240,26 @@ interface MsgAPI {
|
|
|
225
240
|
4. ✅ UDP fully implemented in msga (UdpService.cs)
|
|
226
241
|
5. ✅ httpudp-client detects msgapi.udp and uses it natively
|
|
227
242
|
|
|
228
|
-
### Phase 2: UDP in msger/msgview
|
|
229
|
-
1.
|
|
230
|
-
2.
|
|
243
|
+
### Phase 2: UDP in msger/msgview ✅ Done
|
|
244
|
+
1. ✅ msger (Rust) — `std::net::UdpSocket` via NativeEvent + thread::spawn
|
|
245
|
+
2. ✅ msgview (Electron) — Node.js `dgram` via ipcMain.handle + preload
|
|
246
|
+
|
|
247
|
+
### Phase 3: File System + Shell in msga ✅ Done
|
|
248
|
+
1. ✅ fs.read with encoding options (utf8/base64/binary)
|
|
249
|
+
2. ✅ fs.readAsDataUrl with MIME auto-detection
|
|
250
|
+
3. ✅ fs.list with attributes/created on Windows
|
|
251
|
+
4. ✅ shell.exec — process execution with stdout/stderr capture
|
|
252
|
+
5. ✅ shell.open — open files/URLs with system default handler
|
|
231
253
|
|
|
232
|
-
### Phase
|
|
233
|
-
1.
|
|
234
|
-
2. Implement
|
|
235
|
-
3. Add
|
|
254
|
+
### Phase 4: File System + Shell in msger/msgview
|
|
255
|
+
1. Implement fs.* via msgcommon shared code
|
|
256
|
+
2. Implement shell.exec/open via msgcommon shared code
|
|
257
|
+
3. Add mkdir, rename, copy, stat extensions
|
|
236
258
|
|
|
237
|
-
### Phase
|
|
259
|
+
### Phase 5: System Integration (Lower Priority)
|
|
238
260
|
1. Clipboard support
|
|
239
261
|
2. Notifications
|
|
240
|
-
3.
|
|
262
|
+
3. shell.showInFolder, shell.trash
|
|
241
263
|
|
|
242
264
|
---
|
|
243
265
|
|
package/msgapidefs.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// TypeScript definitions for the msgapi JavaScript API
|
|
2
|
-
// Type-safe access to msgapi's window control, file system, and UDP features
|
|
2
|
+
// Type-safe access to msgapi's window control, file system, shell, and UDP features
|
|
3
3
|
// Used by msgview, msger, and msga. See README.md for docs.
|
|
4
|
+
//
|
|
5
|
+
// ⚠️ SECURITY: msgapi gives full native OS access (filesystem, processes, networking).
|
|
6
|
+
// Intended ONLY for trusted apps — native apps written as web pages. Do not expose to untrusted content.
|
|
7
|
+
// ⚠️ EXPERIMENTAL: APIs are experimental and subject to change.
|
|
4
8
|
|
|
5
9
|
export interface MsgResult { // Result object returned when the window closes
|
|
6
10
|
button: string; // Button that was clicked
|
|
@@ -17,6 +21,8 @@ export interface FileInfo { // File information from file system operations
|
|
|
17
21
|
isDir: boolean; // True if this is a directory
|
|
18
22
|
size: number; // File size in bytes (0 for directories)
|
|
19
23
|
modified?: string; // Last modified timestamp (ISO 8601 string)
|
|
24
|
+
created?: string; // Creation time (ISO 8601 string); undefined on platforms without creation time
|
|
25
|
+
attributes?: number; // Win32 FILE_ATTRIBUTE_* flags (e.g. 0x00400000 = RECALL_ON_DATA_ACCESS for OneDrive cloud-only); undefined on Android/Linux
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
export interface FileDialogOptions { // Options for file selection dialogs
|
|
@@ -74,7 +80,7 @@ export interface MsgAPI {
|
|
|
74
80
|
|
|
75
81
|
// ── Configuration ────────────────────────────────────────────
|
|
76
82
|
|
|
77
|
-
setLogging?(value?: LoggingMode): LoggingMode; // null/undefined = query only. Returns previous state.
|
|
83
|
+
setLogging?(value?: LoggingMode, name?: string): LoggingMode; // null/undefined = query only. name included in log messages/filenames. Returns previous state.
|
|
78
84
|
setAutoUpdate?(value?: AutoUpdateMode): AutoUpdateMode; // null/undefined = query only. Returns previous state. msga Android only.
|
|
79
85
|
installUpdate?(url: string): Promise<void>; // Download + install APK from url. msga Android only; web app owns update policy.
|
|
80
86
|
getNetworkInfo?(): Promise<{ localIp: string; localMask: string }>; // Current device IP + subnet mask (fresh, not cached)
|
|
@@ -97,7 +103,8 @@ export interface MsgAPI {
|
|
|
97
103
|
selectFiles(options?: FileDialogOptions): Promise<SelectedFile[]>; // Multi-file picker
|
|
98
104
|
saveFileAs(content: string, defaultFilename?: string, options?: FileDialogOptions): Promise<string | null>; // Save dialog (returns path or null)
|
|
99
105
|
selectFolder(options?: FileDialogOptions): Promise<string | null>; // Folder picker (null if cancelled)
|
|
100
|
-
read(path: string): Promise<string>;
|
|
106
|
+
read(path: string, options?: { encoding?: 'utf8' | 'base64' | 'binary' }): Promise<string | Uint8Array>; // Read file; utf8 (default)=text, base64=raw b64 string, binary=Uint8Array
|
|
107
|
+
readAsDataUrl(path: string, mimeType?: string): Promise<string>; // Read file as data URL (auto-detects MIME from extension if omitted)
|
|
101
108
|
write(path: string, content: string): Promise<void>; // Write file contents (requires allowFs)
|
|
102
109
|
list(path: string): Promise<FileInfo[]>; // List directory contents (requires allowFs)
|
|
103
110
|
exists(path: string): Promise<boolean>; // Check if file/directory exists (requires allowFs)
|
|
@@ -113,6 +120,30 @@ export interface MsgAPI {
|
|
|
113
120
|
sendReceive(host: string, port: number, data: string | Uint8Array, timeoutMs?: number): Promise<Uint8Array>; // Send and wait for response
|
|
114
121
|
broadcast(port: number, data: string | Uint8Array): Promise<void>; // Broadcast to local network
|
|
115
122
|
};
|
|
123
|
+
|
|
124
|
+
// ── HTTP Fetch (CORS/mixed-content bypass) ──────────────────
|
|
125
|
+
// Routes requests through the native host — no mixed-content or CORS restrictions
|
|
126
|
+
// Returns a real Response object: .ok, .status, .json(), .text(), .headers, .clone()
|
|
127
|
+
|
|
128
|
+
http?: {
|
|
129
|
+
fetch(url: string, init?: RequestInit): Promise<Response>; // Drop-in fetch() replacement via native HTTP
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ── Shell / Process Execution ─────────────────────────────────
|
|
133
|
+
// Native process execution — run commands, open files with default handler
|
|
134
|
+
// ⚠️ Powerful: grants full process execution. Use only in trusted apps.
|
|
135
|
+
|
|
136
|
+
shell?: {
|
|
137
|
+
exec(command: string, args?: string[], options?: { // Run external command, capture output
|
|
138
|
+
cwd?: string; // Working directory
|
|
139
|
+
timeout?: number; // Timeout in ms (0 = no timeout)
|
|
140
|
+
stdin?: string; // Pipe string to stdin
|
|
141
|
+
encoding?: 'utf8' | 'base64'; // stdout/stderr encoding (default utf8)
|
|
142
|
+
}): Promise<{ stdout: string; stderr: string; exitCode: number }>;
|
|
143
|
+
open(path: string): Promise<void>; // Open file/URL with system default handler (ShellExecute / Intent.ACTION_VIEW)
|
|
144
|
+
showInFolder(path: string): Promise<void>; // Select file in Explorer / file manager
|
|
145
|
+
trash(path: string): Promise<void>; // Move to Recycle Bin / Trash (soft delete, not permanent)
|
|
146
|
+
};
|
|
116
147
|
}
|
|
117
148
|
|
|
118
149
|
// Global window.msgapi declaration
|