electrobun 1.18.4-beta.5 → 2.0.1-beta.2
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/LICENSE +21 -0
- package/README.md +26 -167
- package/bin/electrobun.cjs +225 -153
- package/package.json +18 -49
- package/bun.lock +0 -119
- package/dist/api/browser/builtinrpcSchema.ts +0 -19
- package/dist/api/browser/global.d.ts +0 -36
- package/dist/api/browser/index.ts +0 -234
- package/dist/api/browser/webviewtag.ts +0 -88
- package/dist/api/browser/wgputag.ts +0 -48
- package/dist/api/bun/ElectrobunConfig.ts +0 -530
- package/dist/api/bun/__tests__/ffi-contract.test.ts +0 -105
- package/dist/api/bun/core/ApplicationMenu.ts +0 -70
- package/dist/api/bun/core/BrowserView.ts +0 -419
- package/dist/api/bun/core/BrowserWindow.ts +0 -400
- package/dist/api/bun/core/BuildConfig.ts +0 -71
- package/dist/api/bun/core/ContextMenu.ts +0 -75
- package/dist/api/bun/core/GpuWindow.ts +0 -289
- package/dist/api/bun/core/Paths.ts +0 -5
- package/dist/api/bun/core/Socket.ts +0 -22
- package/dist/api/bun/core/Tray.ts +0 -197
- package/dist/api/bun/core/Updater.ts +0 -1162
- package/dist/api/bun/core/Utils.ts +0 -487
- package/dist/api/bun/core/WGPUView.ts +0 -167
- package/dist/api/bun/core/menuRoles.ts +0 -181
- package/dist/api/bun/events/ApplicationEvents.ts +0 -22
- package/dist/api/bun/events/event.ts +0 -29
- package/dist/api/bun/events/eventEmitter.ts +0 -45
- package/dist/api/bun/events/trayEvents.ts +0 -11
- package/dist/api/bun/events/webviewEvents.ts +0 -39
- package/dist/api/bun/events/windowEvents.ts +0 -23
- package/dist/api/bun/index.ts +0 -298
- package/dist/api/bun/preload/.generated/compiled.ts +0 -8
- package/dist/api/bun/preload/build.ts +0 -65
- package/dist/api/bun/preload/dragRegions.ts +0 -41
- package/dist/api/bun/preload/encryption.ts +0 -86
- package/dist/api/bun/preload/events.ts +0 -171
- package/dist/api/bun/preload/globals.d.ts +0 -45
- package/dist/api/bun/preload/index-sandboxed.ts +0 -28
- package/dist/api/bun/preload/index.ts +0 -77
- package/dist/api/bun/preload/internalRpc.ts +0 -80
- package/dist/api/bun/preload/overlaySync.ts +0 -107
- package/dist/api/bun/preload/webviewTag.ts +0 -451
- package/dist/api/bun/preload/wgpuTag.ts +0 -246
- package/dist/api/bun/proc/linux.md +0 -43
- package/dist/api/bun/proc/native.ts +0 -3385
- package/dist/api/bun/webGPU.ts +0 -346
- package/dist/api/bun/webgpuAdapter.ts +0 -3011
- package/dist/api/shared/bun-version.ts +0 -3
- package/dist/api/shared/cef-version.ts +0 -5
- package/dist/api/shared/electrobun-version.ts +0 -2
- package/dist/api/shared/naming.test.ts +0 -327
- package/dist/api/shared/naming.ts +0 -188
- package/dist/api/shared/platform.ts +0 -48
- package/dist/api/shared/rpc.ts +0 -541
- package/dist/main.js +0 -168
- package/dist/preload-full.js +0 -913
- package/dist/preload-sandboxed.js +0 -111
- package/dist/zig-sdk/electrobun.zig +0 -1993
- package/src/cli/bun.lockb +0 -0
- package/src/cli/index.ts +0 -5689
- package/src/cli/package-lock.json +0 -81
- package/src/cli/package.json +0 -11
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Blackboard Technologies inc.
|
|
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
CHANGED
|
@@ -1,178 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
<a href="https://electrobun.dev"><img src="https://github.com/blackboardsh/electrobun/assets/75102186/8799b522-0507-45e9-86e3-c3cfded1aa7c" alt="Logo" height=170></a>
|
|
3
|
-
</p>
|
|
1
|
+
# Electrobun npm bootstrap
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
The `electrobun` npm package is a small compatibility entry point for npm,
|
|
4
|
+
pnpm, Yarn, and Bun users. It contains no Electrobun runtime or SDK. The
|
|
5
|
+
`electrobun` command installs Hutch into `~/.dash` when necessary and delegates
|
|
6
|
+
all commands to `hutch electrobun`.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
Get started with a template <br />
|
|
9
|
-
<code><strong>npx electrobun init</strong></code>
|
|
10
|
-
</div>
|
|
8
|
+
Create a project from the current stable template catalog:
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## What is Electrobun?
|
|
15
|
-
|
|
16
|
-
Electrobun aims to be a complete **solution-in-a-box** for building, updating, and shipping ultra fast, tiny, and cross-platform desktop applications written in Typescript.
|
|
17
|
-
Under the hood it uses <a href="https://bun.sh">bun</a> to execute the main process and to bundle webview typescript, and has native bindings written in Objc, C++, and several core parts written in <a href="https://ziglang.org/">zig</a>.
|
|
18
|
-
|
|
19
|
-
Visit <a href="https://docs.electrobunny.ai/electrobun/">https://docs.electrobunny.ai/electrobun/</a> to see api documentation, guides, and more.
|
|
20
|
-
|
|
21
|
-
You use it via npm.
|
|
22
|
-
|
|
23
|
-
Don't miss our:
|
|
24
|
-
- self-extracting bundles that use ZSTD compression for more compact distributables as small as 16MB
|
|
25
|
-
- zig optimized BSDIFF implementation that lets you ship tiny app updates as small as 4KB
|
|
26
|
-
- `bundleCEF` flag to bundle and pin Chromium for those that want that tradeoff of consistency over file size
|
|
27
|
-
- `bundleWGPU` that lets you use Bun Typescript -> WGPU to control a native GPU surface without a webview
|
|
28
|
-
- Our Three.js and Babylon.js adapters that work right in Bun
|
|
29
|
-
- Our `<electrobun-webview>` and `<electrobun-wpgu>` html elements that let you composit proper OOPIFs and native GPU surfaces into your UIs
|
|
30
|
-
- so much more.
|
|
31
|
-
|
|
32
|
-
**Project Goals**
|
|
33
|
-
|
|
34
|
-
- Write typescript for the main process and webviews without having to think about it.
|
|
35
|
-
- Isolation between main and webview processes with fast, typed, easy to implement RPC between them.
|
|
36
|
-
- Small self-extracting app bundles ~14MB (when using system webview, most of this is the bun runtime)
|
|
37
|
-
- Even smaller app updates as small as 4KB (using bsdiff it only downloads tiny patches between versions)
|
|
38
|
-
- Provide everything you need in one tightly integrated workflow to start writing code in 5 minutes and distribute in 10.
|
|
39
|
-
|
|
40
|
-
## Apps Built with Electrobun
|
|
41
|
-
- [24agents](https://github.com/jhsu/24agents) - Hyperprompter
|
|
42
|
-
- [act-track-ai](https://github.com/IrdanGu/act-track-ai) - personal desktop productivity tracker
|
|
43
|
-
- [Agents Council](https://github.com/MrLesk/agents-council) - agent-to-agent MCP communication tool for feedback requests
|
|
44
|
-
- [ai-wrapped](https://github.com/gulivan/ai-wrapped) - Wrapped-style desktop dashboard for your AI coding agent activity
|
|
45
|
-
- [Audio TTS](https://github.com/blackboardsh/audio-tts) - desktop text-to-speech app using Qwen3-TTS for voice design, cloning, and generation
|
|
46
|
-
- [aueio-player-desktop](https://github.com/tuomashatakka/aueio-player-desktop) - beautiful, minimal cross-platform audio player
|
|
47
|
-
- [bestdiff](https://github.com/tesmond/bestdiff) - a git diff checker with curved connectors
|
|
48
|
-
- [BuddyWriter](https://github.com/OxFrancesco/BuddyWriter) - BuddyWriter desktop and mobile apps
|
|
49
|
-
- [burns](https://github.com/l3wi/burns) - a Smithers manager
|
|
50
|
-
- [cbx-tool](https://github.com/jebin2/cbx-tool) - desktop app for reading and editing comic book archives (.cbz/.cbr)
|
|
51
|
-
- [Co(lab)](https://blackboard.sh/colab/) - a hybrid web browser + code editor for deep work
|
|
52
|
-
- [codlogs](https://github.com/tobitege/codlogs) - search and export local Codex sessions via CLI or desktop app
|
|
53
|
-
- [Codex Agents Composer](https://github.com/MrLesk/codex-agents-composer) - desktop app for managing your Codex agents and their skills
|
|
54
|
-
- [codex-devtools](https://github.com/gulivan/codex-devtools) - desktop inspector for Codex session data; browse conversations, search messages, and analyze agent activity
|
|
55
|
-
- [Deskdown](https://github.com/guarana-studio/deskdown) - transform any web address into a desktop app in under 20 seconds
|
|
56
|
-
- [Dictate](https://github.com/siddhantparadox/dictate) - Windows dictation app with local and BYOK cloud transcription
|
|
57
|
-
- [dev-3.0](https://github.com/h0x91b/dev-3.0) - helps you not get lost while managing multiple AI agents across projects
|
|
58
|
-
- [DOOM](https://github.com/blackboardsh/electrobun-doom) - DOOM implemented in 2 ways: bun -> (c doom -> bundled wgpu) and (full ts port bun -> bundled wgpu)
|
|
59
|
-
- [dotlock](https://github.com/tsconfigdotjson/dotlock) - macOS desktop app for managing `.env` files across your projects
|
|
60
|
-
- [electrobun-pdf](https://github.com/GijungKim/electrobun-pdf) - local-first PDF & DOCX editor for opening, annotating, and exporting documents without leaving your machine
|
|
61
|
-
- [electrobun-rms](https://github.com/khanhthanhdev/electrobun-rms) - fast Electrobun desktop app template with React, Tailwind CSS, and Vite
|
|
62
|
-
- [golb](https://github.com/chrisdadev13/golb) - desktop AI coding workspace built with React, Vite, and Tailwind
|
|
63
|
-
- [GOG Achievements GUI](https://github.com/timendum/gog-achievements-gui) - desktop app for managing GOG achievements
|
|
64
|
-
- [groov](https://github.com/laurenzcodes/groov) - desktop audio deck monitor
|
|
65
|
-
- [Guerilla Glass](https://github.com/okikeSolutions/guerillaglass) - open-source cross-platform creator studio for fast Record -> Edit -> Deliver workflows
|
|
66
|
-
- [Invoke](https://getinvoke.com) - macOS UI automation & shortcut platform
|
|
67
|
-
- [Marginalia](https://github.com/lars-hoeijmans/Marginalia) - a simple note taking app
|
|
68
|
-
- [MarkBun](https://github.com/xiaochong/markbun) - fast, beautiful, Typora-like markdown desktop editor
|
|
69
|
-
- [md-browse](https://github.com/needle-tools/md-browse) - a markdown-first browser that converts web pages to clean markdown
|
|
70
|
-
- [moop](https://github.com/zrubinrattet/moop/) - desktop app for batch image optimization for the web
|
|
71
|
-
- [Patchline](https://github.com/adwaithks/Patchline) - lightweight desktop Git client for reading patches and line diffs, then staging and committing changes
|
|
72
|
-
- [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
|
|
73
|
-
- [PiBun](https://github.com/khairold/pibun) - desktop GUI for the Pi coding agent with chat, terminal, git integration, and plugin system
|
|
74
|
-
- [PLEXI](https://github.com/ianjamesburke/PLEXI) - a multi-dimensional terminal multiplexer for the agentic era
|
|
75
|
-
- [Prometheus](https://github.com/opensourcectl/prometheus) - desktop utility toolbox for file cleanup, document manipulation, and image processing
|
|
76
|
-
- [Quiver](https://ataraxy-labs.github.io/quiver/) - desktop app for GitHub PR reviews, merge conflict resolution, and AI commit messages
|
|
77
|
-
- [remotecode.io](https://github.com/samuelfaj/remotecode.io) - continue local AI coding sessions (Claude Code or Codex) from your mobile device
|
|
78
|
-
- [sirene](https://github.com/KevinBonnoron/sirene) - self-hosted multi-backend text-to-speech platform with voice cloning
|
|
79
|
-
- [StoryForge](https://github.com/vrrdnt/StoryForge) - desktop app for Vintage Story players to switch between game versions, modpacks, servers, and accounts
|
|
80
|
-
- [Tensamin Client](https://github.com/Tensamin/Client) - web, desktop, and mobile app for accessing Tensamin
|
|
81
|
-
- [tokenpass-desktop](https://github.com/b-open-io/tokenpass-desktop) - desktop app that runs the Sigma Identity stack locally for Bitcoin-backed authentication
|
|
82
|
-
- [typsmthng-desktop](https://github.com/aaditagrawal/typsmthng-desktop) - experimental desktop typing application
|
|
83
|
-
- [VibesOS](https://github.com/popmechanic/VibesOS) - A GUI for Claude Code that makes it easy to vibe code simple, un-hackable apps
|
|
84
|
-
- [VoiceVault](https://github.com/PJH720/VoiceVault) - AI-powered voice recorder with transcription, summarization, and RAG search
|
|
85
|
-
- [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
|
|
86
|
-
- [whatsapp-reminder](https://github.com/FatahChan/whatsapp-reminder) - managed scheduled WhatsApp messages
|
|
87
|
-
|
|
88
|
-
### Video Demos
|
|
89
|
-
|
|
90
|
-
[](https://www.youtube.com/watch?v=Z4dNK1d6l6E)
|
|
91
|
-
|
|
92
|
-
[](https://www.youtube.com/watch?v=WWTCqGmE86w)
|
|
93
|
-
|
|
94
|
-
[](https://x.com/YoavCodes/status/2028499038148903239?s=20)
|
|
95
|
-
|
|
96
|
-
## Star History
|
|
97
|
-
|
|
98
|
-
[](https://www.star-history.com/#blackboardsh/electrobun&type=date&legend=top-left)
|
|
99
|
-
|
|
100
|
-
## Contributing
|
|
101
|
-
Electrobun is one piece of a vision I'm building. I'm optimizing for focus and execution. Issues and PRs can be used to share ideas, but there should be no expectation that I will review, respond to, or merge them.
|
|
102
|
-
|
|
103
|
-
Ways to get involved:
|
|
104
|
-
|
|
105
|
-
- Read the [Contribution guidelines](./CONTRIBUTING.md)
|
|
106
|
-
- 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>
|
|
107
|
-
- Join the conversation on <a href="https://discord.gg/ueKE4tjaCE">Discord</a>
|
|
108
|
-
- Create and participate in Github issues and discussions
|
|
109
|
-
- Let me know what you're building with Electrobun
|
|
110
|
-
|
|
111
|
-
## Development Setup
|
|
112
|
-
Building apps with Electrobun is as easy as updating your package.json dependencies with `npm add electrobun` or try one of our templates via `npx electrobun init`.
|
|
113
|
-
|
|
114
|
-
**This section is for building Electrobun from source locally in order to contribute fixes to it.**
|
|
115
|
-
|
|
116
|
-
### Prerequisites
|
|
117
|
-
|
|
118
|
-
**macOS:**
|
|
119
|
-
- Xcode command line tools
|
|
120
|
-
- cmake (install via homebrew: `brew install cmake`)
|
|
121
|
-
|
|
122
|
-
**Windows:**
|
|
123
|
-
- Visual Studio Build Tools or Visual Studio with C++ development tools
|
|
124
|
-
- cmake
|
|
125
|
-
|
|
126
|
-
**Linux:**
|
|
127
|
-
- build-essential package
|
|
128
|
-
- cmake
|
|
129
|
-
- webkit2gtk and GTK development packages
|
|
130
|
-
|
|
131
|
-
On Ubuntu/Debian based distros: `sudo apt install build-essential cmake pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev`
|
|
132
|
-
|
|
133
|
-
### First-time Setup
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
git clone --recurse-submodules https://github.com/blackboardsh/electrobun.git
|
|
137
|
-
cd electrobun/package
|
|
138
|
-
bun install
|
|
139
|
-
bun dev:clean
|
|
10
|
+
```sh
|
|
11
|
+
npx electrobun init
|
|
140
12
|
```
|
|
141
13
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
# All commands are run from the /package directory
|
|
146
|
-
cd electrobun/package
|
|
147
|
-
|
|
148
|
-
# After making changes to source code
|
|
149
|
-
bun dev
|
|
150
|
-
|
|
151
|
-
# If you only changed kitchen sink code (not electrobun source)
|
|
152
|
-
bun dev:rerun
|
|
14
|
+
Use the beta catalog only when you ask for it:
|
|
153
15
|
|
|
154
|
-
|
|
155
|
-
|
|
16
|
+
```sh
|
|
17
|
+
npx electrobun init --beta
|
|
156
18
|
```
|
|
157
19
|
|
|
158
|
-
|
|
20
|
+
The npm bootstrap ships under the same release version as Electrobun, but it
|
|
21
|
+
does not contain or select the runtime by itself. An application's exact
|
|
22
|
+
Electrobun version is selected in `hutch.config.ts`; Hutch downloads the
|
|
23
|
+
matching runtime and SDK into the shared `~/.dash` store and projects the SDKs
|
|
24
|
+
into the project's `.hutch/devkit` sysroot. Installing a beta bootstrap still
|
|
25
|
+
uses stable templates unless `init` receives `--beta`.
|
|
159
26
|
|
|
160
|
-
|
|
27
|
+
`DASH_RELEASE_OFFLINE=1` prevents this bootstrap from downloading a missing
|
|
28
|
+
Hutch installation. An already installed Hutch is still invoked, and the flag
|
|
29
|
+
continues to prevent network access for Dash-managed releases and artifacts.
|
|
30
|
+
It does not put npm or another project package manager into offline mode.
|
|
161
31
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- `bun build:release` - Build electrobun in release mode
|
|
32
|
+
You can install Hutch directly from <https://hutch.blackboard.sh> and run the
|
|
33
|
+
same commands without npm:
|
|
165
34
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
## Platform Support
|
|
171
|
-
|
|
172
|
-
| OS | Status |
|
|
173
|
-
|---|---|
|
|
174
|
-
| macOS 14+ | Official |
|
|
175
|
-
| Windows 11+ | Official |
|
|
176
|
-
| Ubuntu 22.04+ | Official |
|
|
177
|
-
| Other Linux distros (gtk3, webkit2gtk-4.1) | Community |
|
|
178
|
-
| Raspberry Pi | Unofficial fork: [kortexa-ai/electrobun (linux-wpe)](https://github.com/kortexa-ai/electrobun/tree/kortexa/linux-wpe) — follow the author [@francip](https://x.com/francip/status/2050149256053539059?s=20) |
|
|
35
|
+
```sh
|
|
36
|
+
hutch electrobun init
|
|
37
|
+
```
|
package/bin/electrobun.cjs
CHANGED
|
@@ -1,169 +1,241 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { spawnSync } = require("node:child_process");
|
|
5
|
+
const { existsSync, mkdtempSync, rmSync, writeFileSync } = require("node:fs");
|
|
6
|
+
const { get } = require("node:https");
|
|
7
|
+
const { homedir, tmpdir } = require("node:os");
|
|
8
|
+
const path = require("node:path");
|
|
9
|
+
|
|
10
|
+
const installerBaseUrl = "https://hutch.blackboard.sh/hutch";
|
|
11
|
+
const maxInstallerBytes = 1024 * 1024;
|
|
12
|
+
const maxInstallerRedirects = 5;
|
|
13
|
+
|
|
14
|
+
function normalizeChannel(value) {
|
|
15
|
+
if (value === "stable") return "production";
|
|
16
|
+
if (value === "production" || value === "canary") return value;
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function hutchChannel(environment) {
|
|
21
|
+
for (const key of ["ELECTROBUN_HUTCH_CHANNEL", "HUTCH_ACTIVE_CHANNEL"]) {
|
|
22
|
+
const selected = normalizeChannel(environment[key]);
|
|
23
|
+
if (selected) return selected;
|
|
24
|
+
}
|
|
25
|
+
return "production";
|
|
26
|
+
}
|
|
2
27
|
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const agent = new ProxyAgent();
|
|
11
|
-
|
|
12
|
-
// Detect platform and architecture
|
|
13
|
-
function getPlatform() {
|
|
14
|
-
switch (process.platform) {
|
|
15
|
-
case 'win32': return 'win';
|
|
16
|
-
case 'darwin': return 'darwin';
|
|
17
|
-
case 'linux': return 'linux';
|
|
18
|
-
default: throw new Error(`Unsupported platform: ${process.platform}`);
|
|
19
|
-
}
|
|
28
|
+
function environmentFlagEnabled(environment, name) {
|
|
29
|
+
const value = environment[name];
|
|
30
|
+
return (
|
|
31
|
+
value === "1" ||
|
|
32
|
+
(typeof value === "string" &&
|
|
33
|
+
["true", "yes"].includes(value.toLowerCase()))
|
|
34
|
+
);
|
|
20
35
|
}
|
|
21
36
|
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
function hutchBinaryPath(channel, environment, platform, userHome) {
|
|
38
|
+
if (environment.ELECTROBUN_HUTCH_BINARY) {
|
|
39
|
+
return environment.ELECTROBUN_HUTCH_BINARY;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dashHome = environment.DASH_HOME || path.join(userHome, ".dash");
|
|
43
|
+
const command = channel === "canary" ? "hutch-canary" : "hutch";
|
|
44
|
+
const pathApi = platform === "win32" ? path.win32 : path.posix;
|
|
45
|
+
return pathApi.join(
|
|
46
|
+
dashHome,
|
|
47
|
+
"bin",
|
|
48
|
+
`${command}${platform === "win32" ? ".exe" : ""}`,
|
|
49
|
+
);
|
|
28
50
|
}
|
|
29
51
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
function hutchArguments(args) {
|
|
53
|
+
const forwarded = Array.from(args);
|
|
54
|
+
if (forwarded[0] !== "init") return forwarded;
|
|
55
|
+
|
|
56
|
+
const explicitTemplateChannel = forwarded.some(
|
|
57
|
+
(argument) =>
|
|
58
|
+
argument === "--beta" ||
|
|
59
|
+
argument.startsWith("--channel="),
|
|
60
|
+
);
|
|
61
|
+
return explicitTemplateChannel
|
|
62
|
+
? forwarded
|
|
63
|
+
: [...forwarded, "--channel=stable"];
|
|
64
|
+
}
|
|
34
65
|
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
function download(url, redirects = 0) {
|
|
67
|
+
if (redirects > maxInstallerRedirects) {
|
|
68
|
+
return Promise.reject(new Error("too many installer redirects"));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let target;
|
|
72
|
+
try {
|
|
73
|
+
target = new URL(url);
|
|
74
|
+
} catch {
|
|
75
|
+
return Promise.reject(new Error("invalid Hutch installer URL"));
|
|
76
|
+
}
|
|
77
|
+
if (target.protocol !== "https:") {
|
|
78
|
+
return Promise.reject(
|
|
79
|
+
new Error("the Hutch installer must be downloaded over HTTPS"),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
const request = get(target, (response) => {
|
|
85
|
+
if (
|
|
86
|
+
response.statusCode >= 300 &&
|
|
87
|
+
response.statusCode < 400 &&
|
|
88
|
+
response.headers.location
|
|
89
|
+
) {
|
|
90
|
+
response.resume();
|
|
91
|
+
let redirected;
|
|
92
|
+
try {
|
|
93
|
+
redirected = new URL(response.headers.location, target);
|
|
94
|
+
} catch {
|
|
95
|
+
reject(new Error("invalid Hutch installer redirect URL"));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
resolve(download(redirected.href, redirects + 1));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (response.statusCode !== 200) {
|
|
103
|
+
response.resume();
|
|
104
|
+
reject(
|
|
105
|
+
new Error(
|
|
106
|
+
`installer download returned HTTP ${response.statusCode ?? "unknown"}`,
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const chunks = [];
|
|
113
|
+
let size = 0;
|
|
114
|
+
response.on("data", (chunk) => {
|
|
115
|
+
size += chunk.length;
|
|
116
|
+
if (size > maxInstallerBytes) {
|
|
117
|
+
request.destroy(
|
|
118
|
+
new Error("installer download exceeded the 1 MiB limit"),
|
|
119
|
+
);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
chunks.push(chunk);
|
|
123
|
+
});
|
|
124
|
+
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
125
|
+
response.on("error", reject);
|
|
126
|
+
});
|
|
127
|
+
request.on("error", reject);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function checkedSpawn(command, args, options) {
|
|
132
|
+
const result = spawnSync(command, args, options);
|
|
133
|
+
if (result.error) throw result.error;
|
|
134
|
+
if (result.status !== 0) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`${command} exited with status ${result.status ?? "unknown"}`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
39
140
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
141
|
+
async function installHutch({ channel, environment, platform }) {
|
|
142
|
+
const temporary = mkdtempSync(path.join(tmpdir(), "electrobun-hutch-"));
|
|
143
|
+
try {
|
|
144
|
+
if (platform === "win32") {
|
|
145
|
+
const installer = path.join(temporary, "install.ps1");
|
|
146
|
+
writeFileSync(
|
|
147
|
+
installer,
|
|
148
|
+
await download(`${installerBaseUrl}/install.ps1`),
|
|
149
|
+
);
|
|
150
|
+
checkedSpawn(
|
|
151
|
+
"powershell.exe",
|
|
152
|
+
[
|
|
153
|
+
"-NoProfile",
|
|
154
|
+
"-NonInteractive",
|
|
155
|
+
"-ExecutionPolicy",
|
|
156
|
+
"Bypass",
|
|
157
|
+
"-File",
|
|
158
|
+
installer,
|
|
159
|
+
"-Channel",
|
|
160
|
+
channel,
|
|
161
|
+
],
|
|
162
|
+
{ env: environment, stdio: "inherit" },
|
|
163
|
+
);
|
|
164
|
+
} else {
|
|
165
|
+
const installer = path.join(temporary, "install.sh");
|
|
166
|
+
writeFileSync(
|
|
167
|
+
installer,
|
|
168
|
+
await download(`${installerBaseUrl}/install.sh`),
|
|
169
|
+
{ mode: 0o700 },
|
|
170
|
+
);
|
|
171
|
+
checkedSpawn("sh", [installer, "--channel", channel], {
|
|
172
|
+
env: environment,
|
|
173
|
+
stdio: "inherit",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
} finally {
|
|
177
|
+
rmSync(temporary, { force: true, recursive: true });
|
|
178
|
+
}
|
|
43
179
|
}
|
|
44
180
|
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
https.get(url, {agent}, (response) => {
|
|
56
|
-
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
57
|
-
// Follow redirect
|
|
58
|
-
return downloadFile(response.headers.location, filePath).then(resolve).catch(reject);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (response.statusCode !== 200) {
|
|
62
|
-
reject(new Error(`Download failed: ${response.statusCode}`));
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
response.pipe(file);
|
|
67
|
-
|
|
68
|
-
file.on('finish', () => {
|
|
69
|
-
file.close();
|
|
70
|
-
resolve();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
file.on('error', reject);
|
|
74
|
-
}).on('error', reject);
|
|
75
|
-
});
|
|
181
|
+
function runHutch({ binary, args, environment }) {
|
|
182
|
+
const result = spawnSync(binary, ["electrobun", ...args], {
|
|
183
|
+
env: environment,
|
|
184
|
+
stdio: "inherit",
|
|
185
|
+
});
|
|
186
|
+
if (result.error) throw result.error;
|
|
187
|
+
if (result.status !== null) return result.status;
|
|
188
|
+
if (result.signal === "SIGINT") return 130;
|
|
189
|
+
if (result.signal === "SIGTERM") return 143;
|
|
190
|
+
return 1;
|
|
76
191
|
}
|
|
77
192
|
|
|
78
|
-
async function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
await downloadFile(tarballUrl, tarballPath);
|
|
109
|
-
|
|
110
|
-
// Extract using system tar (available on macOS, Linux, and Windows 10+)
|
|
111
|
-
execSync(`${getTarCommand()} -xzf "${tarballPath}"`, { cwd: cacheDir, stdio: 'pipe' });
|
|
112
|
-
|
|
113
|
-
// Clean up tarball
|
|
114
|
-
unlinkSync(tarballPath);
|
|
115
|
-
|
|
116
|
-
// Check if CLI binary was extracted
|
|
117
|
-
if (!existsSync(cliBinary)) {
|
|
118
|
-
throw new Error(`CLI binary not found at ${cliBinary} after extraction`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Make executable on Unix systems
|
|
122
|
-
if (platform !== 'win') {
|
|
123
|
-
chmodSync(cliBinary, '755');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Copy CLI to bin location so npm scripts can find it
|
|
127
|
-
mkdirSync(dirname(binLocation), { recursive: true });
|
|
128
|
-
copyFileSync(cliBinary, binLocation);
|
|
129
|
-
|
|
130
|
-
// Make the bin location executable too
|
|
131
|
-
if (platform !== 'win') {
|
|
132
|
-
chmodSync(binLocation, '755');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.log('electrobun CLI downloaded successfully!');
|
|
136
|
-
return binLocation;
|
|
137
|
-
|
|
138
|
-
} catch (error) {
|
|
139
|
-
throw new Error(`Failed to download electrobun CLI: ${error.message}`);
|
|
140
|
-
}
|
|
193
|
+
async function main(options = {}) {
|
|
194
|
+
const args = hutchArguments(options.args ?? process.argv.slice(2));
|
|
195
|
+
const environment = options.environment ?? process.env;
|
|
196
|
+
const platform = options.platform ?? process.platform;
|
|
197
|
+
const userHome = options.userHome ?? homedir();
|
|
198
|
+
const fileExists = options.existsSync ?? existsSync;
|
|
199
|
+
const install = options.installHutch ?? installHutch;
|
|
200
|
+
const run = options.runHutch ?? runHutch;
|
|
201
|
+
const channel = hutchChannel(environment);
|
|
202
|
+
const binary = hutchBinaryPath(channel, environment, platform, userHome);
|
|
203
|
+
|
|
204
|
+
if (!fileExists(binary)) {
|
|
205
|
+
if (environment.ELECTROBUN_HUTCH_BINARY) {
|
|
206
|
+
throw new Error(`ELECTROBUN_HUTCH_BINARY does not exist: ${binary}`);
|
|
207
|
+
}
|
|
208
|
+
if (environmentFlagEnabled(environment, "DASH_RELEASE_OFFLINE")) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`Hutch is not installed at ${binary}; DASH_RELEASE_OFFLINE prevents downloading it`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
console.error(
|
|
214
|
+
`Electrobun requires Hutch; installing the latest ${channel} release...`,
|
|
215
|
+
);
|
|
216
|
+
await install({ channel, environment, platform });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!fileExists(binary)) {
|
|
220
|
+
throw new Error(`Hutch was not installed at ${binary}`);
|
|
221
|
+
}
|
|
222
|
+
return run({ binary, args, environment });
|
|
141
223
|
}
|
|
142
224
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
child.on('exit', (code) => {
|
|
155
|
-
process.exit(code || 0);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
child.on('error', (error) => {
|
|
159
|
-
console.error('Failed to start electrobun CLI:', error.message);
|
|
160
|
-
process.exit(1);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
} catch (error) {
|
|
164
|
-
console.error('Error:', error.message);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
225
|
+
if (require.main === module) {
|
|
226
|
+
main()
|
|
227
|
+
.then((status) => {
|
|
228
|
+
process.exitCode = status;
|
|
229
|
+
})
|
|
230
|
+
.catch((error) => {
|
|
231
|
+
console.error(`electrobun: ${error.message}`);
|
|
232
|
+
process.exitCode = 1;
|
|
233
|
+
});
|
|
167
234
|
}
|
|
168
235
|
|
|
169
|
-
|
|
236
|
+
module.exports = {
|
|
237
|
+
hutchArguments,
|
|
238
|
+
hutchBinaryPath,
|
|
239
|
+
hutchChannel,
|
|
240
|
+
main,
|
|
241
|
+
};
|