@tomorrowos/sdk 0.4.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.
@@ -52,6 +52,15 @@ export interface DeviceListItem {
52
52
  version: number;
53
53
  publishedAt: string;
54
54
  }>;
55
+ latestErrorAt: string | null;
56
+ latestErrorMessage: string | null;
57
+ }
58
+ export interface DeviceLogEntry {
59
+ timestamp: string;
60
+ level: "info" | "warn" | "error";
61
+ message: string;
62
+ source?: string;
63
+ details?: unknown;
55
64
  }
56
65
  export declare class TomorrowOS extends EventEmitter {
57
66
  readonly brand: TomorrowOSBrand;
@@ -59,6 +68,7 @@ export declare class TomorrowOS extends EventEmitter {
59
68
  readonly playlists: PlaylistCatalog;
60
69
  private readonly devices;
61
70
  private readonly pendingDeviceMeta;
71
+ private readonly deviceLogs;
62
72
  private httpServer;
63
73
  private wss;
64
74
  private staticRoot;
@@ -90,6 +100,8 @@ export declare class TomorrowOS extends EventEmitter {
90
100
  };
91
101
  /** List paired devices with live connection + timing fields for the CMS panel. */
92
102
  listDevices(): Promise<DeviceListItem[]>;
103
+ private pushDeviceLog;
104
+ private getDeviceLogs;
93
105
  private isDeviceConnected;
94
106
  private captureHelloMeta;
95
107
  private getOrCreatePermanentPairingCode;
@@ -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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"}
@@ -132,6 +132,7 @@ export class TomorrowOS extends EventEmitter {
132
132
  playlists;
133
133
  devices = new Map();
134
134
  pendingDeviceMeta = new Map();
135
+ deviceLogs = new Map();
135
136
  httpServer = null;
136
137
  wss = null;
137
138
  staticRoot = null;
@@ -267,6 +268,8 @@ export class TomorrowOS extends EventEmitter {
267
268
  }
268
269
  }
269
270
  const screenOnlineSince = connected && record.lastBootAt ? record.lastBootAt : null;
271
+ const logs = this.deviceLogs.get(deviceId) ?? [];
272
+ const latestError = [...logs].reverse().find((entry) => entry.level === "error");
270
273
  return {
271
274
  deviceId,
272
275
  pairingCode: reg?.permanentPairingCode ?? null,
@@ -288,10 +291,28 @@ export class TomorrowOS extends EventEmitter {
288
291
  lastPolicyPushAt: record.lastPolicyPushAt ?? null,
289
292
  screenOnlineActive,
290
293
  screenOnlineLabel,
291
- screenOnlineSince
294
+ screenOnlineSince,
295
+ latestErrorAt: latestError?.timestamp ?? null,
296
+ latestErrorMessage: latestError?.message ?? null
292
297
  };
293
298
  }));
294
299
  }
300
+ pushDeviceLog(deviceId, entry) {
301
+ const key = String(deviceId || "").trim();
302
+ if (!key)
303
+ return;
304
+ const existing = this.deviceLogs.get(key) ?? [];
305
+ existing.push(entry);
306
+ if (existing.length > 80)
307
+ existing.splice(0, existing.length - 80);
308
+ this.deviceLogs.set(key, existing);
309
+ }
310
+ getDeviceLogs(deviceId) {
311
+ const key = String(deviceId || "").trim();
312
+ if (!key)
313
+ return [];
314
+ return [...(this.deviceLogs.get(key) ?? [])].reverse();
315
+ }
295
316
  isDeviceConnected(deviceId) {
296
317
  const ws = this.devices.get(deviceId);
297
318
  return !!ws && ws.readyState === WebSocket.OPEN;
@@ -710,6 +731,12 @@ export class TomorrowOS extends EventEmitter {
710
731
  sendJson(res, 200, { status: "success", assignments });
711
732
  return;
712
733
  }
734
+ const deviceLogsGet = /^\/device\/([^/]+)\/logs$/.exec(pathname);
735
+ if (req.method === "GET" && deviceLogsGet) {
736
+ const deviceId = decodeURIComponent(deviceLogsGet[1]);
737
+ sendJson(res, 200, { status: "success", logs: this.getDeviceLogs(deviceId) });
738
+ return;
739
+ }
713
740
  if (req.method === "DELETE" && deviceAssignmentsGet) {
714
741
  const deviceId = decodeURIComponent(deviceAssignmentsGet[1]);
715
742
  try {
@@ -971,6 +998,26 @@ export class TomorrowOS extends EventEmitter {
971
998
  console.error("[TomorrowOS] pushLatestPolicy on resume failed:", err);
972
999
  });
973
1000
  })();
1001
+ return;
1002
+ }
1003
+ if (type === "device.log") {
1004
+ const deviceId = ws.deviceId;
1005
+ if (!deviceId)
1006
+ return;
1007
+ const rawLevel = String(msg.level ?? "info").toLowerCase();
1008
+ const level = rawLevel === "error" || rawLevel === "warn" ? rawLevel : "info";
1009
+ const message = String(msg.message ?? "").trim();
1010
+ if (!message)
1011
+ return;
1012
+ const source = typeof msg.source === "string" ? msg.source : undefined;
1013
+ const details = msg.details;
1014
+ const timestamp = typeof msg.timestamp === "string" && msg.timestamp.trim()
1015
+ ? msg.timestamp
1016
+ : new Date().toISOString();
1017
+ this.pushDeviceLog(deviceId, { timestamp, level, message, source, details });
1018
+ if (level === "error") {
1019
+ console.error(`[TomorrowOS][device-log][${deviceId}] ${message}`);
1020
+ }
974
1021
  }
975
1022
  });
976
1023
  ws.on("close", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
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.4.1",
3
+ "version": "0.4.2",
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",
@@ -10,7 +10,7 @@
10
10
  "build-player": "tomorrowos build --platform tizen"
11
11
  },
12
12
  "dependencies": {
13
- "@tomorrowos/sdk": "^0.4.1"
13
+ "@tomorrowos/sdk": "^0.4.2"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^20.0.0",
@@ -657,7 +657,9 @@ function renderDeviceCards() {
657
657
  ["System", device.system || device.platform || "—"],
658
658
  ["Device online", formatDeviceOnlineLabel(device)],
659
659
  ["Last boot", formatDateTimeSeconds(device.lastBootAt)],
660
- ["Latest push", formatDateTimeSeconds(device.lastPolicyPushAt)]
660
+ ["Latest push", formatDateTimeSeconds(device.lastPolicyPushAt)],
661
+ ["Latest error", device.latestErrorMessage || "—"],
662
+ ["Error at", formatDateTimeSeconds(device.latestErrorAt)]
661
663
  ];
662
664
  for (const [label, value] of rows) {
663
665
  const row = document.createElement("div");
@@ -702,6 +704,11 @@ function renderDeviceCards() {
702
704
  clearBtn.textContent = "Clear";
703
705
  clearBtn.addEventListener("click", () => deviceAction(device.deviceId, "content/clear"));
704
706
 
707
+ const logsBtn = document.createElement("button");
708
+ logsBtn.type = "button";
709
+ logsBtn.textContent = "Logs";
710
+ logsBtn.addEventListener("click", () => viewDeviceLogs(device.deviceId));
711
+
705
712
  const unpairBtn = document.createElement("button");
706
713
  unpairBtn.type = "button";
707
714
  unpairBtn.className = "danger";
@@ -713,6 +720,7 @@ function renderDeviceCards() {
713
720
  actions.appendChild(capBtn);
714
721
  actions.appendChild(rebootBtn);
715
722
  actions.appendChild(clearBtn);
723
+ actions.appendChild(logsBtn);
716
724
  actions.appendChild(unpairBtn);
717
725
 
718
726
  card.appendChild(header);
@@ -933,6 +941,17 @@ async function deviceAction(deviceId, action) {
933
941
  if (action === "reboot" || action === "content/clear") await fetchDevices();
934
942
  }
935
943
 
944
+ async function viewDeviceLogs(deviceId) {
945
+ if (!deviceId) return;
946
+ const res = await fetch(`/device/${encodeURIComponent(deviceId)}/logs`);
947
+ const data = await res.json();
948
+ showResult({ deviceId, logs: data.logs || [] });
949
+ if (!res.ok) {
950
+ alert(data.error || "Failed to load logs");
951
+ return;
952
+ }
953
+ }
954
+
936
955
  function startDevicePolling() {
937
956
  if (devicePollTimer) clearInterval(devicePollTimer);
938
957
  void fetchDevices();