@bobfrankston/msger 0.1.188 → 0.1.190

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
@@ -668,10 +668,11 @@ This package is the **Rust/wry-based** implementation. There's also **[@bobfrank
668
668
  - Need reliable cross-platform rendering
669
669
  - Prefer Electron ecosystem
670
670
 
671
- ### Planned: msgapi
672
- Both packages will share a common JavaScript API (`window.msgapi`) for file system access, window manipulation, and bidirectional communication. See [TODO.md](TODO.md) for details.
671
+ ### window.msgapi
673
672
 
674
- For the complete feature comparison and roadmap, see the [shared TODO.md](../msgview/TODO.md) which tracks both projects.
673
+ msger injects `window.msgapi` into loaded pages via `msger-api.js`, providing window control, UDP networking, and HTTP fetch. File system and shell operations are not yet implemented in msger.
674
+
675
+ See **[msgapidefs README](../msgapidefs/README.md)** for the full `window.msgapi` API reference, implementation status, and security notes. APIs are experimental and subject to change.
675
676
 
676
677
  ---
677
678
 
@@ -1,210 +1,26 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Build script to run natively inside WSL
4
- * Handles both x64 and ARM64 builds
5
- * Reads configuration from build-config.json
6
- *
7
- * Usage from WSL:
8
- * node builder/build-under-wsl.ts
9
- */
10
-
11
- import { execSync } from 'child_process';
12
- import fs from 'fs';
13
- import path from 'path';
14
- import { fileURLToPath } from 'url';
15
-
16
- const __filename = fileURLToPath(import.meta.url);
17
- const __dirname = path.dirname(__filename);
18
-
19
- interface BuildConfig {
20
- platforms: {
21
- windows?: boolean;
22
- wsl?: boolean;
23
- pi?: boolean;
24
- arm64?: boolean;
25
- };
26
- options?: {
27
- release?: boolean;
28
- verbose?: boolean;
29
- piHost?: string;
30
- piProjectPath?: string;
31
- };
32
- }
33
-
34
- function timestamp(): string {
35
- const now = new Date();
36
- return now.toTimeString().split(' ')[0];
37
- }
38
-
39
- interface BuildTarget {
40
- name: string;
41
- rustTarget: string;
42
- outputName: string;
43
- description: string;
44
- }
45
-
46
- const targets: Record<string, BuildTarget> = {
47
- x64: {
48
- name: 'x64',
49
- rustTarget: 'x86_64-unknown-linux-gnu',
50
- outputName: 'msgernative',
51
- description: 'Linux x64'
52
- },
53
- arm64: {
54
- name: 'arm64',
55
- rustTarget: 'aarch64-unknown-linux-gnu',
56
- outputName: 'msgernative-linux-aarch64',
57
- description: 'Linux ARM64'
58
- }
59
- };
60
-
61
- function checkToolchain(target: BuildTarget): boolean {
62
- try {
63
- const installed = execSync('rustup target list --installed', { encoding: 'utf-8' });
64
- return installed.includes(target.rustTarget);
65
- } catch {
66
- return false;
67
- }
68
- }
69
-
70
- function installToolchain(target: BuildTarget): boolean {
71
- console.log(` [${timestamp()}] 📦 Installing ${target.description} toolchain...`);
72
-
73
- try {
74
- // Add Rust target
75
- execSync(`rustup target add ${target.rustTarget}`, { stdio: 'inherit' });
76
-
77
- // For ARM64, also install cross-compiler
78
- if (target.name === 'arm64') {
79
- console.log(` [${timestamp()}] 📦 Installing ARM64 cross-compiler...`);
80
- execSync('sudo apt update && sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross', {
81
- stdio: 'inherit'
82
- });
83
- }
84
-
85
- console.log(` [${timestamp()}] ✅ Toolchain installed`);
86
- return true;
87
- } catch (error: any) {
88
- console.error(` [${timestamp()}] ❌ Failed to install toolchain: ${error.message}`);
89
- return false;
90
- }
91
- }
92
-
93
- function build(target: BuildTarget, verbose: boolean): boolean {
94
- console.log(`\n[${timestamp()}] 📦 Building ${target.description} binary...`);
95
-
96
- // Check toolchain
97
- if (!checkToolchain(target)) {
98
- console.log(` [${timestamp()}] ⚙️ ${target.description} toolchain not found`);
99
- if (!installToolchain(target)) {
100
- return false;
101
- }
102
- }
103
-
104
- const nativeDir = path.join(__dirname, '..');
105
- const binDir = path.join(nativeDir, 'bin');
106
- const binaryPath = path.join(binDir, target.outputName);
107
-
108
- try {
109
- console.log(` [${timestamp()}] 🔨 Compiling with cargo...`);
110
- execSync(`cargo build --release --target ${target.rustTarget}`, {
111
- cwd: nativeDir,
112
- stdio: verbose ? 'inherit' : 'pipe'
113
- });
114
-
115
- // Copy to bin directory
116
- if (!fs.existsSync(binDir)) {
117
- fs.mkdirSync(binDir, { recursive: true });
118
- }
119
-
120
- const sourcePath = path.join(nativeDir, 'target', target.rustTarget, 'release', 'msgernative');
121
- fs.copyFileSync(sourcePath, binaryPath);
122
- fs.chmodSync(binaryPath, 0o755);
123
-
124
- const stats = fs.statSync(binaryPath);
125
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
126
- console.log(` [${timestamp()}] ✅ ${target.description} binary completed`);
127
- console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
128
- console.log(` [${timestamp()}] 📁 Output: ${binaryPath}`);
129
- return true;
130
- } catch (error: any) {
131
- console.error(` [${timestamp()}] ❌ Build failed: ${error.message}`);
132
- return false;
133
- }
134
- }
135
-
136
- function loadConfig(): BuildConfig {
137
- const configPath = path.join(__dirname, 'build-config.json');
138
-
139
- if (!fs.existsSync(configPath)) {
140
- console.error('❌ build-config.json not found');
141
- process.exit(1);
142
- }
143
-
144
- try {
145
- const content = fs.readFileSync(configPath, 'utf-8');
146
- return JSON.parse(content);
147
- } catch (error: any) {
148
- console.error(`❌ Failed to parse build-config.json: ${error.message}`);
149
- process.exit(1);
150
- }
151
- }
152
-
153
- function main() {
154
- console.log('🚀 WSL Native Builder\n');
155
-
156
- const config = loadConfig();
157
- const verboseFlag = config.options?.verbose ?? false;
158
-
159
- // Determine which targets to build based on config
160
- const requestedTargets: string[] = [];
161
- if (config.platforms.wsl) requestedTargets.push('x64');
162
- if (config.platforms.arm64) requestedTargets.push('arm64');
163
-
164
- if (requestedTargets.length === 0) {
165
- console.log('⚠️ No WSL platforms enabled in build-config.json');
166
- console.log(' Enable "wsl" for x64 or "arm64" for ARM64 builds');
167
- process.exit(0);
168
- }
169
-
170
- console.log(`📋 Configuration:`);
171
- console.log(` Mode: ${config.options?.release ? 'release' : 'debug'}`);
172
- console.log(` Targets: ${requestedTargets.join(', ')}`);
173
- console.log(` Verbose: ${verboseFlag}\n`);
174
-
175
- const results: { target: string; success: boolean }[] = [];
176
-
177
- for (const targetName of requestedTargets) {
178
- const target = targets[targetName];
179
- if (!target) {
180
- console.error(`❌ Unknown target: ${targetName}`);
181
- console.error(` Available targets: x64, arm64, all`);
182
- process.exit(1);
183
- }
184
-
185
- const success = build(target, verboseFlag);
186
- results.push({ target: targetName, success });
187
- }
188
-
189
- // Summary
190
- console.log('\n' + '='.repeat(50));
191
- console.log('📊 Build Summary:');
192
- console.log('='.repeat(50));
193
-
194
- for (const result of results) {
195
- const status = result.success ? '✅' : '❌';
196
- console.log(`${status} ${result.target}`);
197
- }
198
-
199
- const successCount = results.filter(r => r.success).length;
200
- const totalCount = results.length;
201
-
202
- console.log('='.repeat(50));
203
- console.log(`\n🎉 Completed: ${successCount}/${totalCount} builds successful\n`);
204
-
205
- if (successCount < totalCount) {
206
- process.exit(1);
207
- }
208
- }
209
-
210
- main();
1
+ #!/usr/bin/env node
2
+ /**
3
+ * msger-native WSL builder delegates to @bobfrankston/rust-builder
4
+ * Run this directly inside WSL: node builder/build-under-wsl.ts
5
+ */
6
+ import { loadConfig, build } from "@bobfrankston/rust-builder";
7
+ import path from "path";
8
+ import { fileURLToPath } from "url";
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ const nativeDir = path.join(__dirname, "..");
12
+ const binDir = path.join(nativeDir, "bin");
13
+
14
+ const config = loadConfig(
15
+ path.join(__dirname, "build-config.json"),
16
+ "msgernative",
17
+ nativeDir,
18
+ binDir
19
+ );
20
+
21
+ // Only build WSL targets when running inside WSL
22
+ config.platforms.windows = false;
23
+ config.platforms.pi = false;
24
+
25
+ const success = build(config);
26
+ if (!success) process.exit(1);
@@ -1,556 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Unified build system for msger-native
4
- * Reads build-config.json to determine which platforms to build
5
- */
6
-
7
- import { execSync, spawnSync } from 'child_process';
8
- import fs from 'fs';
9
- import path from 'path';
10
- import { fileURLToPath } from 'url';
11
- import { platform } from 'os';
12
-
13
- const __filename = fileURLToPath(import.meta.url);
14
- const __dirname = path.dirname(__filename);
15
-
16
- interface BuildConfig {
17
- platforms: {
18
- windows?: boolean;
19
- wsl?: boolean;
20
- pi?: boolean;
21
- arm64?: boolean;
22
- };
23
- options?: {
24
- release?: boolean;
25
- verbose?: boolean;
26
- piHost?: string;
27
- piProjectPath?: string;
28
- };
29
- }
30
-
31
- function isWSL(): boolean {
32
- try {
33
- const release = fs.readFileSync('/proc/version', 'utf8');
34
- return release.toLowerCase().includes('microsoft');
35
- } catch {
36
- return false;
37
- }
38
- }
39
-
40
- function isWindows(): boolean {
41
- return platform() === 'win32' && !isWSL();
42
- }
43
-
44
- function getCleanPathForWindows(): string {
45
- // Remove Git's /usr/bin from PATH to avoid link.exe conflict with VS linker
46
- const currentPath = process.env.PATH || '';
47
- const pathParts = currentPath.split(';');
48
- const filteredParts = pathParts.filter(part => {
49
- const lower = part.toLowerCase();
50
- return !lower.includes('git\\usr\\bin') && !lower.includes('git/usr/bin');
51
- });
52
- return filteredParts.join(';');
53
- }
54
-
55
- /**
56
- * Execute a command in WSL using spawn to avoid quoting issues
57
- */
58
- function wslExec(command: string, args: string[], options?: { stdio?: 'inherit' | 'pipe'; encoding?: BufferEncoding }): { success: boolean; output: string; error: string } {
59
- const result = spawnSync('wsl', [command, ...args], {
60
- stdio: options?.stdio || 'pipe',
61
- encoding: options?.encoding || 'utf-8'
62
- });
63
-
64
- return {
65
- success: result.status === 0,
66
- output: result.stdout?.toString() || '',
67
- error: result.stderr?.toString() || ''
68
- };
69
- }
70
-
71
- /**
72
- * Convert Windows path to WSL path
73
- */
74
- function toWSLPath(winPath: string): string {
75
- // Convert backslashes to forward slashes for wslpath
76
- const normalizedPath = winPath.replace(/\\/g, '/');
77
- const result = spawnSync('wsl', ['wslpath', normalizedPath], { encoding: 'utf-8' });
78
- if (result.status !== 0) {
79
- const error = result.stderr?.toString() || 'unknown error';
80
- throw new Error(`Failed to convert path: ${winPath} - ${error}`);
81
- }
82
- return result.stdout.trim();
83
- }
84
-
85
- function needsRebuild(binaryPath: string, sourceDirs: string[]): boolean {
86
- if (!fs.existsSync(binaryPath)) {
87
- return true;
88
- }
89
-
90
- const binaryTime = fs.statSync(binaryPath).mtime.getTime();
91
-
92
- function checkDir(dirPath: string): boolean {
93
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
94
- for (const entry of entries) {
95
- const fullPath = path.join(dirPath, entry.name);
96
- if (entry.isFile()) {
97
- const fileTime = fs.statSync(fullPath).mtime.getTime();
98
- if (fileTime > binaryTime) {
99
- console.log(` 📝 Source file changed: ${entry.name}`);
100
- return true;
101
- }
102
- } else if (entry.isDirectory()) {
103
- if (checkDir(fullPath)) {
104
- return true;
105
- }
106
- }
107
- }
108
- return false;
109
- }
110
-
111
- for (const sourcePath of sourceDirs) {
112
- if (!fs.existsSync(sourcePath)) continue;
113
-
114
- const stats = fs.statSync(sourcePath);
115
- if (stats.isFile()) {
116
- const fileTime = stats.mtime.getTime();
117
- if (fileTime > binaryTime) {
118
- console.log(` 📝 Source file changed: ${sourcePath}`);
119
- return true;
120
- }
121
- } else if (stats.isDirectory()) {
122
- if (checkDir(sourcePath)) {
123
- return true;
124
- }
125
- }
126
- }
127
-
128
- return false;
129
- }
130
-
131
- /**
132
- * Load build configuration from JSON file
133
- */
134
- function loadConfig(): BuildConfig {
135
- const configPath = path.join(__dirname, 'build-config.json');
136
-
137
- if (!fs.existsSync(configPath)) {
138
- console.error('❌ build-config.json not found');
139
- process.exit(1);
140
- }
141
-
142
- try {
143
- const content = fs.readFileSync(configPath, 'utf-8');
144
- return JSON.parse(content);
145
- } catch (error: any) {
146
- console.error(`❌ Failed to parse build-config.json: ${error.message}`);
147
- process.exit(1);
148
- }
149
- }
150
-
151
- function timestamp(): string {
152
- const now = new Date();
153
- return now.toTimeString().split(' ')[0]; // HH:MM:SS
154
- }
155
-
156
- /**
157
- * Build Windows binary
158
- */
159
- function buildWindows(nativeDir: string, binDir: string, verbose: boolean): boolean {
160
- console.log(`\n[${timestamp()}] 📦 Building Windows x64 binary...`);
161
-
162
- const binaryPath = path.join(binDir, 'msgernative.exe');
163
- const sourceDirs = [
164
- path.join(nativeDir, 'src'),
165
- path.join(nativeDir, 'Cargo.toml'),
166
- path.join(nativeDir, 'build.rs')
167
- ];
168
-
169
- if (!needsRebuild(binaryPath, sourceDirs)) {
170
- console.log(` [${timestamp()}] ✅ Binary is up to date, skipping build`);
171
- const stats = fs.statSync(binaryPath);
172
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
173
- console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
174
- return true;
175
- }
176
-
177
- console.log(` [${timestamp()}] 🔧 Fixing PATH to use VS linker instead of Git link.exe`);
178
- const cleanPath = getCleanPathForWindows();
179
-
180
- try {
181
- execSync('cargo build --release', {
182
- cwd: nativeDir,
183
- stdio: verbose ? 'inherit' : 'pipe',
184
- env: { ...process.env, PATH: cleanPath }
185
- });
186
-
187
- // Copy to bin directory
188
- if (!fs.existsSync(binDir)) {
189
- fs.mkdirSync(binDir, { recursive: true });
190
- }
191
-
192
- const sourcePath = path.join(nativeDir, 'target', 'release', 'msgernative.exe');
193
- fs.copyFileSync(sourcePath, binaryPath);
194
-
195
- // Copy msger icons (.ico and .png) to bin directory
196
- const winIcoSource = path.join(nativeDir, '..', 'msger.ico');
197
- const winIcoDest = path.join(binDir, 'msger.ico');
198
- const winPngSource = path.join(nativeDir, '..', 'msger.png');
199
- const winPngDest = path.join(binDir, 'msger.png');
200
-
201
- if (fs.existsSync(winIcoSource)) {
202
- fs.copyFileSync(winIcoSource, winIcoDest);
203
- console.log(` [${timestamp()}] 🎨 Icon (ICO) copied to bin directory`);
204
- }
205
- if (fs.existsSync(winPngSource)) {
206
- fs.copyFileSync(winPngSource, winPngDest);
207
- console.log(` [${timestamp()}] 🎨 Icon (PNG) copied to bin directory`);
208
- }
209
-
210
- const stats = fs.statSync(binaryPath);
211
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
212
- console.log(` [${timestamp()}] ✅ Windows x64 binary completed`);
213
- console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
214
- return true;
215
- } catch (error: any) {
216
- console.error(` [${timestamp()}] ❌ Windows build failed: ${error.message}`);
217
- return false;
218
- }
219
- }
220
-
221
- /**
222
- * Build WSL Linux binary
223
- */
224
- function buildWSL(nativeDir: string, binDir: string, verbose: boolean): boolean {
225
- console.log(`\n[${timestamp()}] 📦 Building WSL Linux x64 binary...`);
226
-
227
- const binaryPath = path.join(binDir, 'msgernative');
228
- const wslNativeDir = toWSLPath(nativeDir);
229
-
230
- try {
231
- // Use rustup's cargo, not system cargo
232
- const cmd = `export PATH="$HOME/.cargo/bin:$PATH" && cd "${wslNativeDir}" && cargo build --release --target x86_64-unknown-linux-gnu`;
233
- const result = wslExec('bash', ['-c', cmd], {
234
- stdio: verbose ? 'inherit' : 'pipe'
235
- });
236
-
237
- if (!result.success) {
238
- throw new Error(result.error || 'Build failed');
239
- }
240
-
241
- // Copy to bin directory
242
- if (!fs.existsSync(binDir)) {
243
- fs.mkdirSync(binDir, { recursive: true });
244
- }
245
-
246
- const sourcePath = path.join(nativeDir, 'target', 'x86_64-unknown-linux-gnu', 'release', 'msgernative');
247
- fs.copyFileSync(sourcePath, binaryPath);
248
- fs.chmodSync(binaryPath, 0o755);
249
-
250
- // Copy msger.png icon to bin directory (Linux uses PNG)
251
- const wslIconSource = path.join(nativeDir, '..', 'msger.png');
252
- const wslIconDest = path.join(binDir, 'msger.png');
253
- if (fs.existsSync(wslIconSource)) {
254
- fs.copyFileSync(wslIconSource, wslIconDest);
255
- console.log(` [${timestamp()}] 🎨 Icon (PNG) copied to bin directory`);
256
- }
257
-
258
- const stats = fs.statSync(binaryPath);
259
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
260
- console.log(` [${timestamp()}] ✅ WSL Linux x64 binary completed`);
261
- console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
262
- return true;
263
- } catch (error: any) {
264
- console.error(` [${timestamp()}] ❌ WSL build failed: ${error.message}`);
265
- return false;
266
- }
267
- }
268
-
269
- /**
270
- * Check if Rust is installed on Pi
271
- */
272
- function checkPiRust(piHost: string): boolean {
273
- try {
274
- // Source cargo env before checking (cargo won't be in PATH until sourced)
275
- const result = execSync(`ssh ${piHost} "source ~/.cargo/env 2>/dev/null && command -v cargo"`, {
276
- encoding: 'utf-8',
277
- stdio: 'pipe'
278
- });
279
- return result.trim().length > 0;
280
- } catch {
281
- return false;
282
- }
283
- }
284
-
285
- /**
286
- * Install Rust on Pi
287
- */
288
- function installPiRust(piHost: string, verbose: boolean): boolean {
289
- console.log(` [${timestamp()}] 📦 Rust not found on Pi, installing...`);
290
-
291
- try {
292
- // Install Rust using rustup
293
- execSync(`ssh ${piHost} "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"`, {
294
- stdio: verbose ? 'inherit' : 'pipe'
295
- });
296
-
297
- console.log(` [${timestamp()}] ✅ Rust installed on Pi`);
298
- return true;
299
- } catch (error: any) {
300
- console.error(` [${timestamp()}] ❌ Failed to install Rust on Pi: ${error.message}`);
301
- return false;
302
- }
303
- }
304
-
305
- /**
306
- * Build Raspberry Pi ARM64 binary
307
- */
308
- function buildPi(nativeDir: string, binDir: string, verbose: boolean, piHost: string, piProjectPath: string): boolean {
309
- console.log(`\n[${timestamp()}] 📦 Building Raspberry Pi ARM64 binary...`);
310
- console.log(` Target: ${piHost}:${piProjectPath}`);
311
-
312
- const binaryPath = path.join(binDir, 'msgernative-linux-aarch64');
313
- const sourceDirs = [
314
- path.join(nativeDir, 'src'),
315
- path.join(nativeDir, 'Cargo.toml'),
316
- path.join(nativeDir, 'build.rs')
317
- ];
318
-
319
- try {
320
- // Check if rebuild is needed
321
- if (!needsRebuild(binaryPath, sourceDirs)) {
322
- console.log(` [${timestamp()}] ✅ Binary is up to date, skipping build`);
323
- const stats = fs.statSync(binaryPath);
324
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
325
- console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
326
- return true;
327
- }
328
-
329
- // Check if Rust is installed on Pi, install if missing
330
- if (!checkPiRust(piHost)) {
331
- if (!installPiRust(piHost, verbose)) {
332
- return false;
333
- }
334
- }
335
-
336
- // Copy source files to Pi
337
- console.log(` [${timestamp()}] 📤 Copying source files to Pi...`);
338
- execSync(`ssh ${piHost} "mkdir -p ${piProjectPath}"`, {
339
- stdio: verbose ? 'inherit' : 'pipe'
340
- });
341
-
342
- // Copy only necessary files for compilation: src/, Cargo.toml, build.rs
343
- const filesToCopy = ['src', 'Cargo.toml', 'Cargo.lock', 'build.rs'];
344
- for (const file of filesToCopy) {
345
- const sourcePath = path.join(nativeDir, file);
346
- if (fs.existsSync(sourcePath)) {
347
- console.log(` [${timestamp()}] → ${file}`);
348
- execSync(`scp -r "${sourcePath}" ${piHost}:${piProjectPath}/`, {
349
- stdio: verbose ? 'inherit' : 'pipe'
350
- });
351
- }
352
- }
353
-
354
- // Build remotely on Pi (source cargo environment in case just installed)
355
- console.log(` [${timestamp()}] 🔗 Building on remote Raspberry Pi...`);
356
- execSync(`ssh ${piHost} "source ~/.cargo/env && cd ${piProjectPath} && cargo build --release"`, {
357
- stdio: verbose ? 'inherit' : 'pipe'
358
- });
359
-
360
- // Copy back only the compiled binary
361
- console.log(` [${timestamp()}] 📥 Copying binary from Pi...`);
362
- if (!fs.existsSync(binDir)) {
363
- fs.mkdirSync(binDir, { recursive: true });
364
- }
365
-
366
- execSync(`scp ${piHost}:${piProjectPath}/target/release/msgernative "${binaryPath}"`, {
367
- stdio: verbose ? 'inherit' : 'pipe'
368
- });
369
-
370
- fs.chmodSync(binaryPath, 0o755);
371
-
372
- const stats = fs.statSync(binaryPath);
373
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
374
- console.log(` [${timestamp()}] ✅ Raspberry Pi ARM64 binary completed`);
375
- console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
376
- return true;
377
- } catch (error: any) {
378
- console.error(` [${timestamp()}] ❌ Pi build failed: ${error.message}`);
379
- return false;
380
- }
381
- }
382
-
383
- /**
384
- * Check if ARM64 toolchain is installed
385
- */
386
- function checkARM64Toolchain(): boolean {
387
- // Check with PATH set to include cargo bin
388
- // Also verify ARM64 libraries are present (linker needs these)
389
- const cmd = 'export PATH="$HOME/.cargo/bin:$PATH" && which rustup && rustup target list --installed | grep -q aarch64-unknown-linux-gnu && which aarch64-linux-gnu-gcc && dpkg -l | grep -q "libwebkit2gtk-4.1.*arm64"';
390
- const result = wslExec('bash', ['-c', cmd]);
391
- return result.success;
392
- }
393
-
394
- /**
395
- * Install ARM64 toolchain
396
- */
397
- function installARM64Toolchain(verbose: boolean): boolean {
398
- console.log(` [${timestamp()}] 📦 ARM64 toolchain not found, installing...`);
399
-
400
- // Get project root (parent of msger-native) and convert to WSL path
401
- const projectRoot = path.resolve(__dirname, '..', '..');
402
- const wslPath = toWSLPath(projectRoot);
403
-
404
- // Check if rustup is installed, if not install Rust first
405
- const rustupCheck = wslExec('which', ['rustup']);
406
- if (!rustupCheck.success) {
407
- console.log(` [${timestamp()}] 📦 Installing Rust in WSL...`);
408
- const rustInstallScript = `${wslPath}/msger-native/pibuild/install-rust-wsl.sh`;
409
- const rustInstall = wslExec('bash', [rustInstallScript], { stdio: verbose ? 'inherit' : 'pipe' });
410
-
411
- if (!rustInstall.success) {
412
- console.error(` [${timestamp()}] ❌ Failed to install Rust in WSL`);
413
- if (!verbose && rustInstall.error) {
414
- console.error(` [${timestamp()}] ${rustInstall.error}`);
415
- }
416
- return false;
417
- }
418
- console.log(` [${timestamp()}] ✅ Rust installed in WSL`);
419
- }
420
-
421
- // Now install ARM64 cross-compilation toolchain
422
- const scriptPath = `${wslPath}/msger-native/pibuild/setup-arm64-toolchain.sh`;
423
- const result = wslExec('bash', [scriptPath], { stdio: verbose ? 'inherit' : 'pipe' });
424
-
425
- if (!result.success) {
426
- console.error(` [${timestamp()}] ❌ Failed to install ARM64 toolchain`);
427
- if (!verbose && result.error) {
428
- console.error(` [${timestamp()}] ${result.error}`);
429
- }
430
- return false;
431
- }
432
-
433
- console.log(` [${timestamp()}] ✅ ARM64 toolchain installed successfully`);
434
- return true;
435
- }
436
-
437
- /**
438
- * Build generic ARM64 binary via cross-compilation
439
- */
440
- function buildARM64(nativeDir: string, binDir: string, verbose: boolean): boolean {
441
- console.log(`\n[${timestamp()}] 📦 Building Linux ARM64 binary (cross-compile)...`);
442
-
443
- // Check and install toolchain if needed
444
- if (!checkARM64Toolchain()) {
445
- console.log(` [${timestamp()}] ⚙️ Checking ARM64 toolchain...`);
446
- if (!installARM64Toolchain(verbose)) {
447
- return false;
448
- }
449
- }
450
-
451
- const binaryPath = path.join(binDir, 'msgernative-linux-aarch64');
452
- const wslNativeDir = toWSLPath(nativeDir);
453
-
454
- console.log(` [${timestamp()}] 🔨 Compiling ARM64 binary (this may take 2-5 minutes)...`);
455
-
456
- try {
457
- // Use rustup's cargo, not system cargo
458
- // Set PKG_CONFIG_ALLOW_CROSS to enable cross-compilation
459
- const cmd = `export PATH="$HOME/.cargo/bin:$PATH" && export PKG_CONFIG_ALLOW_CROSS=1 && cd "${wslNativeDir}" && cargo build --release --target aarch64-unknown-linux-gnu`;
460
- const result = wslExec('bash', ['-c', cmd], {
461
- stdio: verbose ? 'inherit' : 'pipe'
462
- });
463
-
464
- if (!result.success) {
465
- throw new Error(result.error || 'Build failed');
466
- }
467
-
468
- // Copy to bin directory
469
- if (!fs.existsSync(binDir)) {
470
- fs.mkdirSync(binDir, { recursive: true });
471
- }
472
-
473
- const sourcePath = path.join(nativeDir, 'target', 'aarch64-unknown-linux-gnu', 'release', 'msgernative');
474
- fs.copyFileSync(sourcePath, binaryPath);
475
- fs.chmodSync(binaryPath, 0o755);
476
-
477
- const stats = fs.statSync(binaryPath);
478
- const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
479
- console.log(` [${timestamp()}] ✅ Linux ARM64 binary completed`);
480
- console.log(` [${timestamp()}] 📊 Binary size: ${sizeMB} MB`);
481
- return true;
482
- } catch (error: any) {
483
- console.error(` [${timestamp()}] ❌ ARM64 build failed: ${error.message}`);
484
- return false;
485
- }
486
- }
487
-
488
- /**
489
- * Main build orchestrator
490
- */
491
- function main() {
492
- console.log('🚀 msger-native unified builder\n');
493
-
494
- const config = loadConfig();
495
- const verbose = config.options?.verbose ?? false;
496
- const release = config.options?.release ?? true;
497
-
498
- const nativeDir = path.join(__dirname, '..');
499
- const binDir = path.join(nativeDir, 'bin');
500
-
501
- const enabledPlatforms = Object.entries(config.platforms)
502
- .filter(([_, enabled]) => enabled)
503
- .map(([name]) => name);
504
-
505
- console.log(`📋 Build configuration:`);
506
- console.log(` Mode: ${release ? 'release' : 'debug'}`);
507
- console.log(` Platforms: ${enabledPlatforms.join(', ') || 'none'}\n`);
508
-
509
- if (enabledPlatforms.length === 0) {
510
- console.log('⚠️ No platforms enabled in build-config.json');
511
- process.exit(0);
512
- }
513
-
514
- const results: { platform: string; success: boolean }[] = [];
515
-
516
- // Build each enabled platform
517
- if (config.platforms.windows) {
518
- results.push({ platform: 'windows', success: buildWindows(nativeDir, binDir, verbose) });
519
- }
520
-
521
- if (config.platforms.wsl) {
522
- results.push({ platform: 'wsl', success: buildWSL(nativeDir, binDir, verbose) });
523
- }
524
-
525
- if (config.platforms.pi) {
526
- const piHost = config.options?.piHost ?? 'pi4c';
527
- const piProjectPath = config.options?.piProjectPath ?? '/home/pi/dev/msger/msger-native';
528
- results.push({ platform: 'pi', success: buildPi(nativeDir, binDir, verbose, piHost, piProjectPath) });
529
- }
530
-
531
- if (config.platforms.arm64) {
532
- results.push({ platform: 'arm64', success: buildARM64(nativeDir, binDir, verbose) });
533
- }
534
-
535
- // Summary
536
- console.log('\n' + '='.repeat(50));
537
- console.log('📊 Build Summary:');
538
- console.log('='.repeat(50));
539
-
540
- for (const result of results) {
541
- const status = result.success ? '✅' : '❌';
542
- console.log(`${status} ${result.platform}`);
543
- }
544
-
545
- const successCount = results.filter(r => r.success).length;
546
- const totalCount = results.length;
547
-
548
- console.log('='.repeat(50));
549
- console.log(`\n🎉 Completed: ${successCount}/${totalCount} builds successful\n`);
550
-
551
- if (successCount < totalCount) {
552
- process.exit(1);
553
- }
554
- }
555
-
556
- main();
3
+ * msger-native builder delegates to @bobfrankston/rust-builder
4
+ */
5
+ import { loadConfig, build } from "@bobfrankston/rust-builder";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const nativeDir = path.join(__dirname, "..");
11
+ const binDir = path.join(nativeDir, "bin");
12
+
13
+ const config = loadConfig(
14
+ path.join(__dirname, "build-config.json"),
15
+ "msgernative",
16
+ nativeDir,
17
+ binDir
18
+ );
19
+
20
+ // msger-specific: copy icons
21
+ config.options = config.options || {};
22
+ config.options.icons = ["msger.ico", "msger.png"];
23
+ config.options.iconDir = path.join(nativeDir, "..");
24
+
25
+ const success = build(config);
26
+ if (!success) process.exit(1);
@@ -1,108 +1,21 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Post-install script to set executable permissions on Linux binaries
4
- * and check for required system dependencies
5
- */
6
-
7
- import fs from 'fs';
8
- import path from 'path';
9
- import { fileURLToPath } from 'url';
10
- import { execSync } from 'child_process';
11
-
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = path.dirname(__filename);
14
-
15
- function detectDistro() {
16
- if (!fs.existsSync('/etc/os-release')) {
17
- return 'unknown';
18
- }
19
-
20
- try {
21
- const osRelease = fs.readFileSync('/etc/os-release', 'utf8').toLowerCase();
22
-
23
- if (osRelease.includes('ubuntu') || osRelease.includes('debian')) {
24
- return 'debian';
25
- }
26
- if (osRelease.includes('fedora') || osRelease.includes('rhel') || osRelease.includes('centos')) {
27
- return 'fedora';
28
- }
29
- if (osRelease.includes('arch')) {
30
- return 'arch';
31
- }
32
-
33
- return 'unknown';
34
- } catch {
35
- return 'unknown';
36
- }
37
- }
38
-
39
- function showDependencyInstallCommand(distro) {
40
- console.warn('\n⚠️ Missing system dependencies detected!');
41
- console.warn(' Install required libraries with:\n');
42
-
43
- switch (distro) {
44
- case 'debian':
45
- console.warn(' sudo apt update');
46
- console.warn(' sudo apt install libwebkit2gtk-4.1-0 libgtk-3-0\n');
47
- break;
48
- case 'fedora':
49
- console.warn(' sudo dnf install webkit2gtk4.1 gtk3\n');
50
- break;
51
- case 'arch':
52
- console.warn(' sudo pacman -S webkit2gtk gtk3\n');
53
- break;
54
- default:
55
- console.warn(' Install webkit2gtk and gtk3 for your distribution\n');
56
- }
57
- }
58
-
59
- function checkSystemDependencies(binaryPath) {
60
- try {
61
- const lddOutput = execSync(`ldd "${binaryPath}" 2>&1`, { encoding: 'utf8' });
62
-
63
- if (lddOutput.includes('not found')) {
64
- const distro = detectDistro();
65
- showDependencyInstallCommand(distro);
66
- }
67
- } catch (error) {
68
- console.warn(`⚠️ Could not check system dependencies (ldd failed): ${error.message}`);
69
- console.warn(' You may need to install webkit2gtk and gtk3 manually.');
70
- }
71
- }
72
-
73
- function setExecutePermissions(binaryPath, binaryName) {
74
- try {
75
- fs.chmodSync(binaryPath, 0o755);
76
- console.log(`✅ Set execute permissions on ${binaryName} binary`);
77
- } catch (error) {
78
- console.warn(`⚠️ Could not set execute permission on ${binaryPath}: ${error.message}`);
79
- console.warn(` You may need to run: sudo chmod +x ${binaryPath}`);
80
- }
81
- }
82
-
83
- function main() {
84
- // Windows doesn't need executable permissions
85
- if (process.platform === 'win32') {
86
- return;
87
- }
88
-
89
- // Determine binary name based on architecture
90
- const arch = process.arch;
91
- const binaryName = arch === 'arm64' ? 'msgernative-linux-aarch64' : 'msgernative';
92
- const binaryPath = path.join(__dirname, '..', 'bin', binaryName);
93
-
94
- // Binary doesn't exist - warn user
95
- if (!fs.existsSync(binaryPath)) {
96
- console.warn(`⚠️ Binary not found at ${binaryPath}`);
97
- console.warn(` The msger package may not support your platform (${process.platform} ${arch})`);
98
- return;
99
- }
100
-
101
- // Set executable permissions
102
- setExecutePermissions(binaryPath, binaryName);
103
-
104
- // Check for required system libraries
105
- checkSystemDependencies(binaryPath);
106
- }
107
-
108
- main();
1
+ #!/usr/bin/env node
2
+ /**
3
+ * msger-native postinstall delegates to @bobfrankston/rust-builder
4
+ */
5
+ import { runPostinstall } from "@bobfrankston/rust-builder/postinstall";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+
11
+ runPostinstall({
12
+ binaryName: "msgernative",
13
+ binDir: path.join(__dirname, "..", "bin"),
14
+ binaries: {
15
+ win32: "msgernative.exe",
16
+ darwin: "msgernative",
17
+ darwinArm64: "msgernative-arm64",
18
+ linux: "msgernative",
19
+ linuxArm64: "msgernative-linux-aarch64",
20
+ },
21
+ });
package/package.json CHANGED
@@ -1,65 +1,65 @@
1
1
  {
2
- "name": "@bobfrankston/msger",
3
- "version": "0.1.188",
4
- "description": "Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview",
5
- "type": "module",
6
- "main": "./index.js",
7
- "bin": {
8
- "msger": "cli.js"
9
- },
10
- "exports": {
11
- ".": {
12
- "import": "./index.js",
13
- "types": "./index.d.ts"
14
- }
15
- },
16
- "scripts": {
17
- "build": "npm run build:ts && npm run build:native",
18
- "build:native": "cd msger-native && npm run build",
19
- "build:ts": "tsc",
20
- "watch": "tsc -w",
21
- "clean": "node msger-native/builder/clean.ts",
22
- "test": "node test.js",
23
- "postinstall": "node msger-native/builder/postinstall.js",
24
- "prepublishOnly": "npm run build:ts && npm run build:native",
25
- "prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
26
- "preversion": "npm run build:ts && npm run build:native && git add -A",
27
- "postversion": "git push && git push --tags",
28
- "release": "npm run prerelease:local && npm version patch && npm publish --quiet",
29
- "installer": "npm run release && node utils/install-latest.ts"
30
- },
31
- "keywords": [
32
- "message-box",
33
- "dialog",
34
- "webview",
35
- "rust",
36
- "native",
37
- "cross-platform",
38
- "typescript"
39
- ],
40
- "author": "Bob Frankston",
41
- "license": "ISC",
42
- "repository": {
43
- "type": "git",
44
- "url": "git+https://github.com/BobFrankston/msger.git"
45
- },
46
- "publishConfig": {
47
- "access": "public"
48
- },
49
- "devDependencies": {
50
- "@types/node": "^25.2.1"
51
- },
52
- "dependencies": {
53
- "@bobfrankston/msgcommon": "^0.1.23",
54
- "ansi-to-html": "^0.7.2",
55
- "json5": "^2.2.3"
56
- },
57
- "files": [
58
- "*.js",
59
- "*.d.ts",
60
- "*.d.ts.map",
61
- "msger-native/bin/",
62
- "msger-native/builder/",
63
- "README.md"
64
- ]
2
+ "name": "@bobfrankston/msger",
3
+ "version": "0.1.190",
4
+ "description": "Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "bin": {
8
+ "msger": "cli.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "import": "./index.js",
13
+ "types": "./index.d.ts"
14
+ }
15
+ },
16
+ "scripts": {
17
+ "build": "npm run build:ts && npm run build:native",
18
+ "build:native": "cd msger-native && npm run build",
19
+ "build:ts": "tsc",
20
+ "watch": "tsc -w",
21
+ "clean": "node msger-native/builder/clean.ts",
22
+ "test": "node test.js",
23
+ "postinstall": "node msger-native/builder/postinstall.js",
24
+ "prepublishOnly": "npm run build:ts && npm run build:native",
25
+ "prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
26
+ "preversion": "npm run build:ts && npm run build:native && git add -A",
27
+ "release": "npm run prerelease:local && npm version patch && npm publish --quiet",
28
+ "installer": "npm run release && node utils/install-latest.ts",
29
+ "postversion": "git push && git push --tags"
30
+ },
31
+ "keywords": [
32
+ "message-box",
33
+ "dialog",
34
+ "webview",
35
+ "rust",
36
+ "native",
37
+ "cross-platform",
38
+ "typescript"
39
+ ],
40
+ "author": "Bob Frankston",
41
+ "license": "ISC",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/BobFrankston/msger.git"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^25.2.1"
51
+ },
52
+ "dependencies": {
53
+ "@bobfrankston/msgcommon": "^0.1.23",
54
+ "ansi-to-html": "^0.7.2",
55
+ "json5": "^2.2.3"
56
+ },
57
+ "files": [
58
+ "*.js",
59
+ "*.d.ts",
60
+ "*.d.ts.map",
61
+ "msger-native/bin/",
62
+ "msger-native/builder/",
63
+ "README.md"
64
+ ]
65
65
  }