@makerbi/remodex 1.5.8 → 2.0.1

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
@@ -120,7 +120,7 @@ async function main({
120
120
  ok: true,
121
121
  currentVersion: version,
122
122
  plistPath: result?.plistPath,
123
- pairingSession: result?.pairingSession,
123
+ pairingSession: sanitizePairingSessionForOutput(result?.pairingSession),
124
124
  },
125
125
  message: "[remodex] macOS bridge service is running.",
126
126
  jsonOutput,
@@ -141,7 +141,7 @@ async function main({
141
141
  ok: true,
142
142
  currentVersion: version,
143
143
  plistPath: result?.plistPath,
144
- pairingSession: result?.pairingSession,
144
+ pairingSession: sanitizePairingSessionForOutput(result?.pairingSession),
145
145
  },
146
146
  message: "[remodex] macOS bridge service restarted.",
147
147
  jsonOutput,
@@ -156,10 +156,20 @@ async function main({
156
156
  consoleImpl,
157
157
  exitImpl,
158
158
  });
159
- consoleImpl.log("[remodex] Refreshing bridge pairing QR...");
160
159
  const result = await deps.startMacOSBridgeService({
161
160
  waitForPairing: true,
162
161
  });
162
+ if (jsonOutput) {
163
+ emitJson({
164
+ ok: true,
165
+ currentVersion: version,
166
+ plistPath: result?.plistPath,
167
+ pairingSession: sanitizePairingSessionForOutput(result?.pairingSession),
168
+ });
169
+ return;
170
+ }
171
+
172
+ consoleImpl.log("[remodex] Refreshing bridge pairing QR...");
163
173
  deps.printMacOSBridgePairingQr({
164
174
  pairingSession: result.pairingSession,
165
175
  });
@@ -193,7 +203,7 @@ async function main({
193
203
  });
194
204
  if (jsonOutput) {
195
205
  emitJson({
196
- ...deps.getMacOSBridgeServiceStatus(),
206
+ ...sanitizeBridgeServiceStatusForOutput(deps.getMacOSBridgeServiceStatus()),
197
207
  currentVersion: version,
198
208
  });
199
209
  return;
@@ -272,7 +282,7 @@ async function main({
272
282
  "Usage: remodex up | remodex run [--extra-device|--extra-devices=N] | remodex start | remodex restart | "
273
283
  + "remodex qr | remodex pair | remodex stop | remodex status | "
274
284
  + "remodex reset-pairing | remodex resume | remodex watch [threadId] | remodex --version | "
275
- + "append --json to start/restart/stop/status/reset-pairing/resume for machine-readable output"
285
+ + "append --json to start/restart/qr/pair/stop/status/reset-pairing/resume for machine-readable output"
276
286
  );
277
287
  exitImpl(1);
278
288
  }
@@ -361,6 +371,47 @@ function emitJson(payload) {
361
371
  process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
362
372
  }
363
373
 
374
+ // Keeps machine-readable CLI output useful without exposing relay endpoints or live pairing payloads.
375
+ function sanitizeBridgeServiceStatusForOutput(status = {}) {
376
+ const sanitized = {
377
+ ...status,
378
+ daemonConfig: sanitizeDaemonConfigForOutput(status.daemonConfig),
379
+ pairingSession: sanitizePairingSessionForOutput(status.pairingSession),
380
+ };
381
+ return sanitized;
382
+ }
383
+
384
+ function sanitizeDaemonConfigForOutput(config) {
385
+ if (!config || typeof config !== "object") {
386
+ return config || null;
387
+ }
388
+ const { relayUrl, pushServiceUrl, ...rest } = config;
389
+ return {
390
+ ...rest,
391
+ relayConfigured: Boolean(relayUrl),
392
+ pushServiceConfigured: Boolean(pushServiceUrl),
393
+ };
394
+ }
395
+
396
+ function sanitizePairingSessionForOutput(pairingSession) {
397
+ if (!pairingSession || typeof pairingSession !== "object") {
398
+ return pairingSession || null;
399
+ }
400
+ const payload = pairingSession.pairingPayload || {};
401
+ return {
402
+ createdAt: pairingSession.createdAt,
403
+ pairingCode: pairingSession.pairingCode,
404
+ pairingPayload: {
405
+ v: payload.v,
406
+ expiresAt: payload.expiresAt,
407
+ hasRelay: Boolean(payload.relay),
408
+ hasSessionId: Boolean(payload.sessionId),
409
+ hasMacIdentityPublicKey: Boolean(payload.macIdentityPublicKey),
410
+ displayName: payload.displayName,
411
+ },
412
+ };
413
+ }
414
+
364
415
  function assertMacOSCommand(name, {
365
416
  platform = process.platform,
366
417
  consoleImpl = console,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makerbi/remodex",
3
- "version": "1.5.8",
3
+ "version": "2.0.1",
4
4
  "description": "Local bridge between Codex and the Remodex mobile app. Run `remodex up` to start.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/bridge.js CHANGED
@@ -5,7 +5,7 @@
5
5
  // Depends on: ws, crypto, os, ./bridge-status, ./codex-desktop-refresher, ./codex-transport, ./rollout-watch, ./voice-handler
6
6
 
7
7
  const WebSocket = require("ws");
8
- const { randomBytes, randomUUID } = require("crypto");
8
+ const { createHash, randomBytes, randomUUID } = require("crypto");
9
9
  const { execFile, spawn } = require("child_process");
10
10
  const fs = require("fs");
11
11
  const path = require("path");
@@ -46,6 +46,7 @@ const { createPushNotificationTracker } = require("./push-notification-tracker")
46
46
  const { resolveCodexGeneratedImagesRoot } = require("./codex-home");
47
47
  const {
48
48
  loadOrCreateBridgeDeviceState,
49
+ rememberLastSeenClientDeviceKind,
49
50
  rememberLastSeenPhoneAppVersion,
50
51
  resolveBridgeRelaySession,
51
52
  } = require("./secure-device-state");
@@ -238,6 +239,7 @@ function startBridge({
238
239
  requestId: null,
239
240
  startedAt: 0,
240
241
  };
242
+ let activePhoneSummary = null;
241
243
  const secureTransport = createBridgeSecureTransport({
242
244
  sessionId,
243
245
  relayUrl: relayBaseUrl,
@@ -247,6 +249,13 @@ function startBridge({
247
249
  deviceState = nextDeviceState;
248
250
  sendRelayRegistrationUpdate(nextDeviceState);
249
251
  },
252
+ onSecureSessionReady(session) {
253
+ activePhoneSummary = buildActivePhoneSummary(session, deviceState);
254
+ const lastPublishedBridgeStatus = bridgeStatusPublisher.latest();
255
+ if (lastPublishedBridgeStatus) {
256
+ publishBridgeStatus(lastPublishedBridgeStatus);
257
+ }
258
+ },
250
259
  });
251
260
  let primaryRelayChannel = null;
252
261
  // Keeps one stable sender identity across reconnects so buffered replay state
@@ -420,6 +429,9 @@ function startBridge({
420
429
 
421
430
  lastConnectionStatus = status;
422
431
  lastConnectionError = lastError;
432
+ if (status !== "connected") {
433
+ activePhoneSummary = null;
434
+ }
423
435
  publishBridgeStatus({
424
436
  state: "running",
425
437
  connectionStatus: status,
@@ -1529,6 +1541,20 @@ function startBridge({
1529
1541
  function bridgeManagedInitializeCompatibilityError(params) {
1530
1542
  const clientInfo = params && typeof params === "object" ? params.clientInfo : null;
1531
1543
  const clientName = normalizeNonEmptyString(clientInfo?.name);
1544
+ const clientDeviceKind = classifyClientDeviceKind(clientName);
1545
+ if (clientDeviceKind) {
1546
+ deviceState = rememberLastSeenClientDeviceKind(deviceState, clientDeviceKind);
1547
+ if (activePhoneSummary?.connected) {
1548
+ activePhoneSummary = {
1549
+ ...activePhoneSummary,
1550
+ deviceKind: clientDeviceKind,
1551
+ };
1552
+ const lastPublishedBridgeStatus = bridgeStatusPublisher.latest();
1553
+ if (lastPublishedBridgeStatus) {
1554
+ publishBridgeStatus(lastPublishedBridgeStatus);
1555
+ }
1556
+ }
1557
+ }
1532
1558
  if (clientName !== "codexmobile_ios") {
1533
1559
  return null;
1534
1560
  }
@@ -1697,7 +1723,11 @@ function startBridge({
1697
1723
  }
1698
1724
 
1699
1725
  function publishBridgeStatus(status) {
1700
- bridgeStatusPublisher.publish(status);
1726
+ bridgeStatusPublisher.publish({
1727
+ ...status,
1728
+ activeDevice: activePhoneSummary,
1729
+ activePhone: activePhoneSummary,
1730
+ });
1701
1731
  }
1702
1732
 
1703
1733
  // Refreshes the relay's trusted-mac index after the QR bootstrap locks in a phone identity.
@@ -1950,6 +1980,47 @@ function buildMacRegistration(deviceState, pairingSession) {
1950
1980
  };
1951
1981
  }
1952
1982
 
1983
+ function buildActivePhoneSummary(session, deviceState = null) {
1984
+ const phoneFingerprint = shortFingerprint(session?.phoneDeviceId);
1985
+ if (!phoneFingerprint) {
1986
+ return null;
1987
+ }
1988
+
1989
+ return {
1990
+ connected: true,
1991
+ phoneFingerprint,
1992
+ deviceKind: normalizeNonEmptyString(deviceState?.lastSeenDeviceKind) || null,
1993
+ handshakeMode: normalizeNonEmptyString(session?.handshakeMode) || null,
1994
+ keyEpoch: Number.isFinite(session?.keyEpoch) ? session.keyEpoch : null,
1995
+ updatedAt: new Date().toISOString(),
1996
+ };
1997
+ }
1998
+
1999
+ function classifyClientDeviceKind(clientName) {
2000
+ const normalized = normalizeNonEmptyString(clientName).toLowerCase();
2001
+ if (!normalized) {
2002
+ return null;
2003
+ }
2004
+ if (normalized.includes("android")) {
2005
+ return "android";
2006
+ }
2007
+ if (normalized.includes("ios") || normalized.includes("iphone")) {
2008
+ return "iphone";
2009
+ }
2010
+ if (normalized.includes("macos") || normalized.includes("mac")) {
2011
+ return "mac";
2012
+ }
2013
+ return null;
2014
+ }
2015
+
2016
+ function shortFingerprint(value) {
2017
+ const normalized = normalizeNonEmptyString(value);
2018
+ if (!normalized) {
2019
+ return null;
2020
+ }
2021
+ return createHash("sha256").update(normalized).digest("hex").slice(0, 8);
2022
+ }
2023
+
1953
2024
  function shutdown(codex, getSocket, beforeExit = () => {}, { exitCode = 0 } = {}) {
1954
2025
  beforeExit();
1955
2026
 
@@ -2553,6 +2624,10 @@ function compactEmergencySingleTurnForRelay(turn, maxChars, maxItems) {
2553
2624
  "created_at",
2554
2625
  "completedAt",
2555
2626
  "completed_at",
2627
+ "timeZoneIdentifier",
2628
+ "timeZone",
2629
+ "timezone",
2630
+ "time_zone",
2556
2631
  "status",
2557
2632
  "role",
2558
2633
  "kind",
@@ -3822,8 +3897,16 @@ function compactHistoryItemForRelay(item, maxChars) {
3822
3897
  turn_id: typeof item?.turn_id === "string" ? item.turn_id : undefined,
3823
3898
  createdAt: relayScalarHistoryMetadata(item?.createdAt),
3824
3899
  created_at: relayScalarHistoryMetadata(item?.created_at),
3900
+ startedAt: relayScalarHistoryMetadata(item?.startedAt),
3901
+ started_at: relayScalarHistoryMetadata(item?.started_at),
3902
+ completedAt: relayScalarHistoryMetadata(item?.completedAt),
3903
+ completed_at: relayScalarHistoryMetadata(item?.completed_at),
3825
3904
  timestamp: relayScalarHistoryMetadata(item?.timestamp),
3826
3905
  time: relayScalarHistoryMetadata(item?.time),
3906
+ timeZoneIdentifier: relayScalarHistoryMetadata(item?.timeZoneIdentifier),
3907
+ timeZone: relayScalarHistoryMetadata(item?.timeZone),
3908
+ timezone: relayScalarHistoryMetadata(item?.timezone),
3909
+ time_zone: relayScalarHistoryMetadata(item?.time_zone),
3827
3910
  relayPayloadTruncated: true,
3828
3911
  };
3829
3912
  const tailText = maxChars > 0 ? firstRelayTextTail(item, maxChars) : "";