codex-relay 1.4.5 → 1.4.6
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 +13 -19
- package/dist/src.js +9 -15
- package/package.json +1 -1
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(
|
|
138
|
-
const row = await db.prepare("SELECT COUNT(DISTINCT COALESCE(client_session_id, token_hash)) AS count FROM pairing_sessions
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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.
|
|
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.
|
|
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(
|
|
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(
|
|
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)
|
|
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)
|
|
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
|
},
|