@renbostudios/edge-ota 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +5 -2
  2. package/dist/index.js +84 -13
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -30,15 +30,18 @@ edge-ota status
30
30
 
31
31
  ### `edge-ota init [options]`
32
32
 
33
- Generates an ECDSA P-256 key pair, writes `edge-ota.config.json` and `.edge-ota.private.key`, and auto-adds the private key to `.gitignore`.
33
+ Generates an ECDSA P-256 key pair, writes `edge-ota.config.json` and `.edge-ota.private.key`, auto-adds the private key to `.gitignore`, and automatically updates your Expo `app.json` configuration file with the new updates URL.
34
+
35
+ If `-s` or `--server` is not specified, you will be prompted interactively to enter the URL of your hosted or local EdgeOTA server.
34
36
 
35
37
  | Option | Default | Description |
36
38
  |--------|---------|-------------|
37
- | `-s, --server <url>` | `http://localhost:3000` | Your EdgeOTA server URL |
39
+ | `-s, --server <url>` | — | Your EdgeOTA server URL (prompts if omitted) |
38
40
 
39
41
  **Output:**
40
42
  - `edge-ota.config.json` — server URL + public key (safe to commit)
41
43
  - `.edge-ota.private.key` — private signing key (**never commit this**)
44
+ - **Auto-configured:** `app.json` is modified to point `expo.updates.url` to your server's `/api/updates` endpoint.
42
45
 
43
46
  ---
44
47
 
package/dist/index.js CHANGED
@@ -30,7 +30,44 @@ import fs from "fs";
30
30
  import path from "path";
31
31
  import { createWriteStream } from "fs";
32
32
  import archiver from "archiver";
33
+ import readline from "readline";
33
34
  import { generateECDSAKeyPair, signPayload, sha256Hex } from "./core/index.js";
35
+ function askQuestion(query) {
36
+ const rl = readline.createInterface({
37
+ input: process.stdin,
38
+ output: process.stdout
39
+ });
40
+ return new Promise((resolve) => {
41
+ rl.question(query, (answer) => {
42
+ rl.close();
43
+ resolve(answer.trim());
44
+ });
45
+ });
46
+ }
47
+ function updateAppJson(cwd, serverUrl) {
48
+ const appJsonPath = path.resolve(cwd, "app.json");
49
+ if (!fs.existsSync(appJsonPath)) {
50
+ console.log("⚠️ app.json not found in this directory. Skipping auto-update.");
51
+ return;
52
+ }
53
+ try {
54
+ const raw = fs.readFileSync(appJsonPath, "utf-8");
55
+ const data = JSON.parse(raw);
56
+ if (!data.expo) {
57
+ data.expo = {};
58
+ }
59
+ if (!data.expo.updates) {
60
+ data.expo.updates = {};
61
+ }
62
+ const updateUrl = `${serverUrl.replace(/\/$/, "")}/api/updates`;
63
+ data.expo.updates.url = updateUrl;
64
+ fs.writeFileSync(appJsonPath, JSON.stringify(data, null, 2), "utf-8");
65
+ console.log(`✅ Updated app.json: expo.updates.url set to "${updateUrl}"`);
66
+ }
67
+ catch (error) {
68
+ console.error(`❌ Failed to update app.json: ${error.message}`);
69
+ }
70
+ }
34
71
  function loadConfig(cwd) {
35
72
  const configPath = path.resolve(cwd, "edge-ota.config.json");
36
73
  if (!fs.existsSync(configPath)) {
@@ -66,6 +103,7 @@ async function hashFile(filePath) {
66
103
  }
67
104
  const CONTENT_TYPES = {
68
105
  ".js": "application/javascript",
106
+ ".hbc": "application/javascript",
69
107
  ".json": "application/json",
70
108
  ".png": "image/png",
71
109
  ".jpg": "image/jpeg",
@@ -105,19 +143,22 @@ async function collectAssets(distDir) {
105
143
  }
106
144
  // ─── Detect the JS bundle within the Expo export output ──────────────────────
107
145
  function findBundle(distDir, platform) {
108
- // Expo SDK 50+ writes: dist/_expo/static/js/<platform>/<hash>.js
146
+ // Expo SDK 50+ writes: dist/_expo/static/js/<platform>/<hash>.js (or .hbc)
109
147
  const expoStaticJs = path.join(distDir, "_expo", "static", "js", platform);
110
148
  if (fs.existsSync(expoStaticJs)) {
111
- const files = fs.readdirSync(expoStaticJs).filter(f => f.endsWith(".js"));
149
+ const files = fs.readdirSync(expoStaticJs).filter(f => f.endsWith(".js") || f.endsWith(".hbc"));
112
150
  if (files.length)
113
151
  return path.join(expoStaticJs, files[0]);
114
152
  }
115
153
  // Fallback: older SDK flat layout
116
- const flat = path.join(distDir, `index.${platform}.js`);
117
- if (fs.existsSync(flat))
118
- return flat;
119
- // Any .js at root
120
- const rootJs = fs.readdirSync(distDir).find(f => f.endsWith(".js"));
154
+ const flatJs = path.join(distDir, `index.${platform}.js`);
155
+ if (fs.existsSync(flatJs))
156
+ return flatJs;
157
+ const flatHbc = path.join(distDir, `index.${platform}.hbc`);
158
+ if (fs.existsSync(flatHbc))
159
+ return flatHbc;
160
+ // Any .js or .hbc at root
161
+ const rootJs = fs.readdirSync(distDir).find(f => f.endsWith(".js") || f.endsWith(".hbc"));
121
162
  if (rootJs)
122
163
  return path.join(distDir, rootJs);
123
164
  return null;
@@ -134,14 +175,33 @@ program
134
175
  program
135
176
  .command("init")
136
177
  .description("Generate an ECDSA P-256 key pair and write edge-ota.config.json")
137
- .option("-s, --server <url>", "EdgeOTA server URL", "http://localhost:3000")
178
+ .option("-s, --server <url>", "EdgeOTA server URL")
138
179
  .action(async (options) => {
180
+ let serverUrl = options.server;
181
+ if (!serverUrl) {
182
+ console.log("ℹ️ Server URL not provided via -s/--server.");
183
+ const answer = await askQuestion("Enter your EdgeOTA server URL [http://localhost:3000]: ");
184
+ if (!answer) {
185
+ serverUrl = "http://localhost:3000";
186
+ }
187
+ else if (answer.toLowerCase().includes("host")) {
188
+ console.log("\nℹ️ Please set the server URL to your hosted EdgeOTA instance (e.g. https://ota.renbostudios.com)");
189
+ const hostedAnswer = await askQuestion("Hosted server URL: ");
190
+ serverUrl = hostedAnswer.trim() || "http://localhost:3000";
191
+ }
192
+ else {
193
+ serverUrl = answer;
194
+ }
195
+ }
196
+ // Normalise URL (remove trailing slashes)
197
+ serverUrl = serverUrl.replace(/\/$/, "");
139
198
  console.log("⚙️ Generating ECDSA P-256 key pair...");
140
199
  const keys = await generateECDSAKeyPair();
141
200
  const config = {
142
- serverUrl: options.server,
201
+ serverUrl: serverUrl,
143
202
  publicKey: keys.publicKey
144
203
  };
204
+ const cwd = process.cwd();
145
205
  fs.writeFileSync("edge-ota.config.json", JSON.stringify(config, null, 2));
146
206
  fs.writeFileSync(".edge-ota.private.key", keys.privateKey, { mode: 0o600 });
147
207
  // Add the private key to .gitignore if it isn't already
@@ -157,15 +217,20 @@ program
157
217
  else {
158
218
  fs.writeFileSync(gitignorePath, `${gitignoreEntry}\n`);
159
219
  }
220
+ // Auto-update app.json updates.url
221
+ updateAppJson(cwd, serverUrl);
160
222
  console.log("\n✅ EdgeOTA initialised.");
161
223
  console.log(" Config : edge-ota.config.json");
162
224
  console.log(" Key : .edge-ota.private.key (keep this secret!)");
163
225
  console.log("\n📋 Public key to paste into your dashboard → Settings → Infrastructure:\n");
164
226
  console.log(keys.publicKey);
165
- console.log("\n💡 Add this to your app.json:\n" +
227
+ console.log("\n💡 Your app.json updates configuration has been configured to:\n" +
166
228
  ' "updates": {\n' +
167
- ' "url": "' + options.server + '/api/updates"\n' +
168
- " }");
229
+ ' "url": "' + serverUrl + '/api/updates"\n' +
230
+ " }\n" +
231
+ "\n✨ Powered by EdgeOTA — A product of Renbo Studios" +
232
+ "\n🚀 Need scalable app development or custom Cloudflare setup?" +
233
+ "\n👉 Partner with us at https://renbostudios.com to launch your next project!");
169
234
  });
170
235
  // ──────────────────────────────────────────────────────
171
236
  // edge-ota push
@@ -272,7 +337,9 @@ program
272
337
  }
273
338
  }
274
339
  console.log("\n🎉 Done. Your app will receive this update on the next OTA sync.\n" +
275
- " Powered by EdgeOTA — https://github.com/edge-ota");
340
+ "\n✨ Powered by EdgeOTA — A product of Renbo Studios" +
341
+ "\n🚀 Need scalable app development or custom Cloudflare setup?" +
342
+ "\n👉 Partner with us at https://renbostudios.com to launch your next project!");
276
343
  });
277
344
  // ──────────────────────────────────────────────────────
278
345
  // edge-ota status
@@ -311,6 +378,8 @@ program
311
378
  "By": r.publisher
312
379
  }));
313
380
  console.table(rows);
381
+ console.log("\n✨ Powered by EdgeOTA — A product of Renbo Studios" +
382
+ "\n👉 Need scalable app development or cloud setup? Visit us at https://renbostudios.com");
314
383
  });
315
384
  // ──────────────────────────────────────────────────────
316
385
  // edge-ota keygen
@@ -324,5 +393,7 @@ program
324
393
  console.log(keys.privateKey);
325
394
  console.log("\n--- PUBLIC KEY (paste into dashboard) ---");
326
395
  console.log(keys.publicKey);
396
+ console.log("\n✨ Powered by EdgeOTA — A product of Renbo Studios" +
397
+ "\n👉 Need scalable app development or cloud setup? Visit us at https://renbostudios.com");
327
398
  });
328
399
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@renbostudios/edge-ota",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
+ "preferGlobal": true,
4
5
  "description": "Zero-SDK, self-hostable OTA update CLI for Expo — push signed JS bundles to any EdgeOTA server",
5
6
  "license": "MIT",
6
7
  "author": "EdgeOTA Contributors",