@leg3ndy/otto-bridge 0.5.3 → 0.5.4

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/config.js CHANGED
@@ -67,6 +67,9 @@ export async function loadBridgeConfig() {
67
67
  ...parsed,
68
68
  apiBaseUrl: sanitizeApiBaseUrl(parsed.apiBaseUrl),
69
69
  wsUrl: buildWebSocketUrl(parsed.apiBaseUrl),
70
+ // Older pairings may have persisted an outdated bridgeVersion in config.json.
71
+ // The runtime must always report the currently installed package version.
72
+ bridgeVersion: BRIDGE_VERSION,
70
73
  executor: resolveExecutorConfig(undefined, migrateLegacyExecutor(parsed.executor)),
71
74
  };
72
75
  }
package/dist/runtime.js CHANGED
@@ -6,6 +6,52 @@ import { JobCancelledError } from "./executors/shared.js";
6
6
  function delay(ms) {
7
7
  return new Promise((resolve) => setTimeout(resolve, ms));
8
8
  }
9
+ function parseSemverTuple(value) {
10
+ const text = String(value || "").trim().replace(/^[vV]/, "");
11
+ if (!text) {
12
+ return null;
13
+ }
14
+ const parts = text.split(".");
15
+ const parsed = [];
16
+ for (const part of parts) {
17
+ const match = part.match(/^(\d+)/);
18
+ if (!match) {
19
+ return null;
20
+ }
21
+ parsed.push(Number(match[1]));
22
+ }
23
+ return parsed.length > 0 ? parsed : null;
24
+ }
25
+ function compareSemver(left, right) {
26
+ const a = parseSemverTuple(left);
27
+ const b = parseSemverTuple(right);
28
+ if (!a && !b)
29
+ return 0;
30
+ if (!a)
31
+ return -1;
32
+ if (!b)
33
+ return 1;
34
+ const maxLength = Math.max(a.length, b.length);
35
+ for (let index = 0; index < maxLength; index += 1) {
36
+ const leftPart = a[index] ?? 0;
37
+ const rightPart = b[index] ?? 0;
38
+ if (leftPart < rightPart)
39
+ return -1;
40
+ if (leftPart > rightPart)
41
+ return 1;
42
+ }
43
+ return 0;
44
+ }
45
+ function bridgeReleaseFromMessage(message) {
46
+ const nested = message.bridge_release;
47
+ if (nested && typeof nested === "object") {
48
+ return nested;
49
+ }
50
+ if (message.latest_version || message.min_supported_version || message.update_command) {
51
+ return message;
52
+ }
53
+ return null;
54
+ }
9
55
  async function parseSocketMessage(data) {
10
56
  if (typeof data === "string") {
11
57
  return JSON.parse(data);
@@ -25,6 +71,7 @@ export class BridgeRuntime {
25
71
  config;
26
72
  reconnectDelayMs = DEFAULT_RECONNECT_BASE_DELAY_MS;
27
73
  executor;
74
+ lastBridgeReleaseNoticeKey = null;
28
75
  pendingConfirmations = new Map();
29
76
  activeCancels = new Map();
30
77
  constructor(config, executor) {
@@ -116,9 +163,11 @@ export class BridgeRuntime {
116
163
  const type = String(message.type || "");
117
164
  switch (type) {
118
165
  case "device.hello":
166
+ this.maybeLogBridgeReleaseNotice(message);
119
167
  console.log(`[otto-bridge] server hello device=${String(message.device_id || "")}`);
120
168
  return;
121
169
  case "device.hello_ack":
170
+ this.maybeLogBridgeReleaseNotice(message);
122
171
  case "device.heartbeat_ack":
123
172
  return;
124
173
  case "device.job.start":
@@ -145,6 +194,29 @@ export class BridgeRuntime {
145
194
  console.log(`[otto-bridge] event=${type || "unknown"} payload=${JSON.stringify(message)}`);
146
195
  }
147
196
  }
197
+ maybeLogBridgeReleaseNotice(message) {
198
+ const release = bridgeReleaseFromMessage(message);
199
+ if (!release) {
200
+ return;
201
+ }
202
+ const latestVersion = String(release.latest_version || "").trim();
203
+ const minSupportedVersion = String(release.min_supported_version || "").trim();
204
+ const updateCommand = String(release.update_command || "otto-bridge update").trim() || "otto-bridge update";
205
+ const updateRequired = release.update_required === true || (minSupportedVersion ? compareSemver(this.config.bridgeVersion, minSupportedVersion) < 0 : false);
206
+ const updateAvailable = release.update_available === true || (latestVersion ? compareSemver(this.config.bridgeVersion, latestVersion) < 0 : false);
207
+ const noticeKey = [latestVersion, minSupportedVersion, updateRequired ? "req" : "ok", updateAvailable ? "avail" : "cur"].join("|");
208
+ if (!noticeKey.trim() || this.lastBridgeReleaseNoticeKey === noticeKey) {
209
+ return;
210
+ }
211
+ this.lastBridgeReleaseNoticeKey = noticeKey;
212
+ if (updateRequired) {
213
+ console.warn(`[otto-bridge] update required current=${this.config.bridgeVersion} min_supported=${minSupportedVersion || "unknown"} latest=${latestVersion || "unknown"} command="${updateCommand}"`);
214
+ return;
215
+ }
216
+ if (updateAvailable) {
217
+ console.log(`[otto-bridge] update available current=${this.config.bridgeVersion} latest=${latestVersion || "unknown"} command="${updateCommand}"`);
218
+ }
219
+ }
148
220
  resolveConfirmation(message) {
149
221
  const jobId = String(message.job_id || "");
150
222
  const action = String(message.action || "").trim().toLowerCase();
package/dist/types.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export const BRIDGE_CONFIG_VERSION = 1;
2
- export const BRIDGE_VERSION = "0.5.3";
2
+ export const BRIDGE_VERSION = "0.5.4";
3
3
  export const BRIDGE_PACKAGE_NAME = "@leg3ndy/otto-bridge";
4
4
  export const DEFAULT_API_BASE_URL = "http://localhost:8000";
5
5
  export const DEFAULT_POLL_INTERVAL_MS = 3000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leg3ndy/otto-bridge",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local companion for Otto Bridge device pairing and WebSocket runtime.",