@tomorrowos/sdk 0.9.13 → 0.9.16

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.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Best-effort primary non-loopback IPv4 for local development.
3
+ * Returns null when no suitable interface is found.
4
+ */
5
+ export declare function detectPrimaryLanIpv4(): string | null;
6
+ //# sourceMappingURL=lan-address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lan-address.d.ts","sourceRoot":"","sources":["../src/lan-address.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAcpD"}
@@ -0,0 +1,32 @@
1
+ import os from "os";
2
+ /** Prefer common private LAN ranges for local CMS / player media base URLs. */
3
+ function scoreLanIpv4(ip) {
4
+ if (ip.startsWith("192.168."))
5
+ return 3;
6
+ if (ip.startsWith("10."))
7
+ return 2;
8
+ if (/^172\.(1[6-9]|2\d|3[0-1])\./.test(ip))
9
+ return 2;
10
+ return 1;
11
+ }
12
+ /**
13
+ * Best-effort primary non-loopback IPv4 for local development.
14
+ * Returns null when no suitable interface is found.
15
+ */
16
+ export function detectPrimaryLanIpv4() {
17
+ const nets = os.networkInterfaces();
18
+ const addresses = [];
19
+ for (const entries of Object.values(nets)) {
20
+ if (!entries)
21
+ continue;
22
+ for (const ent of entries) {
23
+ const family = ent.family;
24
+ const isV4 = family === "IPv4" || family === 4;
25
+ if (!isV4 || ent.internal)
26
+ continue;
27
+ addresses.push(ent.address);
28
+ }
29
+ }
30
+ addresses.sort((a, b) => scoreLanIpv4(b) - scoreLanIpv4(a));
31
+ return addresses[0] ?? null;
32
+ }
@@ -36,14 +36,14 @@ function resolveDatabaseProvider(env) {
36
36
  };
37
37
  }
38
38
  if (driver === "memory") {
39
- return { provider: "memory", label: "Memory Store", configured: true };
39
+ return { provider: "memory", label: "Database", configured: true };
40
40
  }
41
41
  if (driver === "sqlite" || !driver) {
42
- return { provider: "sqlite", label: "SQLite", configured: true };
42
+ return { provider: "sqlite", label: "Database", configured: true };
43
43
  }
44
44
  return {
45
45
  provider: driver || "unknown",
46
- label: driver ? `Store (${driver})` : "Database",
46
+ label: "Database",
47
47
  configured: hasUrl || driver === "sqlite" || driver === "memory"
48
48
  };
49
49
  }
@@ -98,6 +98,8 @@ export declare class TomorrowOS extends EventEmitter {
98
98
  private staticIndexFile;
99
99
  /** Set when listen() starts — exposed to CMS panel for post-restart reconnect grace. */
100
100
  private serverStartedAt;
101
+ /** Port passed to listen(); used for suggested LAN CMS URL. */
102
+ private listenPort;
101
103
  constructor(options: TomorrowOSOptions);
102
104
  /** Push all device assignments using latest playlist definitions (e.g. after reboot). */
103
105
  pushLatestPolicyToDevice(deviceId: string): Promise<{
@@ -165,6 +167,8 @@ export declare class TomorrowOS extends EventEmitter {
165
167
  }>;
166
168
  };
167
169
  private sendCommandToSocket;
170
+ /** Best-effort LAN URL for local screens (e.g. http://192.168.1.10:3000). */
171
+ private getSuggestedCmsUrl;
168
172
  listen(options: ListenOptions): http.Server;
169
173
  private handleMediaUpload;
170
174
  private storeUploadedMediaAsset;
@@ -1 +1 @@
1
- {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ import { resolveBrandLogoPath, syncProjectAssetsToStaticRoot } from "./brand-ass
11
11
  import { deleteCloudinaryAsset, resolveCloudinaryConfig, uploadBufferToCloudinary } from "./cloudinary-storage.js";
12
12
  import { probeVideoDurationMs } from "./media-probe.js";
13
13
  import { contentHashHex, storeUploadIfNeeded } from "./upload-storage.js";
14
+ import { detectPrimaryLanIpv4 } from "./lan-address.js";
14
15
  import { buildServerStatus } from "./server-status.js";
15
16
  function normalizeDevicePlatform(platform, system) {
16
17
  const explicit = typeof platform === "string" ? platform.trim().toLowerCase() : "";
@@ -213,6 +214,8 @@ export class TomorrowOS extends EventEmitter {
213
214
  staticIndexFile = "index.html";
214
215
  /** Set when listen() starts — exposed to CMS panel for post-restart reconnect grace. */
215
216
  serverStartedAt = null;
217
+ /** Port passed to listen(); used for suggested LAN CMS URL. */
218
+ listenPort = null;
216
219
  constructor(options) {
217
220
  super();
218
221
  this.brand = options.brand;
@@ -716,8 +719,17 @@ export class TomorrowOS extends EventEmitter {
716
719
  }));
717
720
  });
718
721
  }
722
+ /** Best-effort LAN URL for local screens (e.g. http://192.168.1.10:3000). */
723
+ getSuggestedCmsUrl() {
724
+ const ip = detectPrimaryLanIpv4();
725
+ const port = this.listenPort;
726
+ if (!ip || port == null || !Number.isFinite(port))
727
+ return null;
728
+ return `http://${ip}:${port}`;
729
+ }
719
730
  listen(options) {
720
731
  const { port, host = "0.0.0.0", staticRoot, staticIndex } = options;
732
+ this.listenPort = port;
721
733
  this.staticRoot = staticRoot ?? null;
722
734
  if (this.staticRoot) {
723
735
  const idx = (staticIndex ?? "index.html").trim().replace(/^[\\/]+/, "") || "index.html";
@@ -1088,7 +1100,11 @@ export class TomorrowOS extends EventEmitter {
1088
1100
  store: this.store,
1089
1101
  staticRoot: this.staticRoot
1090
1102
  });
1091
- sendJson(res, 200, report);
1103
+ const suggestedCmsUrl = this.getSuggestedCmsUrl();
1104
+ sendJson(res, 200, {
1105
+ ...report,
1106
+ ...(suggestedCmsUrl ? { suggestedCmsUrl } : {})
1107
+ });
1092
1108
  return;
1093
1109
  }
1094
1110
  if (req.method === "POST" && pathname === "/media/upload") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.9.13",
3
+ "version": "0.9.16",
4
4
  "description": "TomorrowOS CMS server SDK - WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "0.9.13",
3
+ "version": "0.9.16",
4
4
  "description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
5
5
  "private": true,
6
6
  "type": "module",
@@ -13,7 +13,7 @@
13
13
  "build-player": "tomorrowos build --platform tizen"
14
14
  },
15
15
  "dependencies": {
16
- "@tomorrowos/sdk": "^0.9.13",
16
+ "@tomorrowos/sdk": "^0.9.16",
17
17
  "dotenv": "^17.2.3",
18
18
  "tsx": "^4.19.0"
19
19
  },
@@ -66,15 +66,16 @@
66
66
  <section class="card" id="cmsUrlSection">
67
67
  <h2>CMS URL for screens</h2>
68
68
  <p class="hint">
69
- <strong>Local development only.</strong> Optional LAN override so TVs reach your PC (not localhost).
70
- Hosted CMS (e.g. Replit) uses its public URL automatically leave this empty there.
69
+ <strong>Local development only.</strong> Defaults to this PC’s LAN IP and listen port so TVs
70
+ can reach media (not localhost). Change and Save if the auto-detected address is wrong.
71
+ Hosted CMS (e.g. Replit) uses its public URL automatically — this section is hidden there.
71
72
  </p>
72
73
  <div class="row">
73
74
  <input
74
75
  id="cmsDeviceBaseUrl"
75
76
  type="text"
76
77
  style="flex: 1; min-width: 16rem"
77
- placeholder="http://192.168.1.105:3000"
78
+ placeholder="Detecting LAN address…"
78
79
  />
79
80
  <button type="button" onclick="saveCmsDeviceBaseUrl()">Save</button>
80
81
  </div>
@@ -185,12 +186,8 @@
185
186
  <div class="modal-card">
186
187
  <h2>On / off timer</h2>
187
188
  <p class="hint" id="onOffTimerModalHint">
188
- Daily schedule on the device clock. Off uses Samsung panel mute (screen black); the player stays connected to the CMS.
189
+ Daily on/off times follow the device clock. Off blacks the screen; the player stays powered and connected to the CMS.
189
190
  </p>
190
- <label class="field-label on-off-timer-enable">
191
- <input type="checkbox" id="onOffTimerEnabled" />
192
- Enable daily timer
193
- </label>
194
191
  <div class="on-off-timer-grid">
195
192
  <label class="field-label">
196
193
  Turn on (unmute)
@@ -201,7 +198,6 @@
201
198
  <input type="time" id="onOffTimerTurnOffAt" value="18:00" />
202
199
  </label>
203
200
  </div>
204
- <p class="hint" id="onOffTimerModalStatus"></p>
205
201
  <div class="modal-actions">
206
202
  <button type="button" id="onOffTimerSaveBtn" class="primary">Save</button>
207
203
  <button type="button" data-close-on-off-timer-modal="1">Cancel</button>
@@ -233,7 +229,7 @@
233
229
  <p class="hint">Current installed SDK version:</p>
234
230
  <p class="update-sdk-version" id="updateSdkVersionLabel">Loading…</p>
235
231
  <p class="hint">
236
- To upgrade on Replit (or another Agent platform), paste this prompt into the Agent chat:
232
+ To upgrade on AI agent platforms (Replit, Vercel, etc.), paste this prompt into the Agent chat:
237
233
  </p>
238
234
  <pre class="update-sdk-prompt" id="updateSdkPromptText">Follow @tomorrowos/sdk REPLIT_UPGRADE.md to upgrade my CMS with the latest SDK.</pre>
239
235
  <div class="modal-actions update-sdk-modal-actions">
@@ -190,6 +190,28 @@ function getExplicitLanMediaBase() {
190
190
  return normalizeMediaBaseUrl(localStorage.getItem(PANEL_MEDIA_BASE_KEY) || "");
191
191
  }
192
192
 
193
+ /**
194
+ * Prefill CMS URL for screens from server-detected LAN IP:port when running on localhost
195
+ * and the operator has not saved a value yet. Auto-persists so thumbs/publish work without Save.
196
+ */
197
+ function applyDefaultCmsUrlForScreens(suggested) {
198
+ if (!isLocalPanelHost(window.location.hostname)) return;
199
+ const normalized = normalizeMediaBaseUrl(suggested);
200
+ if (!normalized) return;
201
+
202
+ const cmsBaseInput = document.getElementById("cmsDeviceBaseUrl");
203
+ const saved = getExplicitLanMediaBase();
204
+ if (saved) {
205
+ if (cmsBaseInput && !String(cmsBaseInput.value || "").trim()) {
206
+ cmsBaseInput.value = saved;
207
+ }
208
+ return;
209
+ }
210
+
211
+ if (cmsBaseInput) cmsBaseInput.value = normalized;
212
+ localStorage.setItem(PANEL_MEDIA_BASE_KEY, normalized);
213
+ }
214
+
193
215
  function playlistHasRelativeMediaUrls(playlist) {
194
216
  return (playlist?.items || []).some((item) => {
195
217
  const url = String(item?.url || "").trim();
@@ -1280,8 +1302,8 @@ function renderDeviceCards() {
1280
1302
 
1281
1303
  const timerBtn = document.createElement("button");
1282
1304
  timerBtn.type = "button";
1283
- timerBtn.textContent = device.onOffTimer?.enabled ? "Timer · On" : "Timer";
1284
- timerBtn.title = "Daily screen on/off (Tizen panel mute)";
1305
+ timerBtn.textContent = "Timer";
1306
+ timerBtn.title = "Daily screen on/off timer";
1285
1307
  timerBtn.addEventListener("click", () => openOnOffTimerModal(device.deviceId));
1286
1308
 
1287
1309
  const unpairBtn = document.createElement("button");
@@ -1815,32 +1837,19 @@ function openOnOffTimerModal(deviceId) {
1815
1837
  const device = devicesCache.find((d) => d.deviceId === deviceId);
1816
1838
  if (!device) return;
1817
1839
  if (!isTizenDevice(device)) {
1818
- alert("On/off timer is available for Samsung Tizen players only.");
1840
+ alert("On/off timer is available for Tizen players only.");
1819
1841
  return;
1820
1842
  }
1821
1843
 
1822
1844
  onOffTimerModalDeviceId = deviceId;
1823
1845
  const modal = document.getElementById("onOffTimerModal");
1824
- const hint = document.getElementById("onOffTimerModalHint");
1825
- const status = document.getElementById("onOffTimerModalStatus");
1826
- const enabledEl = document.getElementById("onOffTimerEnabled");
1827
1846
  const onEl = document.getElementById("onOffTimerTurnOnAt");
1828
1847
  const offEl = document.getElementById("onOffTimerTurnOffAt");
1829
- if (!modal || !enabledEl || !onEl || !offEl) return;
1848
+ if (!modal || !onEl || !offEl) return;
1830
1849
 
1831
1850
  const timer = device.onOffTimer || {};
1832
- enabledEl.checked = Boolean(timer.enabled);
1833
1851
  onEl.value = normalizeTimeInputValue(timer.turnOnAt, "06:00");
1834
1852
  offEl.value = normalizeTimeInputValue(timer.turnOffAt, "18:00");
1835
- if (hint) {
1836
- hint.textContent =
1837
- "Daily schedule on the device clock. Off uses Samsung panel mute (screen black); the player stays connected to the CMS.";
1838
- }
1839
- if (status) {
1840
- status.textContent = device.connected
1841
- ? "Device is online — save will apply immediately."
1842
- : "Device is offline — schedule will save and apply when it reconnects.";
1843
- }
1844
1853
  modal.classList.remove("hidden");
1845
1854
  }
1846
1855
 
@@ -1854,14 +1863,13 @@ async function saveOnOffTimerModal() {
1854
1863
  const deviceId = onOffTimerModalDeviceId;
1855
1864
  if (!deviceId) return;
1856
1865
 
1857
- const enabledEl = document.getElementById("onOffTimerEnabled");
1858
1866
  const onEl = document.getElementById("onOffTimerTurnOnAt");
1859
1867
  const offEl = document.getElementById("onOffTimerTurnOffAt");
1860
1868
  const saveBtn = document.getElementById("onOffTimerSaveBtn");
1861
- if (!enabledEl || !onEl || !offEl) return;
1869
+ if (!onEl || !offEl) return;
1862
1870
 
1863
1871
  const onOffTimer = {
1864
- enabled: Boolean(enabledEl.checked),
1872
+ enabled: true,
1865
1873
  turnOnAt: normalizeTimeInputValue(onEl.value, "06:00"),
1866
1874
  turnOffAt: normalizeTimeInputValue(offEl.value, "18:00")
1867
1875
  };
@@ -2143,6 +2151,9 @@ async function fetchServerStatus() {
2143
2151
  if (typeof data.sdkVersion === "string" && data.sdkVersion.trim()) {
2144
2152
  cachedSdkVersion = data.sdkVersion.trim();
2145
2153
  }
2154
+ if (typeof data.suggestedCmsUrl === "string") {
2155
+ applyDefaultCmsUrlForScreens(data.suggestedCmsUrl);
2156
+ }
2146
2157
  renderServerStatus(data);
2147
2158
  } catch (err) {
2148
2159
  renderServerStatus({
@@ -829,22 +829,11 @@ button.danger {
829
829
  width: min(94vw, 860px);
830
830
  }
831
831
 
832
- .on-off-timer-enable {
833
- display: flex;
834
- align-items: center;
835
- gap: 0.5rem;
836
- margin: 0.85rem 0 0.65rem;
837
- }
838
-
839
- .on-off-timer-enable input {
840
- width: auto;
841
- margin: 0;
842
- }
843
-
844
832
  .on-off-timer-grid {
845
833
  display: grid;
846
834
  grid-template-columns: 1fr 1fr;
847
835
  gap: 0.75rem;
836
+ margin-top: 0.85rem;
848
837
  }
849
838
 
850
839
  @media (max-width: 520px) {