@tomorrowos/sdk 0.1.9 → 0.1.10

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.
@@ -11,5 +11,6 @@ export declare class MemoryStore implements TomorrowOSStore {
11
11
  deletePendingCode(code: string): Promise<void>;
12
12
  setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
13
13
  getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
14
+ deletePairedDevice(deviceId: string): Promise<void>;
14
15
  }
15
16
  //# sourceMappingURL=memory-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IAEjE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;CAG3C"}
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IAEjE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAIpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1D"}
@@ -20,4 +20,7 @@ export class MemoryStore {
20
20
  async getPairedDevice(deviceId) {
21
21
  return this.pairedDevices.get(deviceId);
22
22
  }
23
+ async deletePairedDevice(deviceId) {
24
+ this.pairedDevices.delete(deviceId);
25
+ }
23
26
  }
@@ -20,5 +20,6 @@ export interface TomorrowOSStore {
20
20
  deletePendingCode(code: string): Promise<void>;
21
21
  setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
22
22
  getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
23
+ deletePairedDevice(deviceId: string): Promise<void>;
23
24
  }
24
25
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;IACrE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;CAC5E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;IACrE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;IAC3E,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD"}
@@ -40,10 +40,19 @@ export declare class TomorrowOS extends EventEmitter {
40
40
  pairingVerify(code: string): Promise<{
41
41
  deviceId: string;
42
42
  }>;
43
+ /** Remove pairing for a device and notify it over WebSocket if connected. */
44
+ pairingUnpair(deviceId: string): Promise<{
45
+ deviceId: string;
46
+ notified: boolean;
47
+ }>;
43
48
  pairing: {
44
49
  verify: (code: string) => Promise<{
45
50
  deviceId: string;
46
51
  }>;
52
+ unpair: (deviceId: string) => Promise<{
53
+ deviceId: string;
54
+ notified: boolean;
55
+ }>;
47
56
  };
48
57
  device(deviceId: string): {
49
58
  sendCommand<T = unknown>(method: string, params?: Record<string, unknown>): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAwED,qBAAa,UAAW,SAAQ,YAAY;IAC1C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,eAAe,CAAgB;gBAE3B,OAAO,EAAE,iBAAiB;IAMtC,oEAAoE;IAC9D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAiChE,OAAO;uBACU,MAAM;sBAlCgC,MAAM;;MAmC3D;IAEF,MAAM,CAAC,QAAQ,EAAE,MAAM;oBAGD,CAAC,oBACT,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAU5E,OAAO,CAAC,mBAAmB;IA6D3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM;YAkD7B,iBAAiB;YAqCjB,cAAc;YAmCd,UAAU;YAqDV,gBAAgB;IAgE9B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,gBAAgB;CAgFzB"}
1
+ {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAyED,qBAAa,UAAW,SAAQ,YAAY;IAC1C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,eAAe,CAAgB;gBAE3B,OAAO,EAAE,iBAAiB;IAMtC,oEAAoE;IAC9D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAiChE,6EAA6E;IACvE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IA2BvF,OAAO;uBACU,MAAM;sBA9DgC,MAAM;;2BA+DxC,MAAM;sBA7BgC,MAAM;sBAAY,OAAO;;MA8BlF;IAEF,MAAM,CAAC,QAAQ,EAAE,MAAM;oBAGD,CAAC,oBACT,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAU5E,OAAO,CAAC,mBAAmB;IA6D3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM;YAkD7B,iBAAiB;YAqCjB,cAAc;YAmCd,UAAU;YAkEV,gBAAgB;IAgE9B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,gBAAgB;CAgFzB"}
@@ -90,8 +90,31 @@ export class TomorrowOS extends EventEmitter {
90
90
  this.emit("device.paired", { deviceId: record.deviceId });
91
91
  return { deviceId: record.deviceId };
92
92
  }
93
+ /** Remove pairing for a device and notify it over WebSocket if connected. */
94
+ async pairingUnpair(deviceId) {
95
+ const id = String(deviceId || "").trim();
96
+ if (!id) {
97
+ const err = new Error("deviceId is required");
98
+ err.code = "PAIRING_INVALID";
99
+ throw err;
100
+ }
101
+ await this.store.deletePairedDevice(id);
102
+ const ws = this.devices.get(id);
103
+ let notified = false;
104
+ if (ws && ws.readyState === WebSocket.OPEN) {
105
+ ws.send(JSON.stringify({
106
+ type: "pairing.unpaired",
107
+ method: "tomorrowos.pairing.unpair",
108
+ deviceId: id
109
+ }));
110
+ notified = true;
111
+ }
112
+ this.emit("device.unpaired", { deviceId: id });
113
+ return { deviceId: id, notified };
114
+ }
93
115
  pairing = {
94
- verify: (code) => this.pairingVerify(code)
116
+ verify: (code) => this.pairingVerify(code),
117
+ unpair: (deviceId) => this.pairingUnpair(deviceId)
95
118
  };
96
119
  device(deviceId) {
97
120
  const self = this;
@@ -291,6 +314,19 @@ export class TomorrowOS extends EventEmitter {
291
314
  }
292
315
  return;
293
316
  }
317
+ if (req.method === "POST" && pathname === "/pairing/unpair") {
318
+ const body = (await readJsonBody(req));
319
+ const deviceId = typeof body.deviceId === "string" ? body.deviceId : "";
320
+ try {
321
+ const result = await this.pairingUnpair(deviceId);
322
+ sendJson(res, 200, { status: "success", ...result });
323
+ }
324
+ catch (e) {
325
+ const msg = e instanceof Error ? e.message : "Unpair failed";
326
+ sendJson(res, 400, { status: "failed", error: msg });
327
+ }
328
+ return;
329
+ }
294
330
  if (req.method === "POST") {
295
331
  const parsed = parseDevicePath(pathname);
296
332
  if (parsed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
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.1.9",
3
+ "version": "0.1.10",
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.1.9"
13
+ "@tomorrowos/sdk": "^0.1.10"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^20.0.0",
@@ -19,6 +19,7 @@
19
19
  <div class="row">
20
20
  <input id="code" maxlength="6" placeholder="6-digit code" />
21
21
  <button type="button" onclick="verify()">Verify</button>
22
+ <button type="button" class="danger" onclick="unpair()">Unpair</button>
22
23
  </div>
23
24
  <input type="hidden" id="deviceId" />
24
25
  </section>
@@ -104,6 +104,19 @@ function defaultDurationMs(type) {
104
104
  return 10000;
105
105
  }
106
106
 
107
+ function normalizeDurationMs(item) {
108
+ const maxMs = 3600 * 1000;
109
+ const minMs = 1000;
110
+ let ms = Number(item?.durationMs);
111
+ if (!Number.isFinite(ms) || ms < minMs) {
112
+ return defaultDurationMs(item?.type);
113
+ }
114
+ if (ms === 1000000) {
115
+ return defaultDurationMs(item?.type);
116
+ }
117
+ return Math.min(maxMs, ms);
118
+ }
119
+
107
120
  function savePlaylistDraft() {
108
121
  localStorage.setItem(PANEL_PLAYLIST_KEY, JSON.stringify(playlistItems));
109
122
  }
@@ -142,7 +155,11 @@ function loadPlaylistDraft() {
142
155
  if (!raw) return;
143
156
  const parsed = JSON.parse(raw);
144
157
  if (Array.isArray(parsed)) {
145
- playlistItems = parsed;
158
+ playlistItems = parsed.map((item) => ({
159
+ ...item,
160
+ durationMs: normalizeDurationMs(item)
161
+ }));
162
+ savePlaylistDraft();
146
163
  renderPlaylist();
147
164
  }
148
165
  } catch {
@@ -200,9 +217,10 @@ function renderPlaylist() {
200
217
  durInput.max = "3600";
201
218
  durInput.value = String(Math.round(item.durationMs / 1000));
202
219
  durInput.addEventListener("change", () => {
203
- item.durationMs = Math.max(1000, Number(durInput.value) || 10) * 1000;
220
+ const seconds = Math.min(3600, Math.max(1, Number(durInput.value) || 10));
221
+ item.durationMs = seconds * 1000;
222
+ meta.textContent = `${item.type} · ${seconds}s`;
204
223
  savePlaylistDraft();
205
- renderPlaylist();
206
224
  });
207
225
 
208
226
  const removeBtn = document.createElement("button");
@@ -313,6 +331,39 @@ async function verify() {
313
331
  }
314
332
  }
315
333
 
334
+ async function unpair() {
335
+ const deviceId = getPanelDeviceId();
336
+ if (!deviceId) {
337
+ alert("No paired device in this panel.");
338
+ return;
339
+ }
340
+
341
+ if (
342
+ !confirm(
343
+ "Unpair this device? The screen will show a new 6-digit code and this panel will forget the device."
344
+ )
345
+ ) {
346
+ return;
347
+ }
348
+
349
+ const res = await fetch("/pairing/unpair", {
350
+ method: "POST",
351
+ headers: { "Content-Type": "application/json" },
352
+ body: JSON.stringify({ deviceId })
353
+ });
354
+
355
+ const data = await res.json();
356
+ showResult(data);
357
+
358
+ if (res.ok) {
359
+ setPanelDeviceId("");
360
+ const codeInput = document.getElementById("code");
361
+ if (codeInput) codeInput.value = "";
362
+ } else {
363
+ alert(data.error || "Unpair failed");
364
+ }
365
+ }
366
+
316
367
  async function publish() {
317
368
  const deviceId = getPanelDeviceId();
318
369