codex-relay 1.4.5 → 1.4.7

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/paths.js CHANGED
@@ -92,6 +92,7 @@ function createQueryableDatabase(executor) {
92
92
  }
93
93
  //#endregion
94
94
  //#region src/pairing-store.ts
95
+ const permanentClientSessionExpiresAt = Date.parse("9999-12-31T23:59:59.999Z");
95
96
  async function createTursoPairingSessionStore(path) {
96
97
  if (path !== ":memory:") await mkdir(dirname(path), { recursive: true });
97
98
  const db = await connect(path);
@@ -134,8 +135,8 @@ async function createTursoPairingSessionStore(path) {
134
135
  );
135
136
  `);
136
137
  await ensurePairingSessionColumns();
137
- async function countActive(now) {
138
- const row = await db.prepare("SELECT COUNT(DISTINCT COALESCE(client_session_id, token_hash)) AS count FROM pairing_sessions WHERE expires_at > ?").get(now);
138
+ async function countActive() {
139
+ const row = await db.prepare("SELECT COUNT(DISTINCT COALESCE(client_session_id, token_hash)) AS count FROM pairing_sessions").get();
139
140
  return Number(row?.count ?? 0);
140
141
  }
141
142
  async function deleteSession(tokenHash) {
@@ -238,8 +239,8 @@ async function createTursoPairingSessionStore(path) {
238
239
  created_at,
239
240
  updated_at
240
241
  )
241
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(tokenHash, session.clientSessionId ?? null, session.clientName ?? null, session.expiresAt, secure?.keyEpoch ?? null, secure?.mobileToServerKey ?? null, secure?.serverToMobileKey ?? null, secure?.lastMobileCounter ?? null, secure?.nextServerCounter ?? null, now, now);
242
- return countActive(now);
242
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(tokenHash, session.clientSessionId ?? null, session.clientName ?? null, session.expiresAt ?? permanentClientSessionExpiresAt, secure?.keyEpoch ?? null, secure?.mobileToServerKey ?? null, secure?.serverToMobileKey ?? null, secure?.lastMobileCounter ?? null, secure?.nextServerCounter ?? null, now, now);
243
+ return countActive();
243
244
  },
244
245
  deleteSession,
245
246
  deletePendingPairing,
@@ -254,7 +255,7 @@ async function createTursoPairingSessionStore(path) {
254
255
  WHERE client_session_id = ?`).get(clientSessionId);
255
256
  return row ? pushNotificationSubscriptionFromRow(row) : void 0;
256
257
  },
257
- async getValidSession(tokenHash, now) {
258
+ async getValidSession(tokenHash) {
258
259
  const row = await db.prepare(`SELECT client_name AS clientName,
259
260
  client_session_id AS clientSessionId,
260
261
  expires_at AS expiresAt,
@@ -266,19 +267,14 @@ async function createTursoPairingSessionStore(path) {
266
267
  FROM pairing_sessions
267
268
  WHERE token_hash = ?`).get(tokenHash);
268
269
  if (!row) return;
269
- const expiresAt = Number(row.expiresAt);
270
- if (now > expiresAt) {
271
- await deleteSession(tokenHash);
272
- return;
273
- }
274
270
  return {
275
271
  clientSessionId: typeof row.clientSessionId === "string" ? row.clientSessionId : void 0,
276
272
  clientName: typeof row.clientName === "string" ? row.clientName : void 0,
277
- expiresAt,
273
+ expiresAt: Number(row.expiresAt),
278
274
  secureSession: decodeSecureSession(row)
279
275
  };
280
276
  },
281
- async listActivePushNotificationSubscriptions(now) {
277
+ async listActivePushNotificationSubscriptions() {
282
278
  return resultRows(await db.prepare(`SELECT subscriptions.client_session_id AS clientSessionId,
283
279
  subscriptions.expo_push_token AS expoPushToken,
284
280
  subscriptions.platform,
@@ -287,14 +283,12 @@ async function createTursoPairingSessionStore(path) {
287
283
  FROM push_notification_subscriptions AS subscriptions
288
284
  INNER JOIN pairing_sessions AS sessions
289
285
  ON sessions.client_session_id = subscriptions.client_session_id
290
- WHERE sessions.expires_at > ?
291
- GROUP BY subscriptions.client_session_id`).all(now)).flatMap((row) => {
286
+ GROUP BY subscriptions.client_session_id`).all()).flatMap((row) => {
292
287
  const subscription = pushNotificationSubscriptionFromRow(row);
293
288
  return subscription ? [subscription] : [];
294
289
  });
295
290
  },
296
- async pruneExpired(now) {
297
- await db.prepare("DELETE FROM pairing_sessions WHERE expires_at <= ?").run(now);
291
+ async pruneExpiredPendingPairings(now) {
298
292
  await db.prepare("DELETE FROM pending_pairings WHERE expires_at <= ?").run(now);
299
293
  await db.prepare(`DELETE FROM push_notification_subscriptions
300
294
  WHERE NOT EXISTS (
@@ -325,9 +319,9 @@ async function createTursoPairingSessionStore(path) {
325
319
  created_at,
326
320
  updated_at
327
321
  )
328
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(newTokenHash, session.clientSessionId ?? null, session.clientName ?? null, session.expiresAt, secure?.keyEpoch ?? null, secure?.mobileToServerKey ?? null, secure?.serverToMobileKey ?? null, secure?.lastMobileCounter ?? null, secure?.nextServerCounter ?? null, now, now);
322
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(newTokenHash, session.clientSessionId ?? null, session.clientName ?? null, session.expiresAt ?? permanentClientSessionExpiresAt, secure?.keyEpoch ?? null, secure?.mobileToServerKey ?? null, secure?.serverToMobileKey ?? null, secure?.lastMobileCounter ?? null, secure?.nextServerCounter ?? null, now, now);
329
323
  })();
330
- return countActive(now);
324
+ return countActive();
331
325
  },
332
326
  async updateSecureSession(tokenHash, secureSession) {
333
327
  const secure = encodeSecureSession(secureSession);
@@ -601,4 +595,4 @@ function defaultCodexRelayHome() {
601
595
  }
602
596
  }
603
597
  //#endregion
604
- export { getConnectUrlCandidates as a, createPairingQrPayload as i, codexRelayHome as n, getConnectUrlGuidance as o, legacyCodexRelayDataPath as r, createTursoPairingSessionStore as s, codexRelayDataPath as t };
598
+ export { getConnectUrlCandidates as a, permanentClientSessionExpiresAt as c, createPairingQrPayload as i, codexRelayHome as n, getConnectUrlGuidance as o, legacyCodexRelayDataPath as r, createTursoPairingSessionStore as s, codexRelayDataPath as t };
package/dist/src.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { $ as ResolveApprovalResponseSchema, A as PairRequestSchema, At as WorkspaceChangesResponseSchema, Bt as WorkspaceTailscaleServeResponseSchema, C as ListModelsResponseSchema, Ct as UpdateRuntimePreferencesRequestSchema, D as ListWorkspaceDirectoriesResponseSchema, E as ListThreadsResponseSchema, Et as VersionResponseSchema, Gt as WorkspaceTerminalSessionResponseSchema, H as PushNotificationSettingsResponseSchema, J as ReasoningEffortSchema, Jt as chatMessageDetailsFromPromptContext, Kt as WorkspaceTerminalStartRequestSchema, Mt as WorkspaceFileContentResponseSchema, O as ListWorkspaceFilesResponseSchema, Pt as WorkspaceGitActionResponseSchema, Q as ResolveApprovalRequestSchema, Qt as promptMarkdownWithSkills, S as KnownReasoningEffortSchema, St as ThreadSummarySchema, T as ListSkillsResponseSchema, Tt as UpdateWorkspaceFileContentRequestSchema, U as QueuedThreadInputActionResponseSchema, Ut as WorkspaceTerminalOutputResponseSchema, Vt as WorkspaceTerminalInputRequestSchema, Wt as WorkspaceTerminalResizeRequestSchema, X as RenameThreadRequestSchema, Y as RegisterPushNotificationRequestSchema, Yt as createOpenApiDocument, Z as RenameThreadResponseSchema, Zt as normalizePromptContext, _ as EncryptedPayloadSchema, _t as ThreadGoalStatusSchema, a as ArchiveThreadResponseSchema, at as RuntimePreferencesResponseSchema, b as InterruptThreadRunResponseSchema, ct as StatusResponseSchema, d as CheckoutWorkspaceBranchRequestSchema, dt as SubmitThreadInputResponseSchema, et as RewindThreadRequestSchema, gt as ThreadGoalSchema, h as CreateThreadRequestSchema, ht as ThreadGoalResponseSchema, j as PairResponseSchema, l as ChatMessageSchema, lt as StreamThreadRunEventSchema, m as ContextWindowUsageSchema, mt as ThreadDetailResponseSchema, ot as RuntimePreferencesSchema, p as CommitPushWorkspaceRequestSchema, pt as ThreadContextWindowResponseSchema, q as RateLimitsResponseSchema, qt as apiPaths, rn as stripPromptSkillMentions, tt as RunThreadRequestSchema, ut as StreamThreadRunRequestSchema, vt as ThreadMessageDetailFieldSchema, w as ListQueuedThreadInputsResponseSchema, wt as UpdateThreadGoalRequestSchema, y as ImageAttachmentUploadResponseSchema, yt as ThreadMessageDetailResponseSchema, zt as WorkspaceTailscaleServeRequestSchema } from "./api-schema2.js";
2
- import { a as getConnectUrlCandidates, i as createPairingQrPayload, o as getConnectUrlGuidance, r as legacyCodexRelayDataPath, s as createTursoPairingSessionStore, t as codexRelayDataPath } from "./paths.js";
2
+ import { a as getConnectUrlCandidates, c as permanentClientSessionExpiresAt, i as createPairingQrPayload, o as getConnectUrlGuidance, r as legacyCodexRelayDataPath, s as createTursoPairingSessionStore, t as codexRelayDataPath } from "./paths.js";
3
3
  import { createRequire } from "node:module";
4
4
  import qrcode from "qrcode-terminal";
5
5
  import { execFile, spawn } from "node:child_process";
@@ -872,7 +872,7 @@ function createExpoPushNotificationSender(fetchImpl = fetch) {
872
872
  }
873
873
  function createPushNotificationDispatcher(input) {
874
874
  return { async dispatch(event) {
875
- const subscriptions = await input.sessions.listActivePushNotificationSubscriptions(Date.now());
875
+ const subscriptions = await input.sessions.listActivePushNotificationSubscriptions();
876
876
  const selectedTokens = new Set(subscriptions.filter((subscription) => notificationEnabled(subscription, event.intent)).map((subscription) => subscription.expoPushToken));
877
877
  if (selectedTokens.size === 0) return;
878
878
  const delivery = await input.sender.send([...selectedTokens].map((expoPushToken) => notificationForEvent(expoPushToken, event)));
@@ -1536,7 +1536,7 @@ function createApp(options = {}) {
1536
1536
  }
1537
1537
  const token = parseBearerToken(c.req.header("authorization"));
1538
1538
  const tokenHash = token ? options.pairing.hashClientToken(token) : void 0;
1539
- const validSession = tokenHash ? await options.pairing.sessions.getValidSession(tokenHash, Date.now()) : void 0;
1539
+ const validSession = tokenHash ? await options.pairing.sessions.getValidSession(tokenHash) : void 0;
1540
1540
  if (!tokenHash || !validSession) return c.json(apiError("unauthorized", "Pair this device with the Codex Relay server."), 401);
1541
1541
  if (options.pairing.serverIdentity && !secureSessionsByTokenHash.has(tokenHash)) if (validSession.secureSession) secureSessionsByTokenHash.set(tokenHash, validSession.secureSession);
1542
1542
  else return c.json(apiError("secure_session_required", "Secure session expired. Pair this device again."), 401);
@@ -1557,7 +1557,7 @@ function createApp(options = {}) {
1557
1557
  const parsed = await parsePlainJson(c.req.raw, PairRequestSchema);
1558
1558
  if (!parsed.success) return c.json(validationError(parsed.error), 400);
1559
1559
  if (!parsed.data.secure || !options.pairing.serverIdentity) return c.json(apiError("secure_pairing_required", "Pairing requires the secure QR approval flow."), 400);
1560
- await options.pairing.sessions.pruneExpired(Date.now());
1560
+ await options.pairing.sessions.pruneExpiredPendingPairings(Date.now());
1561
1561
  const approvalCode = await createApprovalCode(options.pairing.sessions);
1562
1562
  const expiresAt = Date.now() + (options.pairing.approvalTtlMs ?? 300 * 1e3);
1563
1563
  const requestOrigin = externalRequestOrigin(c.req.url, (name) => c.req.header(name));
@@ -1595,11 +1595,10 @@ function createApp(options = {}) {
1595
1595
  approvalCode,
1596
1596
  approvalExpiresAt: new Date(pending.expiresAt).toISOString()
1597
1597
  }), 202);
1598
- await options.pairing.sessions.pruneExpired(Date.now());
1598
+ await options.pairing.sessions.pruneExpiredPendingPairings(Date.now());
1599
1599
  const clientToken = options.pairing.createClientToken();
1600
- const expiresAt = Date.now() + options.pairing.tokenTtlMs;
1601
1600
  const tokenHash = options.pairing.hashClientToken(clientToken);
1602
- const clientTokenExpiresAt = new Date(expiresAt).toISOString();
1601
+ const clientTokenExpiresAt = new Date(permanentClientSessionExpiresAt).toISOString();
1603
1602
  const pairing = createSecurePairing({
1604
1603
  approvalCode,
1605
1604
  clientEphemeralPublicKey: pending.clientEphemeralPublicKey,
@@ -1613,7 +1612,6 @@ function createApp(options = {}) {
1613
1612
  const tokenCount = await options.pairing.sessions.createSession(tokenHash, {
1614
1613
  clientSessionId: pending.clientSessionId,
1615
1614
  clientName: pending.clientName,
1616
- expiresAt,
1617
1615
  secureSession: pairing.session
1618
1616
  });
1619
1617
  await options.pairing.sessions.deletePendingPairing(approvalCode);
@@ -1656,14 +1654,12 @@ function createApp(options = {}) {
1656
1654
  const oldSession = oldToken ? await getValidClientSession(options.pairing, oldToken) : void 0;
1657
1655
  if (!oldToken || !oldSession) return c.json(apiError("unauthorized", "Pair this device with the Codex Relay server."), 401);
1658
1656
  const clientToken = options.pairing.createClientToken();
1659
- const expiresAt = Date.now() + options.pairing.tokenTtlMs;
1660
1657
  const oldTokenHash = options.pairing.hashClientToken(oldToken);
1661
1658
  const newTokenHash = options.pairing.hashClientToken(clientToken);
1662
1659
  const clientSessionId = normalizeClientSessionId(c.req.header("x-codex-relay-client-session-id")) ?? oldSession.clientSessionId;
1663
1660
  const tokenCount = await options.pairing.sessions.rotateSession(oldTokenHash, newTokenHash, {
1664
1661
  clientSessionId,
1665
1662
  clientName: oldSession.clientName,
1666
- expiresAt,
1667
1663
  secureSession: secureSessionsByTokenHash.get(oldTokenHash) ?? oldSession.secureSession
1668
1664
  });
1669
1665
  const secureSession = secureSessionsByTokenHash.get(oldTokenHash) ?? oldSession.secureSession;
@@ -1674,7 +1670,7 @@ function createApp(options = {}) {
1674
1670
  });
1675
1671
  const response = PairResponseSchema.parse({
1676
1672
  clientToken,
1677
- clientTokenExpiresAt: new Date(expiresAt).toISOString()
1673
+ clientTokenExpiresAt: new Date(permanentClientSessionExpiresAt).toISOString()
1678
1674
  });
1679
1675
  const jsonResponse = await secureJson(c, options.pairing, secureSessionsByTokenHash, response, 201);
1680
1676
  if (secureSession) {
@@ -2855,7 +2851,7 @@ async function pairedClientSessionIdForAuthorization(authorization, pairing) {
2855
2851
  if (!pairing) return;
2856
2852
  const token = parseBearerToken(authorization);
2857
2853
  if (!token) return;
2858
- return (await pairing.sessions.getValidSession(pairing.hashClientToken(token), Date.now()))?.clientSessionId;
2854
+ return (await pairing.sessions.getValidSession(pairing.hashClientToken(token)))?.clientSessionId;
2859
2855
  }
2860
2856
  function defaultPushNotificationPreferences() {
2861
2857
  return {
@@ -2897,7 +2893,7 @@ async function createApprovalCode(sessions) {
2897
2893
  throw new Error("Unable to allocate a pairing approval code.");
2898
2894
  }
2899
2895
  async function getValidClientSession(pairing, token) {
2900
- return pairing.sessions.getValidSession(pairing.hashClientToken(token), Date.now());
2896
+ return pairing.sessions.getValidSession(pairing.hashClientToken(token));
2901
2897
  }
2902
2898
  function externalRequestOrigin(requestUrl, header) {
2903
2899
  const request = new URL(requestUrl);
@@ -6970,7 +6966,6 @@ function errorMessage(error) {
6970
6966
  //#region src/index.ts
6971
6967
  const port = Number(process.env.PORT ?? 8787);
6972
6968
  const hostname$1 = process.env.HOST ?? "0.0.0.0";
6973
- const clientTokenTtlMs = 10080 * 60 * 1e3;
6974
6969
  const dangerouslyAutoApprove = process.env.CODEX_RELAY_DANGEROUSLY_AUTO_APPROVE === "1";
6975
6970
  const serverIdentity = await getServerIdentity();
6976
6971
  const approvalSecret = await getApprovalSecret();
@@ -7018,7 +7013,6 @@ serve({
7018
7013
  createClientToken: () => randomBytes(32).toString("base64url"),
7019
7014
  hashClientToken,
7020
7015
  sessions: sessionStore,
7021
- tokenTtlMs: clientTokenTtlMs,
7022
7016
  onPaired: ({ clientName, tokenCount }) => {
7023
7017
  logRuntimeEvent("Paired", `Mobile client connected${clientName ? ` from ${clientName}` : ""}; ${formatClientCount(tokenCount)} active.`);
7024
7018
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-relay",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
4
4
  "description": "Local Codex Relay CLI bridge for the Codex Relay mobile app.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {