@rubytech/create-maxy 1.0.782 → 1.0.784

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.782",
3
+ "version": "1.0.784",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -39,6 +39,10 @@ const RESULT_FILE = args["result-file"] || "/tmp/wifi-provision-result";
39
39
  const BRAND_JSON = args["brand-json"] || "";
40
40
  const HOSTNAME = args.hostname || "maxy";
41
41
  const PORTAL_HOST = args["portal-host"] || "";
42
+ // Brand service port (e.g. 19200). Distinct from PORT, which is the
43
+ // captive portal's own listen port (always 80). Used only to render the
44
+ // post-connect URL.
45
+ const DEVICE_PORT = parseInt(args["device-port"] || "19200", 10);
42
46
 
43
47
  // ── Load brand config ─────────────────────────────────────────────────
44
48
  // All visual surfaces (colour, font, logo) come from brand.json so the
@@ -243,9 +247,11 @@ function portalHTML() {
243
247
  }
244
248
  .address-box a {
245
249
  color: ${colors.primary};
246
- text-decoration: none;
250
+ text-decoration: underline;
247
251
  font-size: 16px;
248
252
  font-weight: 600;
253
+ display: block;
254
+ -webkit-tap-highlight-color: ${colors.primary};
249
255
  }
250
256
  .hint { color: #888; font-size: 13px; margin-top: 12px; line-height: 1.5; }
251
257
  </style>
@@ -285,13 +291,13 @@ function portalHTML() {
285
291
  <div class="card" style="padding:32px 16px">
286
292
  <div class="success-icon">&#10003;</div>
287
293
  <h1>Connected!</h1>
288
- <p class="hint">${escapedBrandName} is now online. Redirecting in <span id="redirect-countdown">20</span>s…</p>
294
+ <p class="hint">${escapedBrandName} is now online. Redirecting in <span id="redirect-countdown">15</span>s…</p>
289
295
  <div class="address-box">
290
- <a id="device-link" href="#" target="_blank"></a>
296
+ <a id="device-link" href="#"></a>
291
297
  </div>
292
- <p class="hint" id="ip-fallback" style="display:none">If the page above doesn't load (some Android browsers don't resolve <code>.local</code>), use this direct IP:</p>
293
- <div class="address-box" id="ip-fallback-box" style="display:none">
294
- <a id="device-link-ip" href="#" target="_blank"></a>
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
301
  </div>
296
302
  <p class="hint">This access point will close shortly.<br>Your phone will reconnect to your WiFi automatically.</p>
297
303
  </div>
@@ -301,7 +307,10 @@ function portalHTML() {
301
307
  (function() {
302
308
  "use strict";
303
309
  var selectedSSID = "";
304
- var devicePort = ${PORT === 80 ? '""' : JSON.stringify(":" + PORT)};
310
+ // The brand service port (19200, configurable via --port), NOT the
311
+ // captive portal port (always 80). devicePort is appended to the
312
+ // post-connect URL so the link points at the admin server.
313
+ var devicePort = ${DEVICE_PORT === 80 ? '""' : JSON.stringify(":" + DEVICE_PORT)};
305
314
  var deviceHostname = ${JSON.stringify(HOSTNAME)};
306
315
 
307
316
  // Safe text insertion — no innerHTML for user-controlled content
@@ -410,37 +419,44 @@ function portalHTML() {
410
419
  ? "http://" + data.hostname + ".local" + devicePort
411
420
  : null;
412
421
  var ipAddr = data.ip ? "http://" + data.ip + devicePort : null;
413
- // Display: prefer the friendly .local URL.
414
- // Auto-redirect target: the raw IP. On Android (notably Brave) the
415
- // mDNS .local lookup fails and the auto-navigate would dead-end;
416
- // the IP works on every platform.
417
- var displayAddr = hostnameAddr || ipAddr;
422
+ // Auto-navigate target: prefer the IP because mDNS .local resolution
423
+ // 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.
418
426
  var redirectAddr = ipAddr || hostnameAddr;
419
427
  var link = document.getElementById("device-link");
420
- link.href = displayAddr;
421
- link.textContent = displayAddr;
422
- if (hostnameAddr && ipAddr && hostnameAddr !== ipAddr) {
423
- var ipLink = document.getElementById("device-link-ip");
424
- ipLink.href = ipAddr;
425
- ipLink.textContent = ipAddr;
426
- document.getElementById("ip-fallback").style.display = "block";
427
- document.getElementById("ip-fallback-box").style.display = "block";
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.
433
+ 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";
428
438
  }
429
439
  showScreen("success-screen");
430
- // Countdown timing: the Pi keeps the AP up for ~10s after writing the
431
- // success result (so this poll can land), then tears it down. The
432
- // phone then needs ~5s to drop the AP SSID and auto-rejoin its home
433
- // WiFi. A 20s countdown crosses both gates so the redirect lands
434
- // when the phone is back on its home network and the device is
435
- // routable on its new IP.
436
- var remaining = 20;
440
+ // 15s countdown: the Pi keeps the AP up for ~10s after writing the
441
+ // success result so this poll can land, then tears it down; the
442
+ // phone then needs ~5s to drop the AP SSID and rejoin home WiFi.
443
+ var remaining = 15;
437
444
  var countdownEl = document.getElementById("redirect-countdown");
438
445
  var ticker = setInterval(function() {
439
446
  remaining -= 1;
440
447
  if (countdownEl) countdownEl.textContent = String(remaining);
441
448
  if (remaining <= 0) {
442
449
  clearInterval(ticker);
443
- window.location.href = redirectAddr;
450
+ // Try multiple navigation primitives — captive webviews on some
451
+ // platforms ignore one but accept another.
452
+ try { window.location.replace(redirectAddr); } catch(e) {}
453
+ try { window.location.href = redirectAddr; } catch(e) {}
454
+ // Last-resort: synthesise a click on a freshly-built anchor.
455
+ try {
456
+ var a = document.createElement("a");
457
+ a.href = redirectAddr; a.rel = "noopener";
458
+ document.body.appendChild(a); a.click();
459
+ } catch(e) {}
444
460
  }
445
461
  }, 1000);
446
462
  }
@@ -60,6 +60,24 @@ LOG_FILE="${LOG_DIR}/wifi-provision.log"
60
60
 
61
61
  mkdir -p "$LOG_DIR"
62
62
 
63
+ # Resolve the brand service's public port so the captive portal can render
64
+ # the post-connect URL with the correct port (the captive portal itself
65
+ # runs on :80, but the device's admin URL is on the brand port). Mirrors
66
+ # the same precedence as the installer: ~/{configDir}/.env override, then
67
+ # the systemd unit's Environment=PORT, then the documented default.
68
+ BRAND_PORT="19200"
69
+ if [ -f "${MAXY_DIR}/.env" ]; then
70
+ _p=$(grep -E '^PORT=' "${MAXY_DIR}/.env" 2>/dev/null | tail -1 | cut -d= -f2 | tr -d '"' | tr -d "'")
71
+ [ -n "$_p" ] && BRAND_PORT="$_p"
72
+ fi
73
+ if [ "$BRAND_PORT" = "19200" ]; then
74
+ _svc="${INSTALL_HOME}/.config/systemd/user/${HOSTNAME_NAME}.service"
75
+ if [ -f "$_svc" ]; then
76
+ _p=$(grep -E '^Environment=PORT=' "$_svc" 2>/dev/null | tail -1 | cut -d= -f3)
77
+ [ -n "$_p" ] && BRAND_PORT="$_p"
78
+ fi
79
+ fi
80
+
63
81
  # ── Derived constants ────────────────────────────────────────────────
64
82
  # SSID: lowercase the product name and replace spaces with hyphens — gives
65
83
  # `maxy`, `real-agent`, etc. (kebab-case, no "-Setup" suffix). The user's
@@ -290,6 +308,7 @@ DNSMASQ_EOF
290
308
  --brand-json "$BRAND_JSON" \
291
309
  --hostname "$HOSTNAME_NAME" \
292
310
  --portal-host "$PORTAL_HOST" \
311
+ --device-port "$BRAND_PORT" \
293
312
  >> "$LOG_FILE" 2>&1 &
294
313
  HTTP_SERVER_PID=$!
295
314
  sleep 1