brosh 0.2.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 +181 -0
- package/brosh_brandmark.svg +3 -0
- package/brosh_logo.svg +27 -0
- package/cli_icon.svg +52 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +138 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +618 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +25 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +28 -0
- package/dist/lib.js.map +1 -0
- package/dist/mode-selector.d.ts +7 -0
- package/dist/mode-selector.d.ts.map +1 -0
- package/dist/mode-selector.js +138 -0
- package/dist/mode-selector.js.map +1 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +79 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/recording/index.d.ts +4 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +3 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/manager.d.ts +62 -0
- package/dist/recording/manager.d.ts.map +1 -0
- package/dist/recording/manager.js +123 -0
- package/dist/recording/manager.js.map +1 -0
- package/dist/recording/recorder.d.ts +95 -0
- package/dist/recording/recorder.d.ts.map +1 -0
- package/dist/recording/recorder.js +330 -0
- package/dist/recording/recorder.js.map +1 -0
- package/dist/recording/types.d.ts +65 -0
- package/dist/recording/types.d.ts.map +1 -0
- package/dist/recording/types.js +2 -0
- package/dist/recording/types.js.map +1 -0
- package/dist/sandbox/ModeSelector.d.ts +2 -0
- package/dist/sandbox/ModeSelector.d.ts.map +1 -0
- package/dist/sandbox/ModeSelector.js +2 -0
- package/dist/sandbox/ModeSelector.js.map +1 -0
- package/dist/sandbox/config.d.ts +46 -0
- package/dist/sandbox/config.d.ts.map +1 -0
- package/dist/sandbox/config.js +144 -0
- package/dist/sandbox/config.js.map +1 -0
- package/dist/sandbox/controller.d.ts +72 -0
- package/dist/sandbox/controller.d.ts.map +1 -0
- package/dist/sandbox/controller.js +208 -0
- package/dist/sandbox/controller.js.map +1 -0
- package/dist/sandbox/index.d.ts +6 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +4 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/mode-prompt.d.ts +10 -0
- package/dist/sandbox/mode-prompt.d.ts.map +1 -0
- package/dist/sandbox/mode-prompt.js +130 -0
- package/dist/sandbox/mode-prompt.js.map +1 -0
- package/dist/sandbox/prompt.d.ts +10 -0
- package/dist/sandbox/prompt.d.ts.map +1 -0
- package/dist/sandbox/prompt.js +434 -0
- package/dist/sandbox/prompt.js.map +1 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +59 -0
- package/dist/server.js.map +1 -0
- package/dist/terminal/index.d.ts +5 -0
- package/dist/terminal/index.d.ts.map +1 -0
- package/dist/terminal/index.js +3 -0
- package/dist/terminal/index.js.map +1 -0
- package/dist/terminal/manager.d.ts +153 -0
- package/dist/terminal/manager.d.ts.map +1 -0
- package/dist/terminal/manager.js +276 -0
- package/dist/terminal/manager.js.map +1 -0
- package/dist/terminal/session.d.ts +137 -0
- package/dist/terminal/session.d.ts.map +1 -0
- package/dist/terminal/session.js +752 -0
- package/dist/terminal/session.js.map +1 -0
- package/dist/tools/definitions.d.ts +18 -0
- package/dist/tools/definitions.d.ts.map +1 -0
- package/dist/tools/definitions.js +114 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/getContent.d.ts +32 -0
- package/dist/tools/getContent.d.ts.map +1 -0
- package/dist/tools/getContent.js +38 -0
- package/dist/tools/getContent.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +49 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/screenshot.d.ts +20 -0
- package/dist/tools/screenshot.d.ts.map +1 -0
- package/dist/tools/screenshot.js +28 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/sendKey.d.ts +31 -0
- package/dist/tools/sendKey.d.ts.map +1 -0
- package/dist/tools/sendKey.js +38 -0
- package/dist/tools/sendKey.js.map +1 -0
- package/dist/tools/startRecording.d.ts +68 -0
- package/dist/tools/startRecording.d.ts.map +1 -0
- package/dist/tools/startRecording.js +111 -0
- package/dist/tools/startRecording.js.map +1 -0
- package/dist/tools/stopRecording.d.ts +31 -0
- package/dist/tools/stopRecording.d.ts.map +1 -0
- package/dist/tools/stopRecording.js +76 -0
- package/dist/tools/stopRecording.js.map +1 -0
- package/dist/tools/type.d.ts +31 -0
- package/dist/tools/type.d.ts.map +1 -0
- package/dist/tools/type.js +31 -0
- package/dist/tools/type.js.map +1 -0
- package/dist/transport/gui-protocol.d.ts +163 -0
- package/dist/transport/gui-protocol.d.ts.map +1 -0
- package/dist/transport/gui-protocol.js +68 -0
- package/dist/transport/gui-protocol.js.map +1 -0
- package/dist/transport/gui-stream.d.ts +139 -0
- package/dist/transport/gui-stream.d.ts.map +1 -0
- package/dist/transport/gui-stream.js +440 -0
- package/dist/transport/gui-stream.js.map +1 -0
- package/dist/transport/index.d.ts +6 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +6 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/socket.d.ts +46 -0
- package/dist/transport/socket.d.ts.map +1 -0
- package/dist/transport/socket.js +310 -0
- package/dist/transport/socket.js.map +1 -0
- package/dist/types/mcp-client-info.d.ts +226 -0
- package/dist/types/mcp-client-info.d.ts.map +1 -0
- package/dist/types/mcp-client-info.js +62 -0
- package/dist/types/mcp-client-info.js.map +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +84 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/utils/env.d.ts +17 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +35 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/keys.d.ts +16 -0
- package/dist/utils/keys.d.ts.map +1 -0
- package/dist/utils/keys.js +155 -0
- package/dist/utils/keys.js.map +1 -0
- package/dist/utils/platform.d.ts +16 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +41 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/session-logger.d.ts +31 -0
- package/dist/utils/session-logger.d.ts.map +1 -0
- package/dist/utils/session-logger.js +125 -0
- package/dist/utils/session-logger.js.map +1 -0
- package/dist/utils/stats.d.ts +46 -0
- package/dist/utils/stats.d.ts.map +1 -0
- package/dist/utils/stats.js +89 -0
- package/dist/utils/stats.js.map +1 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +9 -0
- package/dist/utils/version.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +61 -0
- package/packages/desktop-electron/THIRD-PARTY-NOTICES +56 -0
- package/packages/desktop-electron/build/afterPack.cjs +147 -0
- package/packages/desktop-electron/package-lock.json +10071 -0
- package/packages/desktop-electron/package.json +170 -0
- package/packages/desktop-electron/resources/icons/mac/icon.icns +0 -0
- package/packages/desktop-electron/resources/icons/png/1024x1024.png +0 -0
- package/packages/desktop-electron/resources/icons/png/128x128.png +0 -0
- package/packages/desktop-electron/resources/icons/png/16x16.png +0 -0
- package/packages/desktop-electron/resources/icons/png/24x24.png +0 -0
- package/packages/desktop-electron/resources/icons/png/256x256.png +0 -0
- package/packages/desktop-electron/resources/icons/png/32x32.png +0 -0
- package/packages/desktop-electron/resources/icons/png/48x48.png +0 -0
- package/packages/desktop-electron/resources/icons/png/512x512.png +0 -0
- package/packages/desktop-electron/resources/icons/png/64x64.png +0 -0
- package/packages/desktop-electron/resources/icons/win/icon.ico +0 -0
- package/packages/desktop-electron/scripts/download-models.js +97 -0
- package/packages/desktop-electron/scripts/prepare-sandbox-bins.js +186 -0
- package/packages/desktop-electron/tests/main/ai-detection/additionalFunctions.test.ts +224 -0
- package/packages/desktop-electron/tests/main/ai-detection/checkOverridePrefix.test.ts +162 -0
- package/packages/desktop-electron/tests/main/ai-detection/classifyInput.test.ts +132 -0
- package/packages/desktop-electron/tests/main/ai-detection/detectTypos.test.ts +342 -0
- package/packages/desktop-electron/tests/main/ai-detection/fixtures/commands.ts +134 -0
- package/packages/desktop-electron/tests/main/ai-detection/fixtures/natural-language.ts +133 -0
- package/packages/desktop-electron/tests/main/ai-detection/fixtures/typos.ts +123 -0
- package/packages/desktop-electron/tests/main/ai-detection/hasValidSubcommand.test.ts +218 -0
- package/packages/desktop-electron/tests/main/ai-detection/isCommandNotFound.test.ts +117 -0
- package/packages/desktop-electron/tests/main/error-triage/buildTriagePrompt.test.ts +133 -0
- package/packages/desktop-electron/tests/main/error-triage/parseTriageResponse.test.ts +123 -0
- package/packages/desktop-electron/tests/main/terminal-bridge/battery-optimization.test.ts +243 -0
- package/packages/desktop-electron/tests/main/terminal-bridge/command-fast-track.test.ts +292 -0
- package/packages/desktop-electron/tests/main/terminal-bridge/default-cwd.test.ts +70 -0
- package/packages/desktop-electron/tests/setup.ts +274 -0
- package/packages/desktop-electron/tsconfig.json +18 -0
- package/packages/desktop-electron/tsconfig.main.json +20 -0
- package/packages/desktop-electron/vite.config.ts +19 -0
- package/packages/desktop-electron/vitest.config.ts +18 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "brosh-desktop",
|
|
3
|
+
"productName": "brosh",
|
|
4
|
+
"version": "0.2.2",
|
|
5
|
+
"description": "Desktop GUI for brosh - A terminal emulator with MCP support",
|
|
6
|
+
"author": "Ellery Familia",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "dist/main/index.js",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "ELECTRON_DEV=true npm run build:main && concurrently \"npm run watch:main\" \"npm run watch:renderer\" \"ELECTRON_DEV=true npm run electron\"",
|
|
12
|
+
"build": "npm run build:main && npm run build:renderer",
|
|
13
|
+
"build:main": "tsc -p tsconfig.main.json && cp src/main/preload.cjs dist/main/",
|
|
14
|
+
"build:renderer": "vite build",
|
|
15
|
+
"watch:main": "tsc -p tsconfig.main.json --watch",
|
|
16
|
+
"watch:renderer": "vite build --watch",
|
|
17
|
+
"electron": "electron .",
|
|
18
|
+
"start": "npm run build && electron .",
|
|
19
|
+
"prepackage": "node scripts/prepare-sandbox-bins.js && rm -rf node_modules/brosh && mkdir -p node_modules/brosh && cp -r ../../dist ../../package.json node_modules/brosh/",
|
|
20
|
+
"package": "npm run prepackage && npm run build && electron-builder",
|
|
21
|
+
"package:mac": "npm run prepackage && npm run build && electron-builder --mac",
|
|
22
|
+
"package:win": "npm run prepackage && npm run build && electron-builder --win",
|
|
23
|
+
"package:linux": "npm run prepackage && npm run build && electron-builder --linux",
|
|
24
|
+
"postpackage": "rm -rf node_modules/brosh && npm install",
|
|
25
|
+
"download-models": "node scripts/download-models.js",
|
|
26
|
+
"prepare-sandbox-bins": "node scripts/prepare-sandbox-bins.js",
|
|
27
|
+
"postinstall": "node scripts/download-models.js",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"test:coverage": "vitest run --coverage"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@huggingface/transformers": "^3.8.1",
|
|
34
|
+
"@monaco-editor/react": "^4.7.0",
|
|
35
|
+
"@tiptap/extension-image": "^3.19.0",
|
|
36
|
+
"@tiptap/extension-link": "^3.19.0",
|
|
37
|
+
"@tiptap/extension-placeholder": "^3.19.0",
|
|
38
|
+
"@tiptap/extension-table": "^3.19.0",
|
|
39
|
+
"@tiptap/extension-table-cell": "^3.19.0",
|
|
40
|
+
"@tiptap/extension-table-header": "^3.19.0",
|
|
41
|
+
"@tiptap/extension-table-row": "^3.19.0",
|
|
42
|
+
"@tiptap/extension-task-item": "^3.19.0",
|
|
43
|
+
"@tiptap/extension-task-list": "^3.19.0",
|
|
44
|
+
"@tiptap/pm": "^3.19.0",
|
|
45
|
+
"@tiptap/react": "^3.19.0",
|
|
46
|
+
"@tiptap/starter-kit": "^3.19.0",
|
|
47
|
+
"@xterm/addon-canvas": "^0.7.0",
|
|
48
|
+
"@xterm/addon-clipboard": "^0.2.0",
|
|
49
|
+
"@xterm/addon-fit": "^0.10.0",
|
|
50
|
+
"@xterm/addon-image": "^0.9.0",
|
|
51
|
+
"@xterm/addon-search": "^0.16.0",
|
|
52
|
+
"@xterm/addon-web-links": "^0.11.0",
|
|
53
|
+
"@xterm/addon-webgl": "^0.18.0",
|
|
54
|
+
"@xterm/xterm": "^5.5.0",
|
|
55
|
+
"brosh": "file:../..",
|
|
56
|
+
"chokidar": "^5.0.0",
|
|
57
|
+
"cli-highlight": "^2.1.11",
|
|
58
|
+
"electron-store": "^10.0.0",
|
|
59
|
+
"electron-updater": "^6.7.3",
|
|
60
|
+
"fastest-levenshtein": "^1.0.16",
|
|
61
|
+
"monaco-editor": "^0.55.1",
|
|
62
|
+
"monaco-vim": "^0.4.4",
|
|
63
|
+
"posthog-node": "^5.24.7",
|
|
64
|
+
"tiptap-markdown": "^0.9.0",
|
|
65
|
+
"ws": "^8.19.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/node": "^20.0.0",
|
|
69
|
+
"@types/react": "^18.2.0",
|
|
70
|
+
"@types/react-dom": "^18.2.0",
|
|
71
|
+
"@types/ws": "^8.18.1",
|
|
72
|
+
"@vitejs/plugin-react": "^4.2.0",
|
|
73
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
74
|
+
"concurrently": "^8.2.0",
|
|
75
|
+
"electron": "^33.0.0",
|
|
76
|
+
"electron-builder": "^25.0.0",
|
|
77
|
+
"react": "^18.2.0",
|
|
78
|
+
"react-dom": "^18.2.0",
|
|
79
|
+
"typescript": "^5.0.0",
|
|
80
|
+
"vite": "^5.0.0",
|
|
81
|
+
"vitest": "^2.0.0"
|
|
82
|
+
},
|
|
83
|
+
"build": {
|
|
84
|
+
"appId": "com.ellery.brosh",
|
|
85
|
+
"productName": "brosh",
|
|
86
|
+
"afterPack": "./build/afterPack.cjs",
|
|
87
|
+
"directories": {
|
|
88
|
+
"output": "release"
|
|
89
|
+
},
|
|
90
|
+
"files": [
|
|
91
|
+
"dist/**/*",
|
|
92
|
+
"resources/**/*",
|
|
93
|
+
"node_modules/**/*",
|
|
94
|
+
"!**/node_modules/brosh/packages/**",
|
|
95
|
+
"!**/node_modules/brosh/node_modules/**",
|
|
96
|
+
"!**/node_modules/brosh/docs/**",
|
|
97
|
+
"!**/node_modules/brosh/*.md",
|
|
98
|
+
"!**/node_modules/brosh/*.png",
|
|
99
|
+
"!**/node_modules/brosh/*.svg",
|
|
100
|
+
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
|
|
101
|
+
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
|
|
102
|
+
"!**/node_modules/*.d.ts",
|
|
103
|
+
"!**/node_modules/.bin",
|
|
104
|
+
"!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
|
|
105
|
+
"!.editorconfig",
|
|
106
|
+
"!**/._*",
|
|
107
|
+
"!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
|
|
108
|
+
"!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
|
|
109
|
+
"!**/{appveyor.yml,.travis.yml,circle.yml}",
|
|
110
|
+
"!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}",
|
|
111
|
+
"!**/node_modules/**/typescript/**",
|
|
112
|
+
"!**/node_modules/**/@types/**",
|
|
113
|
+
"!**/node_modules/**/eslint*/**",
|
|
114
|
+
"!**/node_modules/**/prettier*/**",
|
|
115
|
+
"!**/node_modules/onnxruntime-web/**"
|
|
116
|
+
],
|
|
117
|
+
"asarUnpack": [
|
|
118
|
+
"**/node_modules/node-pty/**",
|
|
119
|
+
"**/node_modules/onnxruntime-node/**",
|
|
120
|
+
"**/node_modules/@img/**"
|
|
121
|
+
],
|
|
122
|
+
"extraResources": [
|
|
123
|
+
{
|
|
124
|
+
"from": "models",
|
|
125
|
+
"to": "models",
|
|
126
|
+
"filter": [
|
|
127
|
+
"**/*"
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"from": "resources/bin",
|
|
132
|
+
"to": "bin",
|
|
133
|
+
"filter": [
|
|
134
|
+
"**/*"
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"publish": [
|
|
139
|
+
{
|
|
140
|
+
"provider": "github",
|
|
141
|
+
"owner": "elleryfamilia",
|
|
142
|
+
"repo": "brosh"
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
"mac": {
|
|
146
|
+
"category": "public.app-category.developer-tools",
|
|
147
|
+
"icon": "resources/icons/mac/icon.icns",
|
|
148
|
+
"target": [
|
|
149
|
+
{
|
|
150
|
+
"target": "dmg"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"target": "zip"
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
"extendInfo": {
|
|
157
|
+
"NSSupportsAutomaticTermination": false,
|
|
158
|
+
"NSSupportsSuddenTermination": false
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"win": {
|
|
162
|
+
"target": "nsis",
|
|
163
|
+
"icon": "resources/icons/png/512x512.png"
|
|
164
|
+
},
|
|
165
|
+
"linux": {
|
|
166
|
+
"icon": "resources/icons/png",
|
|
167
|
+
"category": "Development"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Downloads the brosh-ky ML model files from HuggingFace.
|
|
5
|
+
* Uses only Node.js built-in modules (no extra dependencies).
|
|
6
|
+
*
|
|
7
|
+
* Usage: node scripts/download-models.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createWriteStream, existsSync, mkdirSync, statSync } from 'fs';
|
|
11
|
+
import { dirname, join } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import https from 'https';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const MODELS_DIR = join(__dirname, '..', 'models', 'brosh-ky');
|
|
17
|
+
const BASE_URL = 'https://huggingface.co/elleryfamilia/broshky/resolve/main';
|
|
18
|
+
|
|
19
|
+
const FILES = [
|
|
20
|
+
'config.json',
|
|
21
|
+
'tokenizer.json',
|
|
22
|
+
'tokenizer_config.json',
|
|
23
|
+
'onnx/model.onnx',
|
|
24
|
+
'onnx/model.onnx.data',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function download(url, dest) {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const file = createWriteStream(dest);
|
|
30
|
+
const request = (reqUrl) => {
|
|
31
|
+
https.get(reqUrl, (res) => {
|
|
32
|
+
// Follow redirects (HuggingFace uses 302s to CDN)
|
|
33
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
34
|
+
res.resume();
|
|
35
|
+
const location = res.headers.location;
|
|
36
|
+
// Resolve relative redirects against the current URL
|
|
37
|
+
const nextUrl = location.startsWith('http') ? location : new URL(location, reqUrl).href;
|
|
38
|
+
request(nextUrl);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (res.statusCode !== 200) {
|
|
42
|
+
file.close();
|
|
43
|
+
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const totalBytes = parseInt(res.headers['content-length'], 10) || 0;
|
|
47
|
+
let downloadedBytes = 0;
|
|
48
|
+
res.on('data', (chunk) => {
|
|
49
|
+
downloadedBytes += chunk.length;
|
|
50
|
+
if (totalBytes > 1024 * 1024) {
|
|
51
|
+
const pct = totalBytes ? ((downloadedBytes / totalBytes) * 100).toFixed(1) : '?';
|
|
52
|
+
process.stdout.write(`\r ${pct}% (${(downloadedBytes / 1024 / 1024).toFixed(1)}MB)`);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
res.pipe(file);
|
|
56
|
+
file.on('finish', () => {
|
|
57
|
+
file.close();
|
|
58
|
+
if (totalBytes > 1024 * 1024) process.stdout.write('\n');
|
|
59
|
+
resolve();
|
|
60
|
+
});
|
|
61
|
+
}).on('error', (err) => {
|
|
62
|
+
file.close();
|
|
63
|
+
reject(err);
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
request(url);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function main() {
|
|
71
|
+
console.log(`Downloading brosh-ky model to ${MODELS_DIR}\n`);
|
|
72
|
+
mkdirSync(MODELS_DIR, { recursive: true });
|
|
73
|
+
|
|
74
|
+
let skipped = 0;
|
|
75
|
+
let downloaded = 0;
|
|
76
|
+
|
|
77
|
+
for (const file of FILES) {
|
|
78
|
+
const dest = join(MODELS_DIR, file);
|
|
79
|
+
if (existsSync(dest) && statSync(dest).size > 0) {
|
|
80
|
+
console.log(` [skip] ${file} (already exists)`);
|
|
81
|
+
skipped++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
85
|
+
const url = `${BASE_URL}/${file}`;
|
|
86
|
+
console.log(` [download] ${file}`);
|
|
87
|
+
await download(url, dest);
|
|
88
|
+
downloaded++;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log(`\nDone. ${downloaded} downloaded, ${skipped} skipped.`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
main().catch((err) => {
|
|
95
|
+
console.error(`\nFailed to download models: ${err.message}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Downloads and statically compiles the latest socat and bwrap (bubblewrap)
|
|
5
|
+
* for bundling with the Electron app (Linux only).
|
|
6
|
+
*
|
|
7
|
+
* Static binaries are portable across Linux distros (Ubuntu, Arch, Fedora, etc.)
|
|
8
|
+
* regardless of installed system libraries.
|
|
9
|
+
*
|
|
10
|
+
* Build deps (CI installs these):
|
|
11
|
+
* sudo apt-get install -y meson ninja-build pkg-config libcap-dev
|
|
12
|
+
*
|
|
13
|
+
* Licensing:
|
|
14
|
+
* - socat: GPL-2.0 (spawned as child process = "mere aggregation")
|
|
15
|
+
* - bwrap (bubblewrap): LGPL-2.1 (spawned as child process)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { execFileSync, execSync } from "child_process";
|
|
19
|
+
import {
|
|
20
|
+
mkdirSync,
|
|
21
|
+
mkdtempSync,
|
|
22
|
+
rmSync,
|
|
23
|
+
existsSync,
|
|
24
|
+
copyFileSync,
|
|
25
|
+
chmodSync,
|
|
26
|
+
writeFileSync,
|
|
27
|
+
createWriteStream,
|
|
28
|
+
} from "fs";
|
|
29
|
+
import { join, dirname } from "path";
|
|
30
|
+
import { fileURLToPath } from "url";
|
|
31
|
+
import { tmpdir } from "os";
|
|
32
|
+
import { Readable } from "stream";
|
|
33
|
+
import { finished } from "stream/promises";
|
|
34
|
+
|
|
35
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
const targetDir = join(__dirname, "..", "resources", "bin");
|
|
37
|
+
|
|
38
|
+
if (process.platform !== "linux") {
|
|
39
|
+
console.log("[sandbox-bins] Skipping: not Linux (sandbox is Linux-only)");
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── helpers ─────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
async function download(url, dest) {
|
|
46
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
47
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
48
|
+
const ws = createWriteStream(dest);
|
|
49
|
+
await finished(Readable.fromWeb(res.body).pipe(ws));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function run(cmd, opts = {}) {
|
|
53
|
+
try {
|
|
54
|
+
return execSync(cmd, { encoding: "utf8", stdio: "pipe", ...opts });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (err.stderr) console.error(err.stderr);
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function requireTool(name) {
|
|
62
|
+
try {
|
|
63
|
+
execFileSync("which", [name], { stdio: "pipe" });
|
|
64
|
+
} catch {
|
|
65
|
+
console.error(`[sandbox-bins] Required build tool not found: ${name}`);
|
|
66
|
+
console.error(" Install with: sudo apt-get install -y meson ninja-build pkg-config libcap-dev");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── preflight ───────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
for (const tool of ["meson", "ninja", "pkg-config", "gcc", "make", "strip"]) {
|
|
74
|
+
requireTool(tool);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
mkdirSync(targetDir, { recursive: true });
|
|
78
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "sandbox-bins-"));
|
|
79
|
+
const manifest = {};
|
|
80
|
+
|
|
81
|
+
// ── bubblewrap ──────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
async function buildBwrap() {
|
|
84
|
+
console.log("[sandbox-bins] Checking latest bubblewrap release...");
|
|
85
|
+
const res = await fetch("https://api.github.com/repos/containers/bubblewrap/releases/latest");
|
|
86
|
+
if (!res.ok) throw new Error(`GitHub API: ${res.status}`);
|
|
87
|
+
const { tag_name } = await res.json();
|
|
88
|
+
const version = tag_name.replace(/^v/, "");
|
|
89
|
+
console.log(`[sandbox-bins] bubblewrap v${version}`);
|
|
90
|
+
|
|
91
|
+
const tarball = join(tmpDir, `bubblewrap-${version}.tar.xz`);
|
|
92
|
+
await download(
|
|
93
|
+
`https://github.com/containers/bubblewrap/releases/download/${tag_name}/bubblewrap-${version}.tar.xz`,
|
|
94
|
+
tarball,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
execFileSync("tar", ["xf", tarball, "-C", tmpDir]);
|
|
98
|
+
const src = join(tmpDir, `bubblewrap-${version}`);
|
|
99
|
+
|
|
100
|
+
console.log("[sandbox-bins] Building bwrap (static)...");
|
|
101
|
+
const buildDir = join(src, "builddir");
|
|
102
|
+
run(
|
|
103
|
+
`meson setup ${buildDir} --prefer-static --default-library=static -Dman=disabled -Dtests=false`,
|
|
104
|
+
{ cwd: src, env: { ...process.env, LDFLAGS: "-static" } },
|
|
105
|
+
);
|
|
106
|
+
run(`ninja -C ${buildDir}`, { cwd: src });
|
|
107
|
+
|
|
108
|
+
const bin = join(buildDir, "bwrap");
|
|
109
|
+
if (!existsSync(bin)) throw new Error("bwrap binary not produced");
|
|
110
|
+
|
|
111
|
+
execFileSync("strip", [bin]);
|
|
112
|
+
copyFileSync(bin, join(targetDir, "bwrap"));
|
|
113
|
+
chmodSync(join(targetDir, "bwrap"), 0o755);
|
|
114
|
+
|
|
115
|
+
manifest.bwrap = { package: "bubblewrap", version, builtAt: new Date().toISOString() };
|
|
116
|
+
console.log(`[sandbox-bins] bwrap v${version} built`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── socat ───────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
async function buildSocat() {
|
|
122
|
+
console.log("[sandbox-bins] Checking latest socat release...");
|
|
123
|
+
const res = await fetch("http://www.dest-unreach.org/socat/download/");
|
|
124
|
+
if (!res.ok) throw new Error(`socat downloads: ${res.status}`);
|
|
125
|
+
const html = await res.text();
|
|
126
|
+
|
|
127
|
+
// Parse directory listing for socat-X.Y.Z.tar.gz (exclude socat2)
|
|
128
|
+
const found = [...html.matchAll(/href="socat-([\d.]+)\.tar\.gz"/g)]
|
|
129
|
+
.map((m) => m[1])
|
|
130
|
+
.sort((a, b) => {
|
|
131
|
+
const pa = a.split(".").map(Number);
|
|
132
|
+
const pb = b.split(".").map(Number);
|
|
133
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
134
|
+
const diff = (pa[i] || 0) - (pb[i] || 0);
|
|
135
|
+
if (diff !== 0) return diff;
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
138
|
+
});
|
|
139
|
+
if (found.length === 0) throw new Error("No socat versions found on download page");
|
|
140
|
+
const version = found.at(-1);
|
|
141
|
+
console.log(`[sandbox-bins] socat v${version}`);
|
|
142
|
+
|
|
143
|
+
const tarball = join(tmpDir, `socat-${version}.tar.gz`);
|
|
144
|
+
await download(
|
|
145
|
+
`http://www.dest-unreach.org/socat/download/socat-${version}.tar.gz`,
|
|
146
|
+
tarball,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
execFileSync("tar", ["xzf", tarball, "-C", tmpDir]);
|
|
150
|
+
const src = join(tmpDir, `socat-${version}`);
|
|
151
|
+
|
|
152
|
+
// Static build. Disable openssl/readline/libwrap — not needed for the
|
|
153
|
+
// sandbox use case (unix domain socket proxying) and avoids needing
|
|
154
|
+
// their static libraries on every build system.
|
|
155
|
+
console.log("[sandbox-bins] Building socat (static)...");
|
|
156
|
+
run(
|
|
157
|
+
`./configure --disable-openssl --disable-readline --disable-libwrap LDFLAGS="-static"`,
|
|
158
|
+
{ cwd: src },
|
|
159
|
+
);
|
|
160
|
+
run(`make -j$(nproc)`, { cwd: src });
|
|
161
|
+
|
|
162
|
+
const bin = join(src, "socat");
|
|
163
|
+
if (!existsSync(bin)) throw new Error("socat binary not produced");
|
|
164
|
+
|
|
165
|
+
execFileSync("strip", [bin]);
|
|
166
|
+
copyFileSync(bin, join(targetDir, "socat"));
|
|
167
|
+
chmodSync(join(targetDir, "socat"), 0o755);
|
|
168
|
+
|
|
169
|
+
manifest.socat = { package: "socat", version, builtAt: new Date().toISOString() };
|
|
170
|
+
console.log(`[sandbox-bins] socat v${version} built`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── main ────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
await buildBwrap();
|
|
177
|
+
await buildSocat();
|
|
178
|
+
|
|
179
|
+
writeFileSync(join(targetDir, "versions.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
180
|
+
console.log("[sandbox-bins] Wrote versions.json");
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error(`[sandbox-bins] FAILED: ${err.message}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
} finally {
|
|
185
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
186
|
+
}
|