@rubytech/create-maxy 1.0.784 → 1.0.786

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/dist/index.js CHANGED
@@ -1358,6 +1358,19 @@ function buildPlatform() {
1358
1358
  // server/package.json but NOT shipped as pre-built node_modules — npm pack silently
1359
1359
  // strips files from nested node_modules (e.g. rxjs/package.json), breaking require().
1360
1360
  // Install fresh on device to guarantee a complete dependency tree.
1361
+ //
1362
+ // On upgrade, wipe `node_modules` first so npm extracts a clean tree. Without
1363
+ // this, an interrupted previous install (network blip, operator cancellation,
1364
+ // power loss) can leave nested package.json files half-truncated — the most
1365
+ // common manifestation is `Error: Invalid package config .../rxjs/package.json`
1366
+ // at server startup, which loops the brand service indefinitely. The wipe
1367
+ // adds ~30 s to upgrades but eliminates a class of unrecoverable customer
1368
+ // states; reliability wins over speed for a one-shot install path.
1369
+ const serverNodeModules = join(INSTALL_DIR, "server", "node_modules");
1370
+ if (existsSync(serverNodeModules)) {
1371
+ console.log(" Wiping previous server/node_modules for a clean reinstall...");
1372
+ rmSync(serverNodeModules, { recursive: true, force: true });
1373
+ }
1361
1374
  console.log(` Installing server dependencies (${join(INSTALL_DIR, "server")})...`);
1362
1375
  shellRetry("npm", ["install", "--omit=dev", ...NPM_NET_FLAGS], { cwd: join(INSTALL_DIR, "server") }, 3, 15);
1363
1376
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.784",
3
+ "version": "1.0.786",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -292,12 +292,10 @@ function portalHTML() {
292
292
  <div class="success-icon">&#10003;</div>
293
293
  <h1>Connected!</h1>
294
294
  <p class="hint">${escapedBrandName} is now online. Redirecting in <span id="redirect-countdown">15</span>s…</p>
295
- <div class="address-box">
296
- <a id="device-link" href="#"></a>
297
- </div>
298
- <p class="hint" id="manual-hint" style="display:none">If your phone doesn't redirect automatically, open this in your normal browser:</p>
299
- <div class="address-box" id="manual-box" style="display:none">
300
- <span id="device-link-text" style="user-select:all"></span>
295
+ <a id="open-btn" href="#" class="btn" style="text-decoration:none;display:flex;align-items:center;justify-content:center">Open ${escapedBrandName} →</a>
296
+ <p class="hint" style="margin-top:16px">Or copy this URL into your browser:</p>
297
+ <div class="address-box" style="user-select:all" id="manual-box">
298
+ <span id="device-link-text"></span>
301
299
  </div>
302
300
  <p class="hint">This access point will close shortly.<br>Your phone will reconnect to your WiFi automatically.</p>
303
301
  </div>
@@ -421,21 +419,11 @@ function portalHTML() {
421
419
  var ipAddr = data.ip ? "http://" + data.ip + devicePort : null;
422
420
  // Auto-navigate target: prefer the IP because mDNS .local resolution
423
421
  // is flaky on Android (notably Brave) and the redirect dead-ends.
424
- // The displayed link uses the same URL so what the user sees is what
425
- // they get if they tap.
426
422
  var redirectAddr = ipAddr || hostnameAddr;
427
- var link = document.getElementById("device-link");
428
- link.href = redirectAddr;
429
- link.textContent = redirectAddr;
430
- // Always surface a manual fallback line — captive webviews on some
431
- // phone OSes silently swallow JS navigation, and the user needs to
432
- // be able to copy-paste the URL into a real browser.
423
+ var openBtn = document.getElementById("open-btn");
424
+ if (openBtn) openBtn.href = redirectAddr;
433
425
  var manualText = document.getElementById("device-link-text");
434
- if (manualText) {
435
- manualText.textContent = redirectAddr;
436
- document.getElementById("manual-hint").style.display = "block";
437
- document.getElementById("manual-box").style.display = "block";
438
- }
426
+ if (manualText) manualText.textContent = redirectAddr;
439
427
  showScreen("success-screen");
440
428
  // 15s countdown: the Pi keeps the AP up for ~10s after writing the
441
429
  // success result so this poll can land, then tears it down; the
@@ -40,15 +40,21 @@ PLATFORM_ROOT="${MAXY_PLATFORM_ROOT:-$(dirname "$SCRIPT_DIR")}"
40
40
  BRAND_JSON="${PLATFORM_ROOT}/config/brand.json"
41
41
  CONFIG_DIR=".maxy"
42
42
  PRODUCT_NAME="Maxy"
43
- HOSTNAME_NAME="maxy"
44
43
  if [ -f "$BRAND_JSON" ] && command -v jq >/dev/null 2>&1; then
45
44
  _dir=$(jq -r '.configDir // empty' "$BRAND_JSON" 2>/dev/null) || true
46
45
  [ -n "$_dir" ] && CONFIG_DIR="$_dir"
47
46
  _name=$(jq -r '.productName // empty' "$BRAND_JSON" 2>/dev/null) || true
48
47
  [ -n "$_name" ] && PRODUCT_NAME="$_name"
49
- _host=$(jq -r '.hostname // empty' "$BRAND_JSON" 2>/dev/null) || true
50
- [ -n "$_host" ] && HOSTNAME_NAME="$_host"
51
48
  fi
49
+ # HOSTNAME_NAME must be the **actual system hostname** (the name Avahi
50
+ # advertises and the post-connect URL must use), not the brand default
51
+ # from brand.json. The operator's --hostname flag rewrites the system
52
+ # hostname but never touches brand.json's hostname field, so reading
53
+ # from the brand mis-routed the captive portal's success URL to a name
54
+ # that doesn't resolve. `hostname -s` is the canonical answer Avahi
55
+ # itself follows when avahi-daemon.conf has `host-name` left empty,
56
+ # so this is the same source of truth.
57
+ HOSTNAME_NAME="$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo maxy)"
52
58
 
53
59
  # Determine the home directory of the installing user. The service runs as
54
60
  # root, but logs and config belong to the user who installed the platform.