electrobun 1.7.0-beta.0 → 1.7.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli/index.ts +257 -237
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrobun",
3
- "version": "1.7.0-beta.0",
3
+ "version": "1.7.0-beta.1",
4
4
  "description": "Build ultra fast, tiny, and cross-platform desktop apps with Typescript.",
5
5
  "license": "MIT",
6
6
  "author": "Blackboard Technologies Inc.",
package/src/cli/index.ts CHANGED
@@ -1126,9 +1126,9 @@ async function createAppImage(
1126
1126
  },
1127
1127
  );
1128
1128
 
1129
- // Create AppRun script (the entry point)
1130
- const appBundleBasename = basename(resolvedAppBundlePath);
1131
- const appRunContent = `#!/bin/bash
1129
+ // Create AppRun script (the entry point)
1130
+ const appBundleBasename = basename(resolvedAppBundlePath);
1131
+ const appRunContent = `#!/bin/bash
1132
1132
  # AppRun script for ${appFileName}
1133
1133
  HERE="$(dirname "$(readlink -f "\${0}")")"
1134
1134
  EXEC="\${HERE}/usr/bin/${appBundleBasename}/bin/launcher"
@@ -1144,9 +1144,9 @@ exec "\${EXEC}" "\$@"
1144
1144
  writeFileSync(appRunPath, appRunContent);
1145
1145
  execSync(`chmod +x ${escapePathForTerminal(appRunPath)}`);
1146
1146
 
1147
- // Create .desktop file in AppDir root
1148
- // Always include Icon field since we always create an icon (either real or placeholder)
1149
- const desktopContent = `[Desktop Entry]
1147
+ // Create .desktop file in AppDir root
1148
+ // Always include Icon field since we always create an icon (either real or placeholder)
1149
+ const desktopContent = `[Desktop Entry]
1150
1150
  Version=1.0
1151
1151
  Type=Application
1152
1152
  Name=${config.app.name}
@@ -1161,7 +1161,7 @@ Categories=Utility;
1161
1161
  const desktopPath = join(appDirPath, `${appFileName}.desktop`);
1162
1162
  writeFileSync(desktopPath, desktopContent);
1163
1163
 
1164
- // Copy icon if available, or create a minimal placeholder
1164
+ // Copy icon if available, or create a minimal placeholder
1165
1165
  if (
1166
1166
  config.build.linux?.icon &&
1167
1167
  existsSync(join(projectRoot, config.build.linux.icon))
@@ -1181,72 +1181,72 @@ Categories=Utility;
1181
1181
  // Create a minimal 1x1 transparent PNG as placeholder to satisfy appimagetool
1182
1182
  // This prevents "Icon entry not found" errors
1183
1183
  const placeholderPNG = Buffer.from([
1184
- 0x89,
1185
- 0x50,
1186
- 0x4e,
1187
- 0x47,
1188
- 0x0d,
1189
- 0x0a,
1190
- 0x1a,
1191
- 0x0a, // PNG signature
1192
- 0x00,
1193
- 0x00,
1194
- 0x00,
1195
- 0x0d,
1196
- 0x49,
1197
- 0x48,
1198
- 0x44,
1199
- 0x52, // IHDR chunk
1200
- 0x00,
1201
- 0x00,
1202
- 0x00,
1203
- 0x01,
1204
- 0x00,
1205
- 0x00,
1206
- 0x00,
1207
- 0x01, // 1x1 dimensions
1208
- 0x08,
1209
- 0x06,
1210
- 0x00,
1211
- 0x00,
1212
- 0x00,
1213
- 0x1f,
1214
- 0x15,
1215
- 0xc4, // 8-bit RGBA
1216
- 0x89,
1217
- 0x00,
1218
- 0x00,
1219
- 0x00,
1220
- 0x0b,
1221
- 0x49,
1222
- 0x44,
1223
- 0x41, // IDAT chunk
1224
- 0x54,
1225
- 0x08,
1226
- 0x99,
1227
- 0x01,
1228
- 0x00,
1229
- 0x00,
1230
- 0x05,
1231
- 0x00,
1232
- 0x01,
1233
- 0x06,
1234
- 0x7a,
1235
- 0x81,
1236
- 0x7c,
1237
- 0x00,
1238
- 0x00,
1239
- 0x00, // IEND chunk
1240
- 0x00,
1241
- 0x49,
1242
- 0x45,
1243
- 0x4e,
1244
- 0x44,
1245
- 0xae,
1246
- 0x42,
1247
- 0x60,
1248
- 0x82,
1249
- ]);
1184
+ 0x89,
1185
+ 0x50,
1186
+ 0x4e,
1187
+ 0x47,
1188
+ 0x0d,
1189
+ 0x0a,
1190
+ 0x1a,
1191
+ 0x0a, // PNG signature
1192
+ 0x00,
1193
+ 0x00,
1194
+ 0x00,
1195
+ 0x0d,
1196
+ 0x49,
1197
+ 0x48,
1198
+ 0x44,
1199
+ 0x52, // IHDR chunk
1200
+ 0x00,
1201
+ 0x00,
1202
+ 0x00,
1203
+ 0x01,
1204
+ 0x00,
1205
+ 0x00,
1206
+ 0x00,
1207
+ 0x01, // 1x1 dimensions
1208
+ 0x08,
1209
+ 0x06,
1210
+ 0x00,
1211
+ 0x00,
1212
+ 0x00,
1213
+ 0x1f,
1214
+ 0x15,
1215
+ 0xc4, // 8-bit RGBA
1216
+ 0x89,
1217
+ 0x00,
1218
+ 0x00,
1219
+ 0x00,
1220
+ 0x0b,
1221
+ 0x49,
1222
+ 0x44,
1223
+ 0x41, // IDAT chunk
1224
+ 0x54,
1225
+ 0x08,
1226
+ 0x99,
1227
+ 0x01,
1228
+ 0x00,
1229
+ 0x00,
1230
+ 0x05,
1231
+ 0x00,
1232
+ 0x01,
1233
+ 0x06,
1234
+ 0x7a,
1235
+ 0x81,
1236
+ 0x7c,
1237
+ 0x00,
1238
+ 0x00,
1239
+ 0x00, // IEND chunk
1240
+ 0x00,
1241
+ 0x49,
1242
+ 0x45,
1243
+ 0x4e,
1244
+ 0x44,
1245
+ 0xae,
1246
+ 0x42,
1247
+ 0x60,
1248
+ 0x82,
1249
+ ]);
1250
1250
 
1251
1251
  const iconDestPath = join(appDirPath, `${appFileName}.png`);
1252
1252
  const dirIconPath = join(appDirPath, ".DirIcon");
@@ -1259,18 +1259,18 @@ Categories=Utility;
1259
1259
  );
1260
1260
  }
1261
1261
 
1262
- // Generate the AppImage using appimagetool
1262
+ // Generate the AppImage using appimagetool
1263
1263
  const appImagePath = join(buildFolder, `${appFileName}.AppImage`);
1264
1264
  if (existsSync(appImagePath)) {
1265
1265
  unlinkSync(appImagePath);
1266
1266
  }
1267
1267
 
1268
- // console.log(`DEBUG: AppDir path: ${appDirPath}`);
1269
- // console.log(`DEBUG: Does AppDir exist? ${existsSync(appDirPath)}`);
1268
+ // console.log(`DEBUG: AppDir path: ${appDirPath}`);
1269
+ // console.log(`DEBUG: Does AppDir exist? ${existsSync(appDirPath)}`);
1270
1270
  console.log(`Generating AppImage: ${appImagePath}`);
1271
1271
  const appImageArch = ARCH === "arm64" ? "aarch64" : "x86_64";
1272
1272
 
1273
- // Use full path to appimagetool if not in PATH
1273
+ // Use full path to appimagetool if not in PATH
1274
1274
  let appimagetoolBase = "appimagetool";
1275
1275
  try {
1276
1276
  execSync("which appimagetool", { stdio: "ignore" });
@@ -1287,7 +1287,7 @@ Categories=Utility;
1287
1287
  }
1288
1288
  }
1289
1289
 
1290
- // Get the command with proper environment for vendored libfuse2
1290
+ // Get the command with proper environment for vendored libfuse2
1291
1291
  const appimagetoolCmd = getAppImageToolCommand().replace(
1292
1292
  "appimagetool",
1293
1293
  appimagetoolBase,
@@ -1311,14 +1311,14 @@ Categories=Utility;
1311
1311
  throw error;
1312
1312
  }
1313
1313
 
1314
- // Verify the AppImage was created
1314
+ // Verify the AppImage was created
1315
1315
  if (!existsSync(appImagePath)) {
1316
1316
  throw new Error(
1317
1317
  `AppImage was not created at expected path: ${appImagePath}`,
1318
1318
  );
1319
1319
  }
1320
1320
 
1321
- // Extract and copy icon for desktop shortcut
1321
+ // Extract and copy icon for desktop shortcut
1322
1322
  const iconExtractPath = join(buildFolder, `${appFileName}.png`);
1323
1323
  if (
1324
1324
  config.build.linux?.icon &&
@@ -1330,23 +1330,23 @@ Categories=Utility;
1330
1330
  } else {
1331
1331
  // Create placeholder icon for desktop shortcut
1332
1332
  const placeholderPNG = Buffer.from([
1333
- 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
1334
- 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
1335
- 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00,
1336
- 0x0b, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x01, 0x00, 0x00, 0x05, 0x00,
1337
- 0x01, 0x06, 0x7a, 0x81, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
1338
- 0x44, 0xae, 0x42, 0x60, 0x82,
1339
- ]);
1333
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
1334
+ 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
1335
+ 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00,
1336
+ 0x0b, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x01, 0x00, 0x00, 0x05, 0x00,
1337
+ 0x01, 0x06, 0x7a, 0x81, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
1338
+ 0x44, 0xae, 0x42, 0x60, 0x82,
1339
+ ]);
1340
1340
  writeFileSync(iconExtractPath, new Uint8Array(placeholderPNG));
1341
1341
  console.log(
1342
1342
  `✓ Created placeholder icon for desktop shortcut: ${iconExtractPath}`,
1343
1343
  );
1344
1344
  }
1345
1345
 
1346
- // Create desktop shortcut alongside the AppImage
1346
+ // Create desktop shortcut alongside the AppImage
1347
1347
  const desktopShortcutPath = join(buildFolder, `${appFileName}.desktop`);
1348
1348
 
1349
- const desktopShortcutContent = `[Desktop Entry]
1349
+ const desktopShortcutContent = `[Desktop Entry]
1350
1350
  Version=1.0
1351
1351
  Type=Application
1352
1352
  Name=${config.app.name}
@@ -2661,20 +2661,36 @@ ${schemesXml}
2661
2661
  }
2662
2662
  }
2663
2663
 
2664
- // All the unique files are in the bundle now. Create an initial temporary tar file
2665
- // for hashing the contents
2666
- // tar the signed and notarized app bundle
2667
- // Use sanitized appFileName for tarball paths (URL-safe), but tar content uses actual bundle folder
2668
- const tmpTarPath = join(
2669
- buildFolder,
2670
- `${appFileName}${targetOS === "macos" ? ".app" : ""}-temp.tar`,
2671
- );
2672
- createTar(tmpTarPath, buildFolder, [basename(appBundleFolderPath)]);
2673
- const tmpTarBuffer = await Bun.file(tmpTarPath).arrayBuffer();
2674
- // Note: wyhash is the default in Bun.hash but that may change in the future
2675
- // so we're being explicit here.
2676
- const hash = Bun.hash.wyhash(tmpTarBuffer, 43770n).toString(36);
2677
- unlinkSync(tmpTarPath);
2664
+ // Create a content hash for version.json. In non-dev builds this is used
2665
+ // by the updater to detect changes. For dev builds we skip it since
2666
+ // the updater isn't relevant.
2667
+ let hash: string;
2668
+ if (buildEnvironment === "dev") {
2669
+ hash = "dev";
2670
+ } else {
2671
+ // Walk the app bundle and create an in-memory tar for hashing
2672
+ // (no temp file on disk). This runs after ASAR packing so the
2673
+ // hash reflects the final shipped bundle contents.
2674
+ console.time("Generate Bundle hash");
2675
+ const bundleFiles: Record<string, Blob> = {};
2676
+ const bundleBase = basename(appBundleFolderPath);
2677
+ const entries = readdirSync(appBundleFolderPath, {
2678
+ recursive: true,
2679
+ });
2680
+ for (const entry of entries) {
2681
+ const entryPath = entry.toString();
2682
+ const fullPath = join(appBundleFolderPath, entryPath);
2683
+ if (statSync(fullPath).isFile()) {
2684
+ bundleFiles[join(bundleBase, entryPath)] = Bun.file(fullPath);
2685
+ }
2686
+ }
2687
+ const archiveBytes = await new Bun.Archive(bundleFiles).bytes();
2688
+ // Note: wyhash is the default in Bun.hash but that may change in the future
2689
+ // so we're being explicit here.
2690
+ hash = Bun.hash.wyhash(archiveBytes, 43770n).toString(36);
2691
+ console.timeEnd("Generate Bundle hash");
2692
+ }
2693
+
2678
2694
  // const bunVersion = execSync(`${bunBinarySourcePath} --version`).toString().trim();
2679
2695
 
2680
2696
  // version.json inside the app bundle
@@ -2820,9 +2836,7 @@ ${schemesXml}
2820
2836
  );
2821
2837
 
2822
2838
  const appImageTarPath = join(buildFolder, `${appFileName}.tar`);
2823
- console.log(
2824
- `Creating tar of installer contents: ${appImageTarPath}`,
2825
- );
2839
+ console.log(`Creating tar of installer contents: ${appImageTarPath}`);
2826
2840
 
2827
2841
  // Tar the inner directory
2828
2842
  createTar(appImageTarPath, tempDirPath, [appFileName]);
@@ -2965,15 +2979,15 @@ ${schemesXml}
2965
2979
  const decompressResult = Bun.spawnSync(
2966
2980
  [
2967
2981
  zstdPath,
2968
- "decompress",
2969
- "-i",
2970
- prevVersionCompressedTarballPath,
2971
- "-o",
2972
- prevTarballPath,
2973
- ],
2974
- {
2975
- cwd: buildFolder,
2976
- stdout: "inherit",
2982
+ "decompress",
2983
+ "-i",
2984
+ prevVersionCompressedTarballPath,
2985
+ "-o",
2986
+ prevTarballPath,
2987
+ ],
2988
+ {
2989
+ cwd: buildFolder,
2990
+ stdout: "inherit",
2977
2991
  stderr: "inherit",
2978
2992
  },
2979
2993
  );
@@ -3209,7 +3223,10 @@ ${schemesXml}
3209
3223
  )} -ov -format ULFO ${escapePathForTerminal(dmgCreationPath)}`,
3210
3224
  );
3211
3225
 
3212
- if (buildEnvironment === "stable" && dmgCreationPath !== finalDmgPath) {
3226
+ if (
3227
+ buildEnvironment === "stable" &&
3228
+ dmgCreationPath !== finalDmgPath
3229
+ ) {
3213
3230
  renameSync(dmgCreationPath, finalDmgPath);
3214
3231
  }
3215
3232
  artifactsToUpload.push(finalDmgPath);
@@ -3320,7 +3337,10 @@ ${schemesXml}
3320
3337
 
3321
3338
  artifactsToUpload.forEach((filePath) => {
3322
3339
  const filename = basename(filePath);
3323
- const destination = join(artifactFolder, `${platformPrefix}-${filename}`);
3340
+ const destination = join(
3341
+ artifactFolder,
3342
+ `${platformPrefix}-${filename}`,
3343
+ );
3324
3344
  try {
3325
3345
  renameSync(filePath, destination);
3326
3346
  } catch {
@@ -3862,57 +3882,57 @@ ${schemesXml}
3862
3882
  const wrapperAppDirPath = join(buildFolder, `${wrapperName}.AppDir`);
3863
3883
 
3864
3884
  // Clean up any existing AppDir
3865
- if (existsSync(wrapperAppDirPath)) {
3866
- rmSync(wrapperAppDirPath, { recursive: true, force: true });
3867
- }
3868
- mkdirSync(wrapperAppDirPath, { recursive: true });
3885
+ if (existsSync(wrapperAppDirPath)) {
3886
+ rmSync(wrapperAppDirPath, { recursive: true, force: true });
3887
+ }
3888
+ mkdirSync(wrapperAppDirPath, { recursive: true });
3869
3889
 
3870
- try {
3871
- // Create usr/bin directory structure
3872
- const usrBinPath = join(wrapperAppDirPath, "usr", "bin");
3873
- mkdirSync(usrBinPath, { recursive: true });
3890
+ try {
3891
+ // Create usr/bin directory structure
3892
+ const usrBinPath = join(wrapperAppDirPath, "usr", "bin");
3893
+ mkdirSync(usrBinPath, { recursive: true });
3874
3894
 
3875
- // Create self-extracting binary with embedded archive (following magic markers pattern)
3876
- const targetPaths = getPlatformPaths("linux", ARCH);
3895
+ // Create self-extracting binary with embedded archive (following magic markers pattern)
3896
+ const targetPaths = getPlatformPaths("linux", ARCH);
3877
3897
 
3878
- // Read the extractor binary
3879
- const extractorBinary = readFileSync(targetPaths.EXTRACTOR);
3898
+ // Read the extractor binary
3899
+ const extractorBinary = readFileSync(targetPaths.EXTRACTOR);
3880
3900
 
3881
- // Read the compressed archive
3882
- const compressedArchive = readFileSync(compressedTarPath);
3901
+ // Read the compressed archive
3902
+ const compressedArchive = readFileSync(compressedTarPath);
3883
3903
 
3884
- // Create metadata JSON
3885
- const metadata = {
3886
- identifier: config.app.identifier,
3887
- name: config.app.name,
3888
- channel: buildEnvironment,
3889
- hash: hash,
3890
- };
3891
- const metadataJson = JSON.stringify(metadata);
3892
- const metadataBuffer = Buffer.from(metadataJson, "utf8");
3893
-
3894
- // Create marker buffers
3895
- const metadataMarker = Buffer.from("ELECTROBUN_METADATA_V1", "utf8");
3896
- const archiveMarker = Buffer.from("ELECTROBUN_ARCHIVE_V1", "utf8");
3897
-
3898
- // Combine extractor + metadata marker + metadata + archive marker + archive
3899
- const combinedBuffer = Buffer.concat([
3900
- new Uint8Array(extractorBinary),
3901
- new Uint8Array(metadataMarker),
3902
- new Uint8Array(metadataBuffer),
3903
- new Uint8Array(archiveMarker),
3904
- new Uint8Array(compressedArchive),
3905
- ]);
3906
-
3907
- // Write the self-extracting binary to AppImage/usr/bin/
3908
- const wrapperExtractorPath = join(usrBinPath, wrapperName);
3909
- writeFileSync(wrapperExtractorPath, new Uint8Array(combinedBuffer), {
3910
- mode: 0o755,
3911
- });
3912
- execSync(`chmod +x ${escapePathForTerminal(wrapperExtractorPath)}`);
3904
+ // Create metadata JSON
3905
+ const metadata = {
3906
+ identifier: config.app.identifier,
3907
+ name: config.app.name,
3908
+ channel: buildEnvironment,
3909
+ hash: hash,
3910
+ };
3911
+ const metadataJson = JSON.stringify(metadata);
3912
+ const metadataBuffer = Buffer.from(metadataJson, "utf8");
3913
+
3914
+ // Create marker buffers
3915
+ const metadataMarker = Buffer.from("ELECTROBUN_METADATA_V1", "utf8");
3916
+ const archiveMarker = Buffer.from("ELECTROBUN_ARCHIVE_V1", "utf8");
3917
+
3918
+ // Combine extractor + metadata marker + metadata + archive marker + archive
3919
+ const combinedBuffer = Buffer.concat([
3920
+ new Uint8Array(extractorBinary),
3921
+ new Uint8Array(metadataMarker),
3922
+ new Uint8Array(metadataBuffer),
3923
+ new Uint8Array(archiveMarker),
3924
+ new Uint8Array(compressedArchive),
3925
+ ]);
3926
+
3927
+ // Write the self-extracting binary to AppImage/usr/bin/
3928
+ const wrapperExtractorPath = join(usrBinPath, wrapperName);
3929
+ writeFileSync(wrapperExtractorPath, new Uint8Array(combinedBuffer), {
3930
+ mode: 0o755,
3931
+ });
3932
+ execSync(`chmod +x ${escapePathForTerminal(wrapperExtractorPath)}`);
3913
3933
 
3914
- // Create AppRun script
3915
- const appRunContent = `#!/bin/bash
3934
+ // Create AppRun script
3935
+ const appRunContent = `#!/bin/bash
3916
3936
  # AppRun script for ${wrapperName}
3917
3937
  HERE="$(dirname "$(readlink -f "\${0}")")"
3918
3938
  EXEC="\${HERE}/usr/bin/${wrapperName}"
@@ -3921,17 +3941,17 @@ EXEC="\${HERE}/usr/bin/${wrapperName}"
3921
3941
  exec "\${EXEC}" "\$@"
3922
3942
  `;
3923
3943
 
3924
- const appRunPath = join(wrapperAppDirPath, "AppRun");
3925
- writeFileSync(appRunPath, appRunContent);
3926
- execSync(`chmod +x ${escapePathForTerminal(appRunPath)}`);
3944
+ const appRunPath = join(wrapperAppDirPath, "AppRun");
3945
+ writeFileSync(appRunPath, appRunContent);
3946
+ execSync(`chmod +x ${escapePathForTerminal(appRunPath)}`);
3927
3947
 
3928
- // Check if icon will be available
3929
- const hasWrapperIcon =
3930
- config.build.linux?.icon &&
3931
- existsSync(join(projectRoot, config.build.linux.icon));
3948
+ // Check if icon will be available
3949
+ const hasWrapperIcon =
3950
+ config.build.linux?.icon &&
3951
+ existsSync(join(projectRoot, config.build.linux.icon));
3932
3952
 
3933
- // Create desktop file
3934
- const desktopContent = `[Desktop Entry]
3953
+ // Create desktop file
3954
+ const desktopContent = `[Desktop Entry]
3935
3955
  Version=1.0
3936
3956
  Type=Application
3937
3957
  Name=${config.app.name} Installer
@@ -3941,88 +3961,88 @@ Terminal=false
3941
3961
  Categories=Utility;
3942
3962
  `;
3943
3963
 
3944
- const desktopPath = join(wrapperAppDirPath, `${wrapperName}.desktop`);
3945
- writeFileSync(desktopPath, desktopContent);
3964
+ const desktopPath = join(wrapperAppDirPath, `${wrapperName}.desktop`);
3965
+ writeFileSync(desktopPath, desktopContent);
3946
3966
 
3947
- // Copy icon if available
3948
- if (hasWrapperIcon) {
3949
- const iconSourcePath = join(projectRoot, config.build.linux.icon);
3950
- const iconDestPath = join(wrapperAppDirPath, `${wrapperName}.png`);
3951
- const dirIconPath = join(wrapperAppDirPath, ".DirIcon");
3967
+ // Copy icon if available
3968
+ if (hasWrapperIcon) {
3969
+ const iconSourcePath = join(projectRoot, config.build.linux.icon);
3970
+ const iconDestPath = join(wrapperAppDirPath, `${wrapperName}.png`);
3971
+ const dirIconPath = join(wrapperAppDirPath, ".DirIcon");
3952
3972
 
3953
- cpSync(iconSourcePath, iconDestPath, { dereference: true });
3954
- cpSync(iconSourcePath, dirIconPath, { dereference: true });
3973
+ cpSync(iconSourcePath, iconDestPath, { dereference: true });
3974
+ cpSync(iconSourcePath, dirIconPath, { dereference: true });
3955
3975
 
3956
- console.log(
3957
- `Copied icon for wrapper AppImage: ${iconSourcePath} -> ${iconDestPath}`,
3958
- );
3959
- }
3976
+ console.log(
3977
+ `Copied icon for wrapper AppImage: ${iconSourcePath} -> ${iconDestPath}`,
3978
+ );
3979
+ }
3960
3980
 
3961
- // Ensure appimagetool is available
3962
- await ensureAppImageTooling();
3981
+ // Ensure appimagetool is available
3982
+ await ensureAppImageTooling();
3963
3983
 
3964
- // Generate the wrapper AppImage
3965
- if (existsSync(wrapperAppImagePath)) {
3966
- unlinkSync(wrapperAppImagePath);
3967
- }
3984
+ // Generate the wrapper AppImage
3985
+ if (existsSync(wrapperAppImagePath)) {
3986
+ unlinkSync(wrapperAppImagePath);
3987
+ }
3968
3988
 
3969
- console.log(`Creating wrapper AppImage: ${wrapperAppImagePath}`);
3970
- const appImageArch = ARCH === "arm64" ? "aarch64" : "x86_64";
3989
+ console.log(`Creating wrapper AppImage: ${wrapperAppImagePath}`);
3990
+ const appImageArch = ARCH === "arm64" ? "aarch64" : "x86_64";
3971
3991
 
3972
- // Use appimagetool to create the wrapper AppImage
3973
- let appimagetoolBase = "appimagetool";
3974
- try {
3975
- execSync("which appimagetool", { stdio: "ignore" });
3976
- } catch {
3977
- const localBinPath = join(
3978
- process.env["HOME"] || "",
3979
- ".local",
3980
- "bin",
3992
+ // Use appimagetool to create the wrapper AppImage
3993
+ let appimagetoolBase = "appimagetool";
3994
+ try {
3995
+ execSync("which appimagetool", { stdio: "ignore" });
3996
+ } catch {
3997
+ const localBinPath = join(
3998
+ process.env["HOME"] || "",
3999
+ ".local",
4000
+ "bin",
4001
+ "appimagetool",
4002
+ );
4003
+ if (existsSync(localBinPath)) {
4004
+ appimagetoolBase = localBinPath;
4005
+ }
4006
+ }
4007
+
4008
+ // Get the command with proper environment for vendored libfuse2
4009
+ const appimagetoolCmd = getAppImageToolCommand().replace(
3981
4010
  "appimagetool",
4011
+ appimagetoolBase,
3982
4012
  );
3983
- if (existsSync(localBinPath)) {
3984
- appimagetoolBase = localBinPath;
3985
- }
3986
- }
3987
4013
 
3988
- // Get the command with proper environment for vendored libfuse2
3989
- const appimagetoolCmd = getAppImageToolCommand().replace(
3990
- "appimagetool",
3991
- appimagetoolBase,
3992
- );
4014
+ try {
4015
+ execSync(
4016
+ `ARCH=${appImageArch} ${appimagetoolCmd} --no-appstream ${escapePathForTerminal(wrapperAppDirPath)} ${escapePathForTerminal(wrapperAppImagePath)}`,
4017
+ {
4018
+ stdio: "inherit",
4019
+ env: { ...process.env, ARCH: appImageArch },
4020
+ },
4021
+ );
4022
+ } catch (error) {
4023
+ console.error("Failed to create wrapper AppImage:", error);
4024
+ throw error;
4025
+ }
3993
4026
 
3994
- try {
3995
- execSync(
3996
- `ARCH=${appImageArch} ${appimagetoolCmd} --no-appstream ${escapePathForTerminal(wrapperAppDirPath)} ${escapePathForTerminal(wrapperAppImagePath)}`,
3997
- {
3998
- stdio: "inherit",
3999
- env: { ...process.env, ARCH: appImageArch },
4000
- },
4001
- );
4002
- } catch (error) {
4003
- console.error("Failed to create wrapper AppImage:", error);
4004
- throw error;
4005
- }
4027
+ // Verify the wrapper AppImage was created
4028
+ if (!existsSync(wrapperAppImagePath)) {
4029
+ throw new Error(
4030
+ `Wrapper AppImage was not created at expected path: ${wrapperAppImagePath}`,
4031
+ );
4032
+ }
4006
4033
 
4007
- // Verify the wrapper AppImage was created
4008
- if (!existsSync(wrapperAppImagePath)) {
4009
- throw new Error(
4010
- `Wrapper AppImage was not created at expected path: ${wrapperAppImagePath}`,
4034
+ const stats = statSync(wrapperAppImagePath);
4035
+ console.log(
4036
+ `✓ Linux wrapper AppImage created: ${wrapperAppImagePath} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`,
4011
4037
  );
4012
- }
4013
-
4014
- const stats = statSync(wrapperAppImagePath);
4015
- console.log(
4016
- `✓ Linux wrapper AppImage created: ${wrapperAppImagePath} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`,
4017
- );
4018
4038
 
4019
- return wrapperAppImagePath;
4020
- } finally {
4021
- if (existsSync(wrapperAppDirPath)) {
4022
- rmSync(wrapperAppDirPath, { recursive: true, force: true });
4039
+ return wrapperAppImagePath;
4040
+ } finally {
4041
+ if (existsSync(wrapperAppDirPath)) {
4042
+ rmSync(wrapperAppDirPath, { recursive: true, force: true });
4043
+ }
4023
4044
  }
4024
4045
  }
4025
- }
4026
4046
 
4027
4047
  function codesignAppBundle(
4028
4048
  appBundleOrDmgPath: string,