electrobun 1.16.0 → 1.16.1-beta.0
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 +36 -2
- package/dist/api/bun/core/BrowserView.ts +30 -2
- package/dist/api/bun/core/Socket.ts +8 -0
- package/dist/api/bun/proc/native.ts +1 -1
- package/package.json +1 -1
- package/src/cli/index.ts +133 -7
package/README.md
CHANGED
|
@@ -27,9 +27,43 @@ Visit <a href="https://blackboard.sh/electrobun/">https://blackboard.sh/electrob
|
|
|
27
27
|
- Provide everything you need in one tightly integrated workflow to start writing code in 5 minutes and distribute in 10.
|
|
28
28
|
|
|
29
29
|
## Apps Built with Electrobun
|
|
30
|
+
- [24agents](https://github.com/jhsu/24agents) - Hyperprompter
|
|
31
|
+
- [act-track-ai](https://github.com/IrdanGu/act-track-ai) - personal desktop productivity tracker
|
|
32
|
+
- [Agents Council](https://github.com/MrLesk/agents-council) - agent-to-agent MCP communication tool for feedback requests
|
|
33
|
+
- [ai-wrapped](https://github.com/gulivan/ai-wrapped) - Wrapped-style desktop dashboard for your AI coding agent activity
|
|
30
34
|
- [Audio TTS](https://github.com/blackboardsh/audio-tts) - desktop text-to-speech app using Qwen3-TTS for voice design, cloning, and generation
|
|
35
|
+
- [aueio-player-desktop](https://github.com/tuomashatakka/aueio-player-desktop) - beautiful, minimal cross-platform audio player
|
|
36
|
+
- [bestdiff](https://github.com/tesmond/bestdiff) - a git diff checker with curved connectors
|
|
37
|
+
- [BuddyWriter](https://github.com/OxFrancesco/BuddyWriter) - BuddyWriter desktop and mobile apps
|
|
38
|
+
- [burns](https://github.com/l3wi/burns) - a Smithers manager
|
|
39
|
+
- [cbx-tool](https://github.com/jebin2/cbx-tool) - desktop app for reading and editing comic book archives (.cbz/.cbr)
|
|
31
40
|
- [Co(lab)](https://blackboard.sh/colab/) - a hybrid web browser + code editor for deep work
|
|
41
|
+
- [codlogs](https://github.com/tobitege/codlogs) - search and export local Codex sessions via CLI or desktop app
|
|
42
|
+
- [Codex Agents Composer](https://github.com/MrLesk/codex-agents-composer) - desktop app for managing your Codex agents and their skills
|
|
43
|
+
- [codex-devtools](https://github.com/gulivan/codex-devtools) - desktop inspector for Codex session data; browse conversations, search messages, and analyze agent activity
|
|
44
|
+
- [Deskdown](https://github.com/guarana-studio/deskdown) - transform any web address into a desktop app in under 20 seconds
|
|
45
|
+
- [dev-3.0](https://github.com/h0x91b/dev-3.0) - helps you not get lost while managing multiple AI agents across projects
|
|
32
46
|
- [DOOM](https://github.com/blackboardsh/electrobun-doom) - DOOM implemented in 2 ways: bun -> (c doom -> bundled wgpu) and (full ts port bun -> bundled wgpu)
|
|
47
|
+
- [electrobun-rms](https://github.com/khanhthanhdev/electrobun-rms) - fast Electrobun desktop app template with React, Tailwind CSS, and Vite
|
|
48
|
+
- [golb](https://github.com/chrisdadev13/golb) - desktop AI coding workspace built with React, Vite, and Tailwind
|
|
49
|
+
- [GOG Achievements GUI](https://github.com/timendum/gog-achievements-gui) - desktop app for managing GOG achievements
|
|
50
|
+
- [groov](https://github.com/laurenzcodes/groov) - desktop audio deck monitor
|
|
51
|
+
- [Guerilla Glass](https://github.com/okikeSolutions/guerillaglass) - open-source cross-platform creator studio for fast Record -> Edit -> Deliver workflows
|
|
52
|
+
- [Marginalia](https://github.com/lars-hoeijmans/Marginalia) - a simple note taking app
|
|
53
|
+
- [md-browse](https://github.com/needle-tools/md-browse) - a markdown-first browser that converts web pages to clean markdown
|
|
54
|
+
- [peekachu](https://github.com/needle-tools/peekachu) - password manager for AIs; store secrets in your OS keychain and scrub output so AI assistants never see actual values
|
|
55
|
+
- [PLEXI](https://github.com/ianjamesburke/PLEXI) - a multi-dimensional terminal multiplexer for the agentic era
|
|
56
|
+
- [Prometheus](https://github.com/opensourcectl/prometheus) - desktop utility toolbox for file cleanup, document manipulation, and image processing
|
|
57
|
+
- [Quiver](https://ataraxy-labs.github.io/quiver/) - desktop app for GitHub PR reviews, merge conflict resolution, and AI commit messages
|
|
58
|
+
- [remotecode.io](https://github.com/samuelfaj/remotecode.io) - continue local AI coding sessions (Claude Code or Codex) from your mobile device
|
|
59
|
+
- [sirene](https://github.com/KevinBonnoron/sirene) - self-hosted multi-backend text-to-speech platform with voice cloning
|
|
60
|
+
- [StoryForge](https://github.com/vrrdnt/StoryForge) - desktop app for Vintage Story players to switch between game versions, modpacks, servers, and accounts
|
|
61
|
+
- [Tensamin Client](https://github.com/Tensamin/Client) - web, desktop, and mobile app for accessing Tensamin
|
|
62
|
+
- [tokenpass-desktop](https://github.com/b-open-io/tokenpass-desktop) - desktop app that runs the Sigma Identity stack locally for Bitcoin-backed authentication
|
|
63
|
+
- [typsmthng-desktop](https://github.com/aaditagrawal/typsmthng-desktop) - experimental desktop typing application
|
|
64
|
+
- [VibesOS](https://github.com/popmechanic/VibesOS) - A GUI for Claude Code that makes it easy to vibe code simple, un-hackable apps
|
|
65
|
+
- [VoiceVault](https://github.com/PJH720/VoiceVault) - AI-powered voice recorder with transcription, summarization, and RAG search
|
|
66
|
+
- [warren](https://github.com/Loa212/warren) - open-source, peer-to-peer terminal mesh for accessing your machines from any device without SSH keys or config files
|
|
33
67
|
|
|
34
68
|
### Video Demos
|
|
35
69
|
|
|
@@ -41,12 +75,12 @@ Visit <a href="https://blackboard.sh/electrobun/">https://blackboard.sh/electrob
|
|
|
41
75
|
|
|
42
76
|
## Star History
|
|
43
77
|
|
|
44
|
-
[](https://www.star-history.com/#blackboardsh/electrobun&type=date&legend=top-left)
|
|
45
79
|
|
|
46
80
|
## Contributing
|
|
47
81
|
Ways to get involved:
|
|
48
82
|
|
|
49
|
-
- Follow us on X for updates <a href="https://twitter.com/BlackboardTech">@BlackboardTech</a> or <a href="https://bsky.app/profile/yoav.codes">@yoav.codes</a>
|
|
83
|
+
- Follow us on X for updates <a href="https://twitter.com/BlackboardTech">@BlackboardTech</a> and <a href="https://twitter.com/YoavCodes">@YoavCodes</a> or on bluesky <a href="https://bsky.app/profile/yoav.codes">@yoav.codes</a>
|
|
50
84
|
- Join the conversation on <a href="https://discord.gg/ueKE4tjaCE">Discord</a>
|
|
51
85
|
- Create and participate in Github issues and discussions
|
|
52
86
|
- Let me know what you're building with Electrobun
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
} from "../../shared/rpc.js";
|
|
10
10
|
import { Updater } from "./Updater";
|
|
11
11
|
import { BuildConfig } from "./BuildConfig";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
rpcPort,
|
|
14
|
+
sendMessageToWebviewViaSocket,
|
|
15
|
+
removeSocketForWebview,
|
|
16
|
+
} from "./Socket";
|
|
13
17
|
import { randomBytes } from "crypto";
|
|
14
18
|
import { type Pointer } from "bun:ffi";
|
|
15
19
|
|
|
@@ -102,6 +106,7 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
102
106
|
sandbox: boolean = false;
|
|
103
107
|
startTransparent: boolean = false;
|
|
104
108
|
startPassthrough: boolean = false;
|
|
109
|
+
isRemoved: boolean = false;
|
|
105
110
|
|
|
106
111
|
constructor(options: Partial<BrowserViewOptions<T>> = defaultOptions) {
|
|
107
112
|
// const rpc = options.rpc;
|
|
@@ -211,6 +216,9 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
211
216
|
// so we have to chunk it
|
|
212
217
|
// TODO: is this still needed after switching from named pipes
|
|
213
218
|
executeJavascript(js: string) {
|
|
219
|
+
if (!this.ptr || this.isRemoved) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
214
222
|
ffi.request.evaluateJavascriptWithNoCompletion({ id: this.id, js });
|
|
215
223
|
}
|
|
216
224
|
|
|
@@ -315,6 +323,9 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
315
323
|
|
|
316
324
|
return {
|
|
317
325
|
send(message: any) {
|
|
326
|
+
if (!that.ptr || that.isRemoved) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
318
329
|
const sentOverSocket = sendMessageToWebviewViaSocket(that.id, message);
|
|
319
330
|
|
|
320
331
|
if (!sentOverSocket) {
|
|
@@ -327,14 +338,31 @@ export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
|
327
338
|
}
|
|
328
339
|
},
|
|
329
340
|
registerHandler(handler: (msg: unknown) => void) {
|
|
341
|
+
if (that.isRemoved) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
330
344
|
that.rpcHandler = handler;
|
|
331
345
|
},
|
|
332
346
|
};
|
|
333
347
|
};
|
|
334
348
|
|
|
335
349
|
remove() {
|
|
336
|
-
|
|
350
|
+
if (!this.ptr || this.isRemoved) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const ptr = this.ptr;
|
|
354
|
+
this.isRemoved = true;
|
|
355
|
+
// Drop JS-side references first so late callbacks cannot target a stale view.
|
|
337
356
|
delete BrowserViewMap[this.id];
|
|
357
|
+
removeSocketForWebview(this.id);
|
|
358
|
+
this.rpc?.setTransport({
|
|
359
|
+
send() {},
|
|
360
|
+
registerHandler() {},
|
|
361
|
+
unregisterHandler() {},
|
|
362
|
+
});
|
|
363
|
+
this.rpcHandler = undefined;
|
|
364
|
+
this.ptr = null as any;
|
|
365
|
+
native.symbols.webviewRemove(ptr);
|
|
338
366
|
}
|
|
339
367
|
|
|
340
368
|
static getById(id: number) {
|
|
@@ -47,6 +47,14 @@ export const socketMap: {
|
|
|
47
47
|
};
|
|
48
48
|
} = {};
|
|
49
49
|
|
|
50
|
+
export const removeSocketForWebview = (webviewId: number) => {
|
|
51
|
+
const rpc = socketMap[webviewId];
|
|
52
|
+
if (!rpc) return;
|
|
53
|
+
|
|
54
|
+
rpc.socket = null;
|
|
55
|
+
delete socketMap[webviewId];
|
|
56
|
+
};
|
|
57
|
+
|
|
50
58
|
const startRPCServer = () => {
|
|
51
59
|
const startPort = 50000;
|
|
52
60
|
const endPort = 65535;
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -482,35 +482,41 @@ async function ensureBunBinary(
|
|
|
482
482
|
targetOS: "macos" | "win" | "linux",
|
|
483
483
|
targetArch: "arm64" | "x64",
|
|
484
484
|
bunVersion?: string,
|
|
485
|
+
bunnyBun?: string,
|
|
485
486
|
): Promise<string> {
|
|
486
|
-
|
|
487
|
+
const effectiveVersion = bunnyBun || bunVersion;
|
|
488
|
+
if (!effectiveVersion) {
|
|
487
489
|
return getPlatformPaths(targetOS, targetArch).BUN_BINARY;
|
|
488
490
|
}
|
|
489
491
|
|
|
490
492
|
const binExt = targetOS === "win" ? ".exe" : "";
|
|
491
|
-
const
|
|
493
|
+
const cacheSubdir = bunnyBun ? "bunny-bun-override" : "bun-override";
|
|
494
|
+
const overrideDir = join(ELECTROBUN_CACHE_PATH, cacheSubdir, `${targetOS}-${targetArch}`);
|
|
492
495
|
const overrideBinary = join(overrideDir, `bun${binExt}`);
|
|
493
496
|
const versionFile = join(overrideDir, ".bun-version");
|
|
494
497
|
|
|
495
498
|
// Check if already downloaded with matching version
|
|
496
499
|
if (existsSync(overrideBinary) && existsSync(versionFile)) {
|
|
497
500
|
const cachedVersion = readFileSync(versionFile, "utf8").trim();
|
|
498
|
-
if (cachedVersion ===
|
|
501
|
+
if (cachedVersion === effectiveVersion) {
|
|
499
502
|
console.log(
|
|
500
|
-
|
|
503
|
+
`${bunnyBun ? "Bunny" : "Custom"} Bun ${effectiveVersion} already cached for ${targetOS}-${targetArch}`,
|
|
501
504
|
);
|
|
502
505
|
return overrideBinary;
|
|
503
506
|
}
|
|
504
|
-
// Version mismatch - remove stale cache
|
|
505
507
|
console.log(
|
|
506
|
-
`Cached Bun version "${cachedVersion}" does not match requested "${
|
|
508
|
+
`Cached Bun version "${cachedVersion}" does not match requested "${effectiveVersion}", re-downloading...`,
|
|
507
509
|
);
|
|
508
510
|
rmSync(overrideDir, { recursive: true, force: true });
|
|
509
511
|
} else if (existsSync(overrideDir)) {
|
|
510
512
|
rmSync(overrideDir, { recursive: true, force: true });
|
|
511
513
|
}
|
|
512
514
|
|
|
513
|
-
|
|
515
|
+
if (bunnyBun) {
|
|
516
|
+
await downloadBunnyBun(bunnyBun, targetOS, targetArch);
|
|
517
|
+
} else {
|
|
518
|
+
await downloadCustomBun(effectiveVersion, targetOS, targetArch);
|
|
519
|
+
}
|
|
514
520
|
return overrideBinary;
|
|
515
521
|
}
|
|
516
522
|
|
|
@@ -664,6 +670,123 @@ async function downloadCustomBun(
|
|
|
664
670
|
}
|
|
665
671
|
}
|
|
666
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Downloads Electrobunny's Bun fork from blackboardsh/bun GitHub releases.
|
|
675
|
+
* Release assets follow the same naming convention as oven-sh/bun:
|
|
676
|
+
* bun-darwin-aarch64.zip, bun-linux-x64.zip, etc.
|
|
677
|
+
*/
|
|
678
|
+
async function downloadBunnyBun(
|
|
679
|
+
releaseTag: string,
|
|
680
|
+
platformOS: "macos" | "win" | "linux",
|
|
681
|
+
platformArch: "arm64" | "x64",
|
|
682
|
+
) {
|
|
683
|
+
let assetName: string;
|
|
684
|
+
let dirName: string;
|
|
685
|
+
|
|
686
|
+
// Asset names match the CI artifact names from blackboardsh/bun
|
|
687
|
+
if (platformOS === "win") {
|
|
688
|
+
assetName = "bun-windows-x64.zip";
|
|
689
|
+
dirName = "bun-windows-x64";
|
|
690
|
+
} else if (platformOS === "macos") {
|
|
691
|
+
assetName = platformArch === "arm64" ? "bun-darwin-arm64.zip" : "bun-darwin-x64.zip";
|
|
692
|
+
dirName = platformArch === "arm64" ? "bun-darwin-arm64" : "bun-darwin-x64";
|
|
693
|
+
} else {
|
|
694
|
+
assetName = platformArch === "arm64" ? "bun-linux-arm64.zip" : "bun-linux-x64.zip";
|
|
695
|
+
dirName = platformArch === "arm64" ? "bun-linux-arm64" : "bun-linux-x64";
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const binExt = platformOS === "win" ? ".exe" : "";
|
|
699
|
+
const overrideDir = join(ELECTROBUN_CACHE_PATH, "bunny-bun-override", `${platformOS}-${platformArch}`);
|
|
700
|
+
const overrideBinary = join(overrideDir, `bun${binExt}`);
|
|
701
|
+
const bunUrl = `https://github.com/blackboardsh/bun/releases/download/${releaseTag}/${assetName}`;
|
|
702
|
+
|
|
703
|
+
console.log(`Using Bunny Bun: ${releaseTag}`);
|
|
704
|
+
console.log(`Downloading from: ${bunUrl}`);
|
|
705
|
+
|
|
706
|
+
mkdirSync(overrideDir, { recursive: true });
|
|
707
|
+
const tempZipPath = join(overrideDir, "temp.zip");
|
|
708
|
+
|
|
709
|
+
try {
|
|
710
|
+
const response = await fetch(bunUrl);
|
|
711
|
+
if (!response.ok) {
|
|
712
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const contentLength = response.headers.get("content-length");
|
|
716
|
+
const totalSize = contentLength ? parseInt(contentLength, 10) : 0;
|
|
717
|
+
const fileStream = createWriteStream(tempZipPath);
|
|
718
|
+
let downloadedSize = 0;
|
|
719
|
+
let lastReportedPercent = -1;
|
|
720
|
+
|
|
721
|
+
if (response.body) {
|
|
722
|
+
const reader = response.body.getReader();
|
|
723
|
+
while (true) {
|
|
724
|
+
const { done, value } = await reader.read();
|
|
725
|
+
if (done) break;
|
|
726
|
+
const chunk = Buffer.from(value);
|
|
727
|
+
fileStream.write(chunk);
|
|
728
|
+
downloadedSize += chunk.length;
|
|
729
|
+
if (totalSize > 0) {
|
|
730
|
+
const percent = Math.round((downloadedSize / totalSize) * 100);
|
|
731
|
+
const percentTier = Math.floor(percent / 10) * 10;
|
|
732
|
+
if (percentTier > lastReportedPercent && percentTier <= 100) {
|
|
733
|
+
console.log(` Progress: ${percentTier}% (${Math.round(downloadedSize / 1024 / 1024)}MB/${Math.round(totalSize / 1024 / 1024)}MB)`);
|
|
734
|
+
lastReportedPercent = percentTier;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
await new Promise((resolve, reject) => {
|
|
741
|
+
fileStream.end((error: any) => { if (error) reject(error); else resolve(void 0); });
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
console.log(`Download completed (${Math.round(downloadedSize / 1024 / 1024)}MB), extracting...`);
|
|
745
|
+
|
|
746
|
+
if (platformOS === "win") {
|
|
747
|
+
execSync(`powershell -command "Expand-Archive -Path '${tempZipPath}' -DestinationPath '${overrideDir}' -Force"`, { stdio: "inherit" });
|
|
748
|
+
} else {
|
|
749
|
+
execSync(`unzip -o ${escapePathForTerminal(tempZipPath)} -d ${escapePathForTerminal(overrideDir)}`, { stdio: "inherit" });
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Move binary from extracted subdirectory
|
|
753
|
+
const extractedBinary = join(overrideDir, dirName, `bun${binExt}`);
|
|
754
|
+
if (existsSync(extractedBinary)) {
|
|
755
|
+
renameSync(extractedBinary, overrideBinary);
|
|
756
|
+
} else {
|
|
757
|
+
throw new Error(`Bun binary not found after extraction at ${extractedBinary}`);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (platformOS !== "win") {
|
|
761
|
+
execSync(`chmod +x ${escapePathForTerminal(overrideBinary)}`);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// Also extract ICU data if present
|
|
765
|
+
const extractedDir = join(overrideDir, dirName);
|
|
766
|
+
if (existsSync(extractedDir)) {
|
|
767
|
+
for (const file of readdirSync(extractedDir)) {
|
|
768
|
+
if (file.endsWith(".dat")) {
|
|
769
|
+
renameSync(join(extractedDir, file), join(overrideDir, file));
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
writeFileSync(join(overrideDir, ".bun-version"), releaseTag);
|
|
775
|
+
|
|
776
|
+
if (existsSync(tempZipPath)) unlinkSync(tempZipPath);
|
|
777
|
+
if (existsSync(extractedDir)) rmSync(extractedDir, { recursive: true, force: true });
|
|
778
|
+
|
|
779
|
+
console.log(`Bunny Bun ${releaseTag} for ${platformOS}-${platformArch} set up successfully`);
|
|
780
|
+
} catch (error: any) {
|
|
781
|
+
if (existsSync(overrideDir)) {
|
|
782
|
+
try { rmSync(overrideDir, { recursive: true, force: true }); } catch {}
|
|
783
|
+
}
|
|
784
|
+
console.error(`Failed to set up Bunny Bun ${releaseTag} for ${platformOS}-${platformArch}:`, error.message);
|
|
785
|
+
console.error(`\nVerify the release tag exists at: https://github.com/blackboardsh/bun/releases`);
|
|
786
|
+
process.exit(1);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
667
790
|
async function ensureCEFDependencies(
|
|
668
791
|
targetOS?: "macos" | "win" | "linux",
|
|
669
792
|
targetArch?: "arm64" | "x64",
|
|
@@ -1357,6 +1480,7 @@ const defaultConfig = {
|
|
|
1357
1480
|
cefVersion: undefined as string | undefined, // Override CEF version: "CEF_VERSION+chromium-CHROMIUM_VERSION"
|
|
1358
1481
|
wgpuVersion: undefined as string | undefined, // Override Dawn (WebGPU) version: "0.2.3" or "v0.2.3-beta.0"
|
|
1359
1482
|
bunVersion: undefined as string | undefined, // Override Bun runtime version: "1.4.2"
|
|
1483
|
+
bunnyBun: undefined as string | undefined, // Use Electrobunny's Bun fork: "bunny-bun-abc1234" (release tag from blackboardsh/bun)
|
|
1360
1484
|
locales: undefined as string[] | "*" | undefined, // ICU locales subset (Linux/Windows)
|
|
1361
1485
|
mac: {
|
|
1362
1486
|
codesign: false,
|
|
@@ -2191,6 +2315,7 @@ Categories=Utility;Application;
|
|
|
2191
2315
|
currentTarget.os,
|
|
2192
2316
|
currentTarget.arch,
|
|
2193
2317
|
config.build.bunVersion,
|
|
2318
|
+
config.build.bunnyBun,
|
|
2194
2319
|
);
|
|
2195
2320
|
// Note: .bin/bun binary in node_modules is a symlink to the versioned one in another place
|
|
2196
2321
|
// in node_modules, so we have to dereference here to get the actual binary in the bundle.
|
|
@@ -3113,6 +3238,7 @@ Categories=Utility;Application;
|
|
|
3113
3238
|
? { cefVersion: config.build?.cefVersion ?? DEFAULT_CEF_VERSION_STRING }
|
|
3114
3239
|
: {}),
|
|
3115
3240
|
bunVersion: config.build?.bunVersion ?? BUN_VERSION,
|
|
3241
|
+
...(config.build?.bunnyBun ? { bunnyBun: config.build.bunnyBun } : {}),
|
|
3116
3242
|
};
|
|
3117
3243
|
|
|
3118
3244
|
// Include chromiumFlags only if the developer defined them
|