seabox 0.1.0-beta.1

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/.mocharc.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "spec": "test/**/*.spec.js",
3
+ "timeout": 60000,
4
+ "reporter": "spec",
5
+ "color": true
6
+ }
package/LICENSE.MD ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Meirion Hughes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,209 @@
1
+ # seabox
2
+
3
+ A reusable tool for building Node.js Single Executable Applications (SEA) with native-module support.
4
+
5
+ ## Features
6
+
7
+ - Bundle Node.js applications into standalone executables
8
+ - Automatic native module (.node, .dll, .so, .dylib) extraction and loading
9
+ - Asset encryption with obfuscated keys embedded in V8 snapshots
10
+ - Multi-platform targeting (Windows, Linux, macOS)
11
+ - V8 snapshot support for faster startup
12
+ - Integrity checking for extracted binaries
13
+ - Automatic code signature removal before injection
14
+ - Simple configuration via package.json
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install --save-dev seabox
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ Add a `sea` configuration to your `package.json`:
25
+
26
+ ```json
27
+ {
28
+ "sea": {
29
+ "entry": "./out/server.js",
30
+ "assets": [
31
+ "./out/client/**/*",
32
+ "./out/lib/**/*",
33
+ "./out/native/**/*",
34
+ "!**/*.md",
35
+ "!**/test/**"
36
+ ],
37
+ "binaries": [
38
+ "*.node",
39
+ "*.dll"
40
+ ],
41
+ "targets": [
42
+ "node24.11.0-win32-x64"
43
+ ],
44
+ "output": "myapp.exe",
45
+ "outputPath": "dist",
46
+ "disableExperimentalSEAWarning": true,
47
+ "useSnapshot": true,
48
+ "useCodeCache": false
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## Configuration Options
54
+
55
+ - **entry**: Path to your bundled application entry point
56
+ - **assets**: Array of glob patterns for files to include (supports `!` prefix for exclusions, e.g., `"!**/*.md"`)
57
+ - **binaries**: Patterns to identify binary files that need extraction (e.g., `.node`, `.dll`)
58
+ - **targets**: Array of target platforms (format: `nodeX.Y.Z-platform-arch`)
59
+ - **output**: Name of the output executable
60
+ - **outputPath**: Directory for build output
61
+ - **disableExperimentalSEAWarning**: Suppress Node.js SEA experimental warnings (default: true)
62
+ - **useSnapshot**: Enable V8 snapshot for faster startup (default: false)
63
+ - **useCodeCache**: Enable V8 code cache (default: false)
64
+ - **encryptAssets**: Enable encryption for assets (default: false)
65
+ - **encryptExclude**: Patterns to exclude from encryption (e.g., `['*.txt']`)
66
+ - **exclude**: *(Deprecated)* Use `!` prefix in assets array instead
67
+
68
+ ## Usage
69
+
70
+ After installing `seabox` as a dev dependency and configuring your `package.json`, build your SEA executable:
71
+
72
+ ### npm script (recommended)
73
+
74
+ Add a build script to your `package.json`:
75
+
76
+ ```json
77
+ {
78
+ "scripts": {
79
+ "build:exe": "seabox",
80
+ "build:exe:verbose": "seabox --verbose"
81
+ }
82
+ }
83
+ ```
84
+
85
+ Then run:
86
+
87
+ ```bash
88
+ npm run build:exe
89
+ ```
90
+
91
+ ### CLI
92
+
93
+ ```bash
94
+ # Build using package.json configuration
95
+ npx seabox
96
+
97
+ # Verbose output
98
+ npx seabox --verbose
99
+ ```
100
+
101
+ ### Programmatic API
102
+
103
+ ```javascript
104
+ const { build } = require('seabox');
105
+
106
+ await build({
107
+ projectRoot: process.cwd(),
108
+ verbose: true
109
+ });
110
+ ```
111
+
112
+ ## How It Works
113
+
114
+ 1. **Asset Scanning**: Scans and resolves all files matching your asset patterns
115
+ 2. **Manifest Generation**: Creates a runtime manifest with metadata for binary extraction
116
+ 3. **Bootstrap Injection**: Prepends bootstrap code to handle native module extraction
117
+ 4. **Blob Creation**: Uses Node.js SEA tooling to create the application blob
118
+ 5. **Binary Fetching**: Downloads the target Node.js binary and removes its signature
119
+ 6. **Injection**: Uses postject to inject the blob into the Node binary
120
+ 7. **Output**: Produces a standalone executable ready for signing and distribution
121
+
122
+ ## Binary Extraction
123
+
124
+ Native modules (`.node`, `.dll`, `.so`, `.dylib`) are automatically:
125
+ - Extracted to a cache directory on first run
126
+ - Integrity-checked using SHA-256 hashes
127
+ - Loaded via custom module resolution
128
+
129
+ Cache location: `%LOCALAPPDATA%\.sea-cache\<appname>\<version>-<platform>-<arch>`
130
+
131
+ ## Native Module Rebuilding
132
+
133
+ If your project has native modules (e.g., `.node` bindings), you may need to rebuild them for the target Node.js version:
134
+
135
+ ```bash
136
+ # Rebuild for a specific target
137
+ npx seabox-rebuild --target node24.11.0-win32-x64
138
+
139
+ # Rebuild with separate options
140
+ npx seabox-rebuild --node-version 24.11.0 --platform linux --arch x64
141
+
142
+ # Rebuild in a specific directory
143
+ npx seabox-rebuild /path/to/project --target node24.11.0-linux-x64
144
+ ```
145
+
146
+ The rebuilder will:
147
+ - Scan all dependencies for native modules (those with `binding.gyp` or `gypfile: true`)
148
+ - Rebuild each one using `node-gyp` for the target platform and Node.js version
149
+ - Download necessary headers for cross-compilation
150
+
151
+ **Note**: Cross-compilation may require additional platform-specific build tools installed.
152
+
153
+ ## Asset Encryption
154
+
155
+ seabox supports optional AES-256-GCM encryption of embedded assets to protect your application code and data:
156
+
157
+ ```json
158
+ {
159
+ "sea": {
160
+ "encryptAssets": true,
161
+ "encryptExclude": ["*.txt", "public/*"],
162
+ "useSnapshot": true
163
+ }
164
+ }
165
+ ```
166
+
167
+ ### How Encryption Works
168
+
169
+ 1. **Build Time**: A random 256-bit encryption key is generated
170
+ 2. **Asset Encryption**: Non-binary assets are encrypted using AES-256-GCM
171
+ 3. **Key Embedding**: The encryption key is obfuscated and embedded in the bootstrap code
172
+ 4. **Snapshot Obfuscation**: When `useSnapshot: true`, the key becomes part of V8 bytecode
173
+ 5. **Runtime Decryption**: Assets are transparently decrypted when accessed
174
+
175
+ ### Security Considerations
176
+
177
+ - **Binary files** (`.node`, `.dll`, `.so`, `.dylib`) are **never encrypted** as they must be extracted as-is
178
+ - The manifest (`sea-manifest.json`) is **not encrypted** to allow bootstrap initialization
179
+ - Encryption provides **obfuscation**, not cryptographic security against determined attackers
180
+ - When combined with V8 snapshots, the encryption key is compiled into bytecode, making extraction significantly harder
181
+ - Use `encryptExclude` to skip encryption for non-sensitive assets (e.g., public files, documentation)
182
+
183
+ ### Best Practices
184
+
185
+ ```json
186
+ {
187
+ "sea": {
188
+ "encryptAssets": true,
189
+ "useSnapshot": true, // Strongly recommended with encryption
190
+ "assets": [
191
+ "./out/**/*",
192
+ "!**/*.md", // Exclude documentation
193
+ "!**/public/**", // Exclude public assets
194
+ "!**/LICENSE" // Exclude license files
195
+ ]
196
+ }
197
+ }
198
+ ```
199
+
200
+ ## Platform Support
201
+
202
+ - **Windows**: `win32-x64`, `win32-arm64`
203
+ - **Linux**: `linux-x64`, `linux-arm64`
204
+ - **macOS**: `darwin-x64`, `darwin-arm64`
205
+
206
+ ## License
207
+
208
+ MIT
209
+ Copyright Meirion Hughes 2025
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Native Module Rebuilder for SEA Builder
5
+ *
6
+ * Scans package dependencies to identify native modules (those with binding.gyp
7
+ * or gypfile: true in package.json) and rebuilds them using node-gyp for a
8
+ * target Node.js version and platform.
9
+ *
10
+ * Supports cross-compilation for different platforms and architectures.
11
+ * Inspired by @electron/rebuild's module identification strategy.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { execSync } = require('child_process');
17
+
18
+ class NativeModuleRebuilder {
19
+ constructor(buildPath, options = {}) {
20
+ this.buildPath = buildPath || process.cwd();
21
+ this.nativeModules = new Map();
22
+ this.visitedPaths = new Set();
23
+
24
+ // Target configuration
25
+ this.targetNodeVersion = options.nodeVersion || process.version.replace('v', '');
26
+ this.targetPlatform = options.platform || process.platform;
27
+ this.targetArch = options.arch || process.arch;
28
+
29
+ // Parse from target string if provided (e.g., "node24.11.0-win32-x64")
30
+ if (options.target) {
31
+ const match = options.target.match(/^node(\d+\.\d+\.\d+)-(\w+)-(\w+)$/);
32
+ if (match) {
33
+ this.targetNodeVersion = match[1];
34
+ this.targetPlatform = match[2];
35
+ this.targetArch = match[3];
36
+ }
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Read and parse package.json from a directory
42
+ */
43
+ readPackageJson(modulePath) {
44
+ const packageJsonPath = path.join(modulePath, 'package.json');
45
+ try {
46
+ if (fs.existsSync(packageJsonPath)) {
47
+ const content = fs.readFileSync(packageJsonPath, 'utf8');
48
+ return JSON.parse(content);
49
+ }
50
+ } catch (error) {
51
+ console.warn(`Warning: Could not read package.json at ${packageJsonPath}:`, error.message);
52
+ }
53
+ return null;
54
+ }
55
+
56
+ /**
57
+ * Check if a module is a native module
58
+ * A module is considered native if it has:
59
+ * - A binding.gyp file, OR
60
+ * - gypfile: true in package.json
61
+ */
62
+ isNativeModule(modulePath) {
63
+ // Check for binding.gyp
64
+ const bindingGypPath = path.join(modulePath, 'binding.gyp');
65
+ if (fs.existsSync(bindingGypPath)) {
66
+ return true;
67
+ }
68
+
69
+ // Check for gypfile: true in package.json
70
+ const packageJson = this.readPackageJson(modulePath);
71
+ if (packageJson && packageJson.gypfile === true) {
72
+ return true;
73
+ }
74
+
75
+ return false;
76
+ }
77
+
78
+ /**
79
+ * Resolve the actual path of a dependency
80
+ */
81
+ resolveDependencyPath(fromPath, depName) {
82
+ const possiblePaths = [];
83
+
84
+ // Check in local node_modules
85
+ let currentPath = fromPath;
86
+ while (currentPath !== path.parse(currentPath).root) {
87
+ const nodeModulesPath = path.join(currentPath, 'node_modules', depName);
88
+ if (fs.existsSync(nodeModulesPath)) {
89
+ possiblePaths.push(nodeModulesPath);
90
+ }
91
+ currentPath = path.dirname(currentPath);
92
+ }
93
+
94
+ // Return the first valid path, resolving symlinks
95
+ for (const testPath of possiblePaths) {
96
+ try {
97
+ return fs.realpathSync(testPath);
98
+ } catch (error) {
99
+ // Skip if symlink is broken
100
+ continue;
101
+ }
102
+ }
103
+
104
+ return null;
105
+ }
106
+
107
+ /**
108
+ * Scan a directory's dependencies recursively
109
+ */
110
+ scanDependencies(modulePath, depth = 0) {
111
+ // Resolve symlinks to avoid scanning the same module multiple times
112
+ let realPath;
113
+ try {
114
+ realPath = fs.realpathSync(modulePath);
115
+ } catch (error) {
116
+ console.warn(`Warning: Could not resolve path ${modulePath}:`, error.message);
117
+ return;
118
+ }
119
+
120
+ // Skip if already visited
121
+ if (this.visitedPaths.has(realPath)) {
122
+ return;
123
+ }
124
+ this.visitedPaths.add(realPath);
125
+
126
+ // Check if this module is a native module
127
+ if (this.isNativeModule(realPath)) {
128
+ const packageJson = this.readPackageJson(realPath);
129
+ const moduleName = packageJson ? packageJson.name : path.basename(realPath);
130
+
131
+ if (!this.nativeModules.has(realPath)) {
132
+ this.nativeModules.set(realPath, moduleName);
133
+ console.log(`Found native module: ${moduleName} at ${realPath}`);
134
+ }
135
+ }
136
+
137
+ // Read package.json to find dependencies
138
+ const packageJson = this.readPackageJson(realPath);
139
+ if (!packageJson) {
140
+ return;
141
+ }
142
+
143
+ // Collect all types of dependencies
144
+ const allDeps = {
145
+ ...packageJson.dependencies,
146
+ ...packageJson.optionalDependencies,
147
+ ...packageJson.devDependencies
148
+ };
149
+
150
+ // Scan each dependency
151
+ for (const depName of Object.keys(allDeps)) {
152
+ const depPath = this.resolveDependencyPath(realPath, depName);
153
+ if (depPath) {
154
+ this.scanDependencies(depPath, depth + 1);
155
+ }
156
+ }
157
+
158
+ // Also check nested node_modules
159
+ const nodeModulesPath = path.join(realPath, 'node_modules');
160
+ if (fs.existsSync(nodeModulesPath)) {
161
+ try {
162
+ const modules = fs.readdirSync(nodeModulesPath);
163
+ for (const moduleName of modules) {
164
+ if (moduleName === '.bin') continue;
165
+
166
+ const modulePath = path.join(nodeModulesPath, moduleName);
167
+ const stat = fs.lstatSync(modulePath);
168
+
169
+ if (stat.isDirectory()) {
170
+ // Handle scoped packages (e.g., @robgeoltd/lib-m2)
171
+ if (moduleName.startsWith('@')) {
172
+ const scopedModules = fs.readdirSync(modulePath);
173
+ for (const scopedModule of scopedModules) {
174
+ const scopedPath = path.join(modulePath, scopedModule);
175
+ this.scanDependencies(scopedPath, depth + 1);
176
+ }
177
+ } else {
178
+ this.scanDependencies(modulePath, depth + 1);
179
+ }
180
+ }
181
+ }
182
+ } catch (error) {
183
+ console.warn(`Warning: Could not read node_modules at ${nodeModulesPath}:`, error.message);
184
+ }
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Get platform-specific configuration for cross-compilation
190
+ */
191
+ getCrossCompileEnv() {
192
+ const env = { ...process.env };
193
+
194
+ // Set target platform and architecture
195
+ if (this.targetPlatform !== process.platform) {
196
+ console.log(` Cross-compiling for platform: ${this.targetPlatform}`);
197
+ }
198
+ if (this.targetArch !== process.arch) {
199
+ console.log(` Cross-compiling for architecture: ${this.targetArch}`);
200
+ }
201
+
202
+ // Map common arch names to node-gyp format
203
+ const archMap = {
204
+ 'x64': 'x64',
205
+ 'ia32': 'ia32',
206
+ 'arm': 'arm',
207
+ 'arm64': 'arm64'
208
+ };
209
+
210
+ env.npm_config_target = this.targetNodeVersion;
211
+ env.npm_config_arch = archMap[this.targetArch] || this.targetArch;
212
+ env.npm_config_target_arch = archMap[this.targetArch] || this.targetArch;
213
+ env.npm_config_disturl = 'https://nodejs.org/dist';
214
+ env.npm_config_runtime = 'node';
215
+ env.npm_config_build_from_source = 'true';
216
+
217
+ return env;
218
+ }
219
+
220
+ /**
221
+ * Rebuild a native module using node-gyp for the target platform
222
+ */
223
+ rebuildModule(modulePath, moduleName) {
224
+ console.log(`\nRebuilding ${moduleName}...`);
225
+ console.log(` Path: ${modulePath}`);
226
+ console.log(` Target: Node.js ${this.targetNodeVersion} (${this.targetPlatform}-${this.targetArch})`);
227
+
228
+ // Determine if we should use npx node-gyp or just node-gyp
229
+ let useNpx = false;
230
+ try {
231
+ execSync('node-gyp --version', { stdio: 'pipe' });
232
+ } catch (error) {
233
+ useNpx = true;
234
+ }
235
+
236
+ const nodeGypCmd = useNpx ? 'npx node-gyp' : 'node-gyp';
237
+
238
+ try {
239
+ const env = this.getCrossCompileEnv();
240
+
241
+ // Clean previous builds
242
+ try {
243
+ execSync(`${nodeGypCmd} clean`, {
244
+ cwd: modulePath,
245
+ stdio: 'pipe',
246
+ env
247
+ });
248
+ } catch (cleanError) {
249
+ // Ignore clean errors
250
+ }
251
+
252
+ // Configure for target
253
+ const configureCmd = `${nodeGypCmd} configure --target=${this.targetNodeVersion} --arch=${this.targetArch} --dist-url=https://nodejs.org/dist`;
254
+ execSync(configureCmd, {
255
+ cwd: modulePath,
256
+ stdio: 'inherit',
257
+ env
258
+ });
259
+
260
+ // Build
261
+ execSync(`${nodeGypCmd} build`, {
262
+ cwd: modulePath,
263
+ stdio: 'inherit',
264
+ env
265
+ });
266
+
267
+ console.log(`✓ Successfully rebuilt ${moduleName}`);
268
+ return true;
269
+ } catch (error) {
270
+ console.error(`✗ Failed to rebuild ${moduleName}:`, error.message);
271
+ return false;
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Main rebuild process
277
+ */
278
+ async rebuild() {
279
+ console.log('Starting native module rebuild process...');
280
+ console.log(`Build path: ${this.buildPath}`);
281
+ console.log(`Target: Node.js ${this.targetNodeVersion} (${this.targetPlatform}-${this.targetArch})\n`);
282
+
283
+ // Verify node-gyp is available
284
+ try {
285
+ execSync('node-gyp --version', { stdio: 'pipe' });
286
+ } catch (error) {
287
+ // Try with npx
288
+ try {
289
+ execSync('npx node-gyp --version', { stdio: 'pipe' });
290
+ } catch (npxError) {
291
+ console.error('Error: node-gyp is not installed or not in PATH');
292
+ console.error('Install it with: npm install -g node-gyp');
293
+ console.error('Or ensure it is in your project dependencies');
294
+ process.exit(1);
295
+ }
296
+ }
297
+
298
+ // Scan for native modules
299
+ console.log('Scanning dependencies for native modules...\n');
300
+ this.scanDependencies(this.buildPath);
301
+
302
+ if (this.nativeModules.size === 0) {
303
+ console.log('\nNo native modules found.');
304
+ return;
305
+ }
306
+
307
+ console.log(`\nFound ${this.nativeModules.size} native module(s) to rebuild.\n`);
308
+ console.log('='.repeat(60));
309
+
310
+ // Rebuild each native module
311
+ let successCount = 0;
312
+ let failCount = 0;
313
+
314
+ for (const [modulePath, moduleName] of this.nativeModules) {
315
+ const success = this.rebuildModule(modulePath, moduleName);
316
+ if (success) {
317
+ successCount++;
318
+ } else {
319
+ failCount++;
320
+ }
321
+ }
322
+
323
+ // Summary
324
+ console.log('\n' + '='.repeat(60));
325
+ console.log('\nRebuild Summary:');
326
+ console.log(` Total modules: ${this.nativeModules.size}`);
327
+ console.log(` Successful: ${successCount}`);
328
+ console.log(` Failed: ${failCount}`);
329
+
330
+ if (failCount > 0) {
331
+ console.log('\nSome modules failed to rebuild. Check the errors above.');
332
+ process.exit(1);
333
+ } else {
334
+ console.log('\n✓ All native modules rebuilt successfully!');
335
+ }
336
+ }
337
+ }
338
+
339
+ // Main execution
340
+ if (require.main === module) {
341
+ const args = process.argv.slice(2);
342
+
343
+ // Parse command line arguments
344
+ const options = {};
345
+ let buildPath = process.cwd();
346
+
347
+ for (let i = 0; i < args.length; i++) {
348
+ const arg = args[i];
349
+
350
+ if (arg === '--target' && args[i + 1]) {
351
+ options.target = args[i + 1];
352
+ i++;
353
+ } else if (arg === '--node-version' && args[i + 1]) {
354
+ options.nodeVersion = args[i + 1];
355
+ i++;
356
+ } else if (arg === '--platform' && args[i + 1]) {
357
+ options.platform = args[i + 1];
358
+ i++;
359
+ } else if (arg === '--arch' && args[i + 1]) {
360
+ options.arch = args[i + 1];
361
+ i++;
362
+ } else if (arg === '--help' || arg === '-h') {
363
+ console.log(`
364
+ seabox-rebuild - Rebuild native modules for target Node.js version
365
+
366
+ Usage:
367
+ seabox-rebuild [options] [build-path]
368
+
369
+ Options:
370
+ --target <target> Target in format: nodeX.Y.Z-platform-arch
371
+ Example: node24.11.0-win32-x64
372
+ --node-version <version> Target Node.js version (e.g., 24.11.0)
373
+ --platform <platform> Target platform (win32, linux, darwin)
374
+ --arch <arch> Target architecture (x64, arm64, ia32)
375
+ --help, -h Show this help message
376
+
377
+ Examples:
378
+ seabox-rebuild --target node24.11.0-win32-x64
379
+ seabox-rebuild --node-version 24.11.0 --platform linux --arch x64
380
+ seabox-rebuild /path/to/project
381
+ `);
382
+ process.exit(0);
383
+ } else if (!arg.startsWith('--')) {
384
+ buildPath = arg;
385
+ }
386
+ }
387
+
388
+ const rebuilder = new NativeModuleRebuilder(buildPath, options);
389
+ rebuilder.rebuild().catch(error => {
390
+ console.error('Fatal error:', error);
391
+ process.exit(1);
392
+ });
393
+ }
394
+
395
+ module.exports = { NativeModuleRebuilder };
package/bin/seabox.js ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CLI entry point for SEA builder
5
+ */
6
+
7
+ const { build } = require('../lib');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ async function main() {
12
+ const args = process.argv.slice(2);
13
+
14
+ const configPath = args.includes('--config') ? args[args.indexOf('--config') + 1] : undefined;
15
+ const verbose = args.includes('--verbose');
16
+ const debug = args.includes('--debug');
17
+
18
+ let showHelp = args.includes('--help') || args.includes('-h');
19
+
20
+ // Try to load config to check if it exists
21
+ if (!configPath) {
22
+ const pkgPath = path.join(process.cwd(), 'package.json');
23
+ if (!fs.existsSync(pkgPath)) {
24
+ showHelp = true;
25
+ }
26
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
27
+ if (!pkg.seabox) {
28
+ showHelp = true;
29
+ }
30
+ }
31
+
32
+ // Show help
33
+ if (showHelp) {
34
+ console.log(`
35
+ SEA Builder - Node.js Single Executable Application Builder
36
+
37
+ Usage:
38
+ seabox [options]
39
+
40
+ Options:
41
+ --config <path> Path to config file (default: reads from package.json)
42
+ --verbose Enable verbose logging
43
+ --debug Keep temporary build files for debugging
44
+ --help, -h Show this help message
45
+
46
+ Configuration:
47
+ Add a "seabox" field to your package.json:
48
+ {
49
+ "seabox": {
50
+ "entry": "./out/app.js",
51
+ "assets": ["./out/**/*"],
52
+ "binaries": ["*.node", "*.dll"],
53
+ "targets": ["node24.11.0-win32-x64"],
54
+ "output": "myapp.exe",
55
+ "outputPath": "dist",
56
+ "useSnapshot": false
57
+ }
58
+ }
59
+
60
+ For more information, see the documentation.
61
+ `);
62
+
63
+ }
64
+
65
+ try {
66
+ await build({
67
+ configPath,
68
+ verbose,
69
+ debug,
70
+ projectRoot: process.cwd()
71
+ });
72
+ } catch (error) {
73
+ console.error('Build failed:', error.message);
74
+ if (verbose) {
75
+ console.error(error.stack);
76
+ }
77
+ process.exit(1);
78
+ }
79
+ }
80
+
81
+ main();