@makerbi/remodex 1.3.9 → 1.3.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.
package/bin/remodex.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@makerbi/remodex",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
4
4
  "description": "Local bridge between Codex and the Remodex mobile app. Run `remodex up` to start.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
- "remodex": "./bin/remodex.js"
7
+ "remodex": "bin/remodex.js"
8
8
  },
9
9
  "files": [
10
10
  "bin/",
package/src/bridge.js CHANGED
@@ -55,6 +55,9 @@ const RELAY_WATCHDOG_PING_INTERVAL_MS = 10_000;
55
55
  const RELAY_WATCHDOG_STALE_AFTER_MS = 70_000;
56
56
  const BRIDGE_STATUS_HEARTBEAT_INTERVAL_MS = 5_000;
57
57
  const STALE_RELAY_STATUS_MESSAGE = "Relay heartbeat stalled; reconnect pending.";
58
+ const CLOSE_CODE_INVALID_RELAY_REQUEST = 4000;
59
+ const CLOSE_CODE_REPLACED_BY_NEW_MAC = 4001;
60
+ const CLOSE_CODE_MAC_UNAUTHORIZED = 4005;
58
61
  const RELAY_HISTORY_IMAGE_REFERENCE_URL = "remodex://history-image-elided";
59
62
  const RELAY_THREAD_PAYLOAD_SOFT_LIMIT_BYTES = 4 * 1024 * 1024;
60
63
  const RELAY_HISTORY_TEXT_TAIL_LIMIT_CHARS = 24_000;
@@ -146,6 +149,7 @@ function startBridge({
146
149
  let lastRelayActivityAt = 0;
147
150
  let lastPublishedBridgeStatus = null;
148
151
  let lastConnectionStatus = null;
152
+ let lastConnectionError = "";
149
153
  let codexLaunchState = config.codexEndpoint ? "connected" : "starting";
150
154
  let codexHandshakeState = config.codexEndpoint ? "warm" : "cold";
151
155
  const forwardedInitializeRequestIds = new Set();
@@ -322,35 +326,42 @@ function startBridge({
322
326
  }
323
327
 
324
328
  // Keeps npm start output compact by emitting only high-signal connection states.
325
- function logConnectionStatus(status) {
326
- if (lastConnectionStatus === status) {
329
+ function logConnectionStatus(status, lastError = "") {
330
+ if (lastConnectionStatus === status && lastConnectionError === lastError) {
327
331
  return;
328
332
  }
329
333
 
330
334
  lastConnectionStatus = status;
335
+ lastConnectionError = lastError;
331
336
  publishBridgeStatus({
332
337
  state: "running",
333
338
  connectionStatus: status,
334
339
  pid: process.pid,
335
- lastError: "",
340
+ lastError,
336
341
  });
337
342
  console.log(`[remodex] ${status}`);
343
+ if (lastError) {
344
+ console.error(`[remodex] ${lastError}`);
345
+ }
338
346
  }
339
347
 
340
348
  // Retries the relay socket while preserving the active Codex process and session id.
341
- function scheduleRelayReconnect(closeCode) {
349
+ function scheduleRelayReconnect(closeCode, closeReason = "") {
342
350
  if (isShuttingDown) {
343
351
  return;
344
352
  }
345
353
 
346
- if (closeCode === 4000 || closeCode === 4001) {
347
- logConnectionStatus("disconnected");
354
+ if (isTerminalRelayCloseCode(closeCode)) {
355
+ const lastError = buildRelayCloseStatusError(closeCode, closeReason);
356
+ logConnectionStatus("disconnected", lastError);
348
357
  shutdown(codex, () => socket, () => {
349
358
  isShuttingDown = true;
350
359
  bridgeWakeAssertion.stop();
351
360
  clearReconnectTimer();
352
361
  clearRelayWatchdog();
353
362
  clearBridgeStatusHeartbeat();
363
+ }, {
364
+ exitCode: closeCode === CLOSE_CODE_MAC_UNAUTHORIZED ? 1 : 0,
354
365
  });
355
366
  return;
356
367
  }
@@ -421,18 +432,19 @@ function startBridge({
421
432
  markRelayActivity();
422
433
  });
423
434
 
424
- nextSocket.on("close", (code) => {
435
+ nextSocket.on("close", (code, reason) => {
436
+ const closeReason = normalizeWebSocketCloseReason(reason);
425
437
  if (socket === nextSocket) {
426
438
  clearRelayWatchdog();
427
439
  }
428
- logConnectionStatus("disconnected");
440
+ logConnectionStatus("disconnected", buildRelayCloseStatusError(code, closeReason));
429
441
  if (socket === nextSocket) {
430
442
  socket = null;
431
443
  }
432
444
  stopContextUsageWatcher();
433
445
  rolloutLiveMirror?.stopAll();
434
446
  desktopRefresher.handleTransportReset();
435
- scheduleRelayReconnect(code);
447
+ scheduleRelayReconnect(code, closeReason);
436
448
  });
437
449
 
438
450
  nextSocket.on("error", () => {
@@ -473,12 +485,13 @@ function startBridge({
473
485
  codex.onClose(() => {
474
486
  clearRelayWatchdog();
475
487
  clearBridgeStatusHeartbeat();
476
- logConnectionStatus("disconnected");
488
+ const lastError = lastConnectionError || "";
489
+ logConnectionStatus("disconnected", lastError);
477
490
  publishBridgeStatus({
478
491
  state: "stopped",
479
492
  connectionStatus: "disconnected",
480
493
  pid: process.pid,
481
- lastError: "",
494
+ lastError,
482
495
  });
483
496
  isShuttingDown = true;
484
497
  bridgeWakeAssertion.stop();
@@ -1281,7 +1294,7 @@ function buildMacRegistration(deviceState, pairingSession) {
1281
1294
  };
1282
1295
  }
1283
1296
 
1284
- function shutdown(codex, getSocket, beforeExit = () => {}) {
1297
+ function shutdown(codex, getSocket, beforeExit = () => {}, { exitCode = 0 } = {}) {
1285
1298
  beforeExit();
1286
1299
 
1287
1300
  const socket = getSocket();
@@ -1291,7 +1304,41 @@ function shutdown(codex, getSocket, beforeExit = () => {}) {
1291
1304
 
1292
1305
  codex.shutdown();
1293
1306
 
1294
- setTimeout(() => process.exit(0), 100);
1307
+ setTimeout(() => process.exit(exitCode), 100);
1308
+ }
1309
+
1310
+ function isTerminalRelayCloseCode(closeCode) {
1311
+ return closeCode === CLOSE_CODE_INVALID_RELAY_REQUEST
1312
+ || closeCode === CLOSE_CODE_REPLACED_BY_NEW_MAC
1313
+ || closeCode === CLOSE_CODE_MAC_UNAUTHORIZED;
1314
+ }
1315
+
1316
+ function normalizeWebSocketCloseReason(reason) {
1317
+ if (typeof reason === "string") {
1318
+ return reason.trim();
1319
+ }
1320
+
1321
+ if (Buffer.isBuffer(reason)) {
1322
+ return reason.toString("utf8").trim();
1323
+ }
1324
+
1325
+ return "";
1326
+ }
1327
+
1328
+ function buildRelayCloseStatusError(closeCode, closeReason = "") {
1329
+ if (!Number.isInteger(closeCode) || closeCode === 1000 || closeCode === 1005) {
1330
+ return "";
1331
+ }
1332
+
1333
+ const normalizedReason = normalizeNonEmptyString(closeReason);
1334
+ if (closeCode === CLOSE_CODE_MAC_UNAUTHORIZED) {
1335
+ return normalizedReason
1336
+ || "Relay authorization failed. Set REMODEX_RELAY_ACCESS_TOKEN or use a relay that does not require a Mac access token.";
1337
+ }
1338
+
1339
+ return normalizedReason
1340
+ ? `Relay closed the connection (${closeCode}): ${normalizedReason}`
1341
+ : `Relay closed the connection (${closeCode}).`;
1295
1342
  }
1296
1343
 
1297
1344
  function extractBridgeMessageContext(rawMessage) {
@@ -2103,10 +2150,12 @@ function persistBridgePreferences(
2103
2150
 
2104
2151
  module.exports = {
2105
2152
  buildHeartbeatBridgeStatus,
2153
+ buildRelayCloseStatusError,
2106
2154
  buildRelayAccessTokenHeaders,
2107
2155
  buildRelayUserAgentHeader,
2108
2156
  createMacOSBridgeWakeAssertion,
2109
2157
  hasRelayConnectionGoneStale,
2158
+ isTerminalRelayCloseCode,
2110
2159
  persistBridgePreferences,
2111
2160
  sanitizeLiveGeneratedImageMessageForRelay,
2112
2161
  sanitizeThreadHistoryImagesForRelay,
@@ -10,7 +10,7 @@ const { version: installedVersion = "" } = require("../package.json");
10
10
  const DEFAULT_CACHE_TTL_MS = 30 * 60 * 1000;
11
11
  const DEFAULT_EMPTY_CACHE_RETRY_MS = 60 * 1000;
12
12
  const DEFAULT_INITIAL_FETCH_WAIT_MS = 250;
13
- const REMODEX_REGISTRY_URL = "https://registry.npmjs.org/remodex/latest";
13
+ const REMODEX_REGISTRY_URL = "https://registry.npmjs.org/@makerbi%2fremodex/latest";
14
14
 
15
15
  function createBridgePackageVersionStatusReader({
16
16
  cacheTtlMs = DEFAULT_CACHE_TTL_MS,