@renbostudios/edge-ota 0.3.1 → 0.4.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/README.md +508 -12
- package/dist/index.js +1149 -849
- package/package.json +20 -15
- package/dist/core/crypto.js +0 -87
- package/dist/core/index.js +0 -2
- package/dist/core/manifest.js +0 -108
package/README.md
CHANGED
|
@@ -1,26 +1,522 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://ota.renbo.site/og.png" alt="Edge OTA" width="100%">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">
|
|
6
|
+
<code>edge-ota</code>
|
|
7
|
+
</h1>
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>Zero-SDK, self-hostable OTA update CLI for Expo React Native</strong><br>
|
|
11
|
+
Push signed JavaScript bundles to any EdgeOTA server.
|
|
12
|
+
</p>
|
|
6
13
|
|
|
7
|
-
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/@renbostudios/edge-ota"><img src="https://img.shields.io/npm/v/@renbostudios/edge-ota?style=flat-square&color=81C784" alt="npm version"></a>
|
|
16
|
+
<a href="https://github.com/renbostudios/edge-ota-cli/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@renbostudios/edge-ota?style=flat-square&color=FFAA00" alt="license"></a>
|
|
17
|
+
<a href="https://www.npmjs.com/package/@renbostudios/edge-ota"><img src="https://img.shields.io/npm/dm/@renbostudios/edge-ota?style=flat-square&color=81C784" alt="downloads"></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<a href="https://github.com/renbostudios/edge-ota-cli">GitHub</a> · <a href="https://ota.renbo.site">Dashboard</a> · <a href="https://ota.renbo.site/docs">Docs</a> · <a href="https://pd1mvpk7lt.ufs.sh/f/6uDGWrRxa3ipmhByNjc2TlHph0oA4dsWRFUuqZMan18Gwb7g">Video Tutorial</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<br>
|
|
8
27
|
|
|
9
28
|
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
╔══════════════════════════════════════════════════════════════════╗
|
|
30
|
+
║ ║
|
|
31
|
+
║ ┌─┐┬┌─┬┌┬┐┌─┐┌─┐┬ ┬┌─┐┌┐┌┌┬┐┌─┐ ║
|
|
32
|
+
║ └─┐││ ┤│││├┤ │ ┬├─┤├─┤│││ │ ┤ └─┐ ║
|
|
33
|
+
║ └─┘┴└─┴┴ ┴└ └─┘┴ ┴┴ ┴┘└┘─┴┘└─┘└─┘ ║
|
|
34
|
+
║ ║
|
|
35
|
+
║ Push signed OTA bundles to the edge. ║
|
|
36
|
+
║ Zero SDK. Zero egress. Full control. ║
|
|
37
|
+
║ ║
|
|
38
|
+
╚══════════════════════════════════════════════════════════════════╝
|
|
16
39
|
```
|
|
17
40
|
|
|
18
|
-
|
|
41
|
+
<br>
|
|
42
|
+
|
|
43
|
+
## ⚡ Quick Start
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
47
|
+
│ STEP 01 │
|
|
48
|
+
│ Install the CLI globally │
|
|
49
|
+
└──────────────────────────────────────────────────────────────┘
|
|
50
|
+
```
|
|
19
51
|
|
|
20
52
|
```bash
|
|
21
53
|
npm install -g @renbostudios/edge-ota
|
|
22
54
|
```
|
|
23
55
|
|
|
56
|
+
```
|
|
57
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
58
|
+
│ STEP 02 │
|
|
59
|
+
│ Log in to your EdgeOTA account │
|
|
60
|
+
└──────────────────────────────────────────────────────────────┘
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
edge-ota login
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
69
|
+
│ STEP 03 │
|
|
70
|
+
│ Register your project & generate signing keys │
|
|
71
|
+
└──────────────────────────────────────────────────────────────┘
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
edge-ota init
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
80
|
+
│ STEP 04 │
|
|
81
|
+
│ ⚠ REBUILD YOUR NATIVE APP (required!) │
|
|
82
|
+
└──────────────────────────────────────────────────────────────┘
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx expo prebuild --clean
|
|
87
|
+
npx expo run:ios
|
|
88
|
+
npx expo run:android
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
93
|
+
│ STEP 05 │
|
|
94
|
+
│ Push an OTA update 🚀 │
|
|
95
|
+
└──────────────────────────────────────────────────────────────┘
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
edge-ota push
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> **📹 Prefer video?** Watch the full setup tutorial below:
|
|
103
|
+
|
|
104
|
+
<p align="center">
|
|
105
|
+
<video src="https://pd1mvpk7lt.ufs.sh/f/6uDGWrRxa3ipmhByNjc2TlHph0oA4dsWRFUuqZMan18Gwb7g" controls width="100%" style="max-width: 640px; border: 1px solid #333; border-radius: 4px; background: #000;">
|
|
106
|
+
Your browser does not support the video tag.
|
|
107
|
+
</video>
|
|
108
|
+
</p>
|
|
109
|
+
|
|
110
|
+
<br>
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
<br>
|
|
115
|
+
|
|
116
|
+
## 📦 Commands
|
|
117
|
+
|
|
118
|
+
### `edge-ota login`
|
|
119
|
+
|
|
120
|
+
Authenticate with your EdgeOTA account. Credentials are stored globally — no per-project `.env` files needed.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
edge-ota login # default server
|
|
124
|
+
edge-ota login --server https://my-server.com # custom server
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
┌──────────────────────────────────────────────┐
|
|
129
|
+
│ 📁 Stored at: │
|
|
130
|
+
│ ~/.config/edge-ota/config.json │
|
|
131
|
+
│ │
|
|
132
|
+
│ Contains: auth token, email, server URL │
|
|
133
|
+
└──────────────────────────────────────────────┘
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### `edge-ota logout`
|
|
139
|
+
|
|
140
|
+
Remove stored credentials from this machine.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
edge-ota logout
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### `edge-ota init`
|
|
149
|
+
|
|
150
|
+
The all-in-one setup command. Registers your project, generates ECDSA P-256 signing keys, and configures `app.json`.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
edge-ota init # interactive setup
|
|
154
|
+
edge-ota init --server https://my-server.com # override server
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**What happens under the hood:**
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
① Fetches your existing projects from the server
|
|
161
|
+
② Lets you select an existing project or create a new one
|
|
162
|
+
③ Generates an ECDSA P-256 key pair
|
|
163
|
+
④ Registers the project with your public key
|
|
164
|
+
⑤ Saves private key → ~/.config/edge-ota/keys/<projectId>.key
|
|
165
|
+
⑥ Updates app.json with server URL, public key, and headers
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
> ⚠ **After `init`, you MUST rebuild your native app.** See [Rebuild Required](#-rebuild-required) below.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### `edge-ota push`
|
|
173
|
+
|
|
174
|
+
Export your Expo bundle and publish an OTA update.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
edge-ota push # all platforms, production channel
|
|
178
|
+
edge-ota push --channel staging # deploy to staging
|
|
179
|
+
edge-ota push --runtime 1.0.0 # target specific runtime version
|
|
180
|
+
edge-ota push --runtime 1.0.5,1.0.6,1.0.7 # target multiple runtime versions (matrix deployment)
|
|
181
|
+
edge-ota push --platform ios # iOS only
|
|
182
|
+
edge-ota push --platform android # Android only
|
|
183
|
+
edge-ota push --skip-export # use existing ./dist
|
|
184
|
+
edge-ota push --dry-run # build & sign, don't upload
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**What happens under the hood:**
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
① Runs `npx expo export` to create the JS bundle
|
|
191
|
+
② Collects all assets from ./dist
|
|
192
|
+
③ For each platform (ios/android):
|
|
193
|
+
→ Locates the platform-specific bundle
|
|
194
|
+
→ Signs the payload with your ECDSA private key
|
|
195
|
+
→ Uploads bundle + signature to the server
|
|
196
|
+
④ Server verifies the signature before storing
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**How it reads your config:**
|
|
200
|
+
|
|
201
|
+
| Source | What it reads |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `expo.updates.url` | Server URL + Project ID |
|
|
204
|
+
| `expo.runtimeVersion` | Runtime version (string or policy) |
|
|
205
|
+
| `~/.config/edge-ota/keys/<id>.key` | ECDSA private key |
|
|
206
|
+
| `expo.extra.edgeOtaServer` | Server URL override |
|
|
207
|
+
|
|
24
208
|
---
|
|
25
209
|
|
|
26
|
-
|
|
210
|
+
### `edge-ota status`
|
|
211
|
+
|
|
212
|
+
List recent deployments for the current project.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
edge-ota status
|
|
216
|
+
edge-ota status --limit 20 # show more releases
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### `edge-ota keygen`
|
|
222
|
+
|
|
223
|
+
Generate a new ECDSA P-256 key pair and print it to stdout. Does not write any files.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
edge-ota keygen
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Useful for manual key rotation or generating keys for server-side signing.
|
|
230
|
+
|
|
231
|
+
<br>
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
<br>
|
|
236
|
+
|
|
237
|
+
## 🔄 Rebuild Required
|
|
238
|
+
|
|
239
|
+
After running `edge-ota init`, you **must** rebuild your native app. The `expo-updates` native module reads its config from the **native binary**, not from `app.json` at runtime.
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
npx expo prebuild --clean
|
|
243
|
+
npx expo run:ios
|
|
244
|
+
npx expo run:android
|
|
245
|
+
|
|
246
|
+
# or via EAS
|
|
247
|
+
eas build --profile production
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
╔══════════════════════════════════════════════════════════════════╗
|
|
252
|
+
║ ⚠ SKIP THIS STEP AND YOU'LL SEE: ║
|
|
253
|
+
║ ║
|
|
254
|
+
║ "Failed to check for update" ║
|
|
255
|
+
║ ║
|
|
256
|
+
║ The native module has no valid server URL to reach because ║
|
|
257
|
+
║ it was never embedded in the binary. ║
|
|
258
|
+
╚══════════════════════════════════════════════════════════════════╝
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
You must also rebuild after any change to `expo.updates.url` or `expo.runtimeVersion` in `app.json`.
|
|
262
|
+
|
|
263
|
+
<br>
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
<br>
|
|
268
|
+
|
|
269
|
+
## 🔁 How It Works
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
Developer Machine EdgeOTA Server User's Phone
|
|
273
|
+
───────────────── ────────────── ────────────
|
|
274
|
+
|
|
275
|
+
edge-ota push ──────────────► POST /api/updates
|
|
276
|
+
(ECDSA verified ✓)
|
|
277
|
+
(stores bundle)
|
|
278
|
+
◄──────────── GET /api/updates
|
|
279
|
+
(expo-updates SDK)
|
|
280
|
+
────────────► signed manifest
|
|
281
|
+
◄──────────── GET /api/assets/:hash
|
|
282
|
+
(downloads bundle)
|
|
283
|
+
(reloads app ✓)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
1. `edge-ota push` runs `expo export` and uploads the Hermes bundle + ECDSA signature
|
|
287
|
+
2. The server **verifies** the ECDSA P-256 signature before storing anything
|
|
288
|
+
3. When users open the app, `expo-updates` checks for a new manifest
|
|
289
|
+
4. The server responds with a signed multipart manifest (Expo Updates Protocol v1)
|
|
290
|
+
5. `expo-updates` downloads and applies the update automatically
|
|
291
|
+
|
|
292
|
+
<br>
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
<br>
|
|
297
|
+
|
|
298
|
+
## ⚙ What `init` Writes to `app.json`
|
|
299
|
+
|
|
300
|
+
```json
|
|
301
|
+
{
|
|
302
|
+
"expo": {
|
|
303
|
+
"updates": {
|
|
304
|
+
"url": "https://your-server.com/api/projects/<projectId>/updates",
|
|
305
|
+
"checkAutomatically": "ON_LOAD",
|
|
306
|
+
"fallbackToCacheTimeout": 30000,
|
|
307
|
+
"requestHeaders": {
|
|
308
|
+
"expo-channel-name": "production"
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
"runtimeVersion": "1.0.0",
|
|
312
|
+
"extra": {
|
|
313
|
+
"edgeOtaServer": "https://your-server.com",
|
|
314
|
+
"edgeOtaPublicKey": "-----BEGIN PUBLIC KEY-----\n..."
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
> `runtimeVersion` is **not modified** by the CLI — it must already exist in your `app.json`. Supports both static strings and policy objects:
|
|
321
|
+
|
|
322
|
+
```json
|
|
323
|
+
"runtimeVersion": "1.0.0"
|
|
324
|
+
```
|
|
325
|
+
```json
|
|
326
|
+
"runtimeVersion": { "policy": "appVersion" }
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
<br>
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
<br>
|
|
334
|
+
|
|
335
|
+
## 🗂 Global Storage
|
|
336
|
+
|
|
337
|
+
All credentials and keys are stored **outside** your project directory:
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
~/.config/edge-ota/
|
|
341
|
+
├── config.json ← auth token, email, server URL
|
|
342
|
+
├── keys/
|
|
343
|
+
│ └── <projectId>.key ← ECDSA private key (per-project)
|
|
344
|
+
└── update-check.json ← auto-update check timestamp
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
349
|
+
│ 🔒 Nothing sensitive is ever stored in your project repo. │
|
|
350
|
+
└──────────────────────────────────────────────────────────────┘
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
<br>
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
<br>
|
|
358
|
+
|
|
359
|
+
## 🧪 Environment Variables
|
|
360
|
+
|
|
361
|
+
| Variable | Purpose |
|
|
362
|
+
|---|---|
|
|
363
|
+
| `EDGE_OTA_TOKEN` | Override the auth token (for CI/CD pipelines) |
|
|
364
|
+
| `EDGE_OTA_UPDATING` | Internal — prevents recursive auto-updates |
|
|
365
|
+
|
|
366
|
+
<br>
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
<br>
|
|
371
|
+
|
|
372
|
+
## 🔄 Auto-Update
|
|
373
|
+
|
|
374
|
+
The CLI checks for newer versions on npm every 15 minutes. If a newer version is found, it auto-installs globally. This happens silently in the background and never blocks command execution.
|
|
375
|
+
|
|
376
|
+
To disable: set `EDGE_OTA_UPDATING=true` in your environment.
|
|
377
|
+
|
|
378
|
+
<br>
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
<br>
|
|
383
|
+
|
|
384
|
+
## 🔐 Security
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
388
|
+
│ │
|
|
389
|
+
│ ✅ ECDSA P-256 signing on every bundle upload │
|
|
390
|
+
│ ✅ Server rejects invalid or missing signatures │
|
|
391
|
+
│ ✅ Private keys stored with 0600 permissions │
|
|
392
|
+
│ ✅ Session tokens expire after 24 hours │
|
|
393
|
+
│ ✅ Private keys never leave your machine │
|
|
394
|
+
│ │
|
|
395
|
+
└──────────────────────────────────────────────────────────────┘
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
<br>
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
<br>
|
|
403
|
+
|
|
404
|
+
## 🖥 Server Setup
|
|
405
|
+
|
|
406
|
+
The EdgeOTA server is a standard Node.js/Express app. Self-host it or use the managed cloud service at [ota.renbo.site](https://ota.renbo.site).
|
|
407
|
+
|
|
408
|
+
### Docker
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
docker pull ghcr.io/itskodaaa/edge-ota-backend:latest
|
|
412
|
+
docker run -p 3000:3000 -e DATABASE_URL=... ghcr.io/itskodaaa/edge-ota-backend
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Docker Compose
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
git clone https://github.com/renbostudios/edge-ota-cli
|
|
419
|
+
cd edge-ota
|
|
420
|
+
docker-compose up -d
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Manual
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
git clone https://github.com/renbostudios/edge-ota-cli
|
|
427
|
+
cd edge-ota
|
|
428
|
+
pnpm install
|
|
429
|
+
pnpm --filter @renbostudios/edge-ota-server-node dev
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
<br>
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
<br>
|
|
437
|
+
|
|
438
|
+
## 🐛 Troubleshooting
|
|
439
|
+
|
|
440
|
+
<details>
|
|
441
|
+
<summary><strong>"Failed to check for update" on app launch</strong></summary>
|
|
442
|
+
|
|
443
|
+
<br>
|
|
444
|
+
|
|
445
|
+
The `expo-updates` native module can't reach your server because the URL was never embedded in the binary.
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
npx expo prebuild --clean
|
|
449
|
+
npx expo run:ios
|
|
450
|
+
npx expo run:android
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
</details>
|
|
454
|
+
|
|
455
|
+
<details>
|
|
456
|
+
<summary><strong>Updates work on iOS but not Android</strong></summary>
|
|
457
|
+
|
|
458
|
+
<br>
|
|
459
|
+
|
|
460
|
+
The Android native project may still have the default Expo Updates URL. Regenerate and rebuild:
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
npx expo prebuild --clean
|
|
464
|
+
npx expo run:android
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Verify by checking `android/app/src/main/AndroidManifest.xml` for the `EXPO_UPDATE_URL` meta-data entry.
|
|
468
|
+
|
|
469
|
+
</details>
|
|
470
|
+
|
|
471
|
+
<details>
|
|
472
|
+
<summary><strong>Updates stopped working after a version bump</strong></summary>
|
|
473
|
+
|
|
474
|
+
<br>
|
|
475
|
+
|
|
476
|
+
If `runtimeVersion` uses the `appVersion` policy, every version bump changes the runtime version. Push a new update after bumping, or switch to a static string.
|
|
477
|
+
|
|
478
|
+
</details>
|
|
479
|
+
|
|
480
|
+
<details>
|
|
481
|
+
<summary><strong>"No signing key found" error on push</strong></summary>
|
|
482
|
+
|
|
483
|
+
<br>
|
|
484
|
+
|
|
485
|
+
Run `edge-ota init` to generate keys, or use `edge-ota keygen` to generate a new pair and register it manually.
|
|
486
|
+
|
|
487
|
+
</details>
|
|
488
|
+
|
|
489
|
+
<br>
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
<br>
|
|
494
|
+
|
|
495
|
+
## 📂 Project Structure
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
edge-ota-cli/
|
|
499
|
+
├── apps/
|
|
500
|
+
│ ├── cli/ ← CLI tool (you are here)
|
|
501
|
+
│ ├── server-node/ ← Express + TypeScript API
|
|
502
|
+
│ ├── worker-oss/ ← Cloudflare Worker (open source)
|
|
503
|
+
│ ├── dashboard/ ← SvelteKit frontend
|
|
504
|
+
│ └── core/ ← Shared crypto/manifest logic
|
|
505
|
+
└── packages/
|
|
506
|
+
└── core/ ← @renbostudios/edge-ota-core
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
<br>
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
<br>
|
|
514
|
+
|
|
515
|
+
<p align="center">
|
|
516
|
+
<strong>Built by</strong><br>
|
|
517
|
+
<a href="https://renbostudios.com">Renbo Studios</a> — Mobile App Development Studio
|
|
518
|
+
</p>
|
|
519
|
+
|
|
520
|
+
<p align="center">
|
|
521
|
+
<sub>MIT License · <code>@renbostudios/edge-ota</code></sub>
|
|
522
|
+
</p>
|