@dev-anywhere/relay 0.6.0 → 0.6.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/assets/web/assets/{fvad-BVUkJ2pD.js → fvad-mBAhtF0y.js} +1 -1
- package/assets/web/assets/{index-CUkAA_Bz.js → index-CXT4gEaR.js} +76 -76
- package/assets/web/index.html +1 -1
- package/assets/web/sw.js +1 -1
- package/dist/{chunk-7YWKWUNZ.js → chunk-TWQN6HNC.js} +338 -7
- package/dist/chunk-TWQN6HNC.js.map +1 -0
- package/dist/handlers/client.d.ts +2 -1
- package/dist/handlers/client.d.ts.map +1 -1
- package/dist/handlers/proxy.d.ts +2 -1
- package/dist/handlers/proxy.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/session-history-route-registry.d.ts +69 -0
- package/dist/session-history-route-registry.d.ts.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-7YWKWUNZ.js.map +0 -1
package/assets/web/index.html
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
<link rel="icon" type="image/svg+xml" href="/brand-icon.svg" />
|
|
45
45
|
<link rel="icon" type="image/png" sizes="64x64" href="/pwa-64x64.png" />
|
|
46
46
|
<link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" />
|
|
47
|
-
<script type="module" crossorigin src="/assets/index-
|
|
47
|
+
<script type="module" crossorigin src="/assets/index-CXT4gEaR.js"></script>
|
|
48
48
|
<link rel="stylesheet" crossorigin href="/assets/index-Dmm_ocq_.css">
|
|
49
49
|
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
|
|
50
50
|
<body class="bg-background text-foreground">
|
package/assets/web/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let o={};const c=e=>i(e,t),f={module:{uri:t},exports:o,require:c};s[t]=Promise.all(n.map(e=>f[e]||c(e))).then(e=>(r(...e),o))}}define(["./workbox-10a3128e"],function(e){"use strict";importScripts("notification-sw.js"),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"1872c500de691dce40960bb85481de07"},{url:"notification-sw.js",revision:"ee0be62f4c1f36625b92680b3060becb"},{url:"index.html",revision:"
|
|
1
|
+
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let o={};const c=e=>i(e,t),f={module:{uri:t},exports:o,require:c};s[t]=Promise.all(n.map(e=>f[e]||c(e))).then(e=>(r(...e),o))}}define(["./workbox-10a3128e"],function(e){"use strict";importScripts("notification-sw.js"),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"1872c500de691dce40960bb85481de07"},{url:"notification-sw.js",revision:"ee0be62f4c1f36625b92680b3060becb"},{url:"index.html",revision:"aa5f2f91104adcc66cc14f85c755800f"},{url:"assets/index-Dmm_ocq_.css",revision:null},{url:"assets/index-CXT4gEaR.js",revision:null},{url:"assets/fvad-mBAhtF0y.js",revision:null},{url:"assets/fvad-DYxbW2my.wasm",revision:null},{url:"assets/__vite-browser-external-BIHI7g3E.js",revision:null},{url:"brand-icon.svg",revision:"efe8505a7b7ef12cbb3a4bcd286254bd"},{url:"maskable-icon-512x512.png",revision:"e762ff07cdedcd78f8650f59c6881727"},{url:"pwa-192x192.png",revision:"4e4603ce0f22528eded553ba7f2ffbc5"},{url:"pwa-512x512.png",revision:"e762ff07cdedcd78f8650f59c6881727"},{url:"pwa-64x64.png",revision:"d322160ad1dc08ba9e133cb3d296ae63"},{url:"manifest.webmanifest",revision:"4daa295c44052f4ce59a1fcfd6f06ab8"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/proxy(\/|$|\?)/,/^\/client(\/|$|\?)/,/^\/voice(\/|$|\?)/,/^\/fonts\//,/^\/health/,/^\/status/,/^\/api\//]})),e.registerRoute(/^\/fonts\/.*\.(woff2|woff|ttf|otf|css)$/,new e.CacheFirst({cacheName:"dev-anywhere-fonts",plugins:[new e.ExpirationPlugin({maxEntries:40,maxAgeSeconds:15552e3})]}),"GET")});
|
|
@@ -888,9 +888,9 @@ var relayControlDefinitions = [
|
|
|
888
888
|
sessionId: IdSchema.optional()
|
|
889
889
|
}, "client_to_proxy"),
|
|
890
890
|
// 会话历史浏览
|
|
891
|
-
control("session_history_request",
|
|
891
|
+
control("session_history_request", RequiredRequestIdShape, "client_to_proxy"),
|
|
892
892
|
control("session_history_response", {
|
|
893
|
-
...
|
|
893
|
+
...RequiredRequestIdShape,
|
|
894
894
|
...RequestErrorShape,
|
|
895
895
|
success: z9.boolean(),
|
|
896
896
|
sessions: z9.array(HistorySessionSchema)
|
|
@@ -1781,7 +1781,7 @@ function rejectNotRegistered(ws) {
|
|
|
1781
1781
|
})
|
|
1782
1782
|
);
|
|
1783
1783
|
}
|
|
1784
|
-
function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, remoteFileBridge) {
|
|
1784
|
+
function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, sessionHistoryRoutes, chaos, remoteFileBridge) {
|
|
1785
1785
|
const proxyWs = ws;
|
|
1786
1786
|
proxyWs.isAlive = true;
|
|
1787
1787
|
proxyWs.on("pong", () => {
|
|
@@ -1829,7 +1829,10 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1829
1829
|
const { proxyId, name } = result.message;
|
|
1830
1830
|
const status = registry.registerProxy(proxyId, proxyWs, name);
|
|
1831
1831
|
proxyWs.proxyId = proxyId;
|
|
1832
|
-
if (status === "reconnected")
|
|
1832
|
+
if (status === "reconnected") {
|
|
1833
|
+
ptySnapshotRoutes.clearProxy(proxyId);
|
|
1834
|
+
sessionHistoryRoutes.clearProxy(proxyId);
|
|
1835
|
+
}
|
|
1833
1836
|
logger.info({ proxyId, status }, "Proxy registered");
|
|
1834
1837
|
proxyWs.send(
|
|
1835
1838
|
serializeControl({
|
|
@@ -1846,6 +1849,7 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1846
1849
|
if (result.kind === "control" && result.message.type === "proxy_disconnect") {
|
|
1847
1850
|
if (proxyWs.proxyId) {
|
|
1848
1851
|
ptySnapshotRoutes.clearProxy(proxyWs.proxyId);
|
|
1852
|
+
sessionHistoryRoutes.clearProxy(proxyWs.proxyId);
|
|
1849
1853
|
notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);
|
|
1850
1854
|
registry.unregisterProxy(proxyWs.proxyId);
|
|
1851
1855
|
logger.info(
|
|
@@ -1931,6 +1935,56 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1931
1935
|
);
|
|
1932
1936
|
return;
|
|
1933
1937
|
}
|
|
1938
|
+
if (result.message.type === "session_history_response") {
|
|
1939
|
+
if (!proxyWs.proxyId) {
|
|
1940
|
+
rejectNotRegistered(proxyWs);
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
const route = sessionHistoryRoutes.resolve(
|
|
1944
|
+
proxyWs.proxyId,
|
|
1945
|
+
result.message.requestId,
|
|
1946
|
+
proxyWs
|
|
1947
|
+
);
|
|
1948
|
+
if (route.kind === "matched") {
|
|
1949
|
+
let delivered = 0;
|
|
1950
|
+
for (const target of route.targets) {
|
|
1951
|
+
if (target.clientWs.readyState !== WebSocket4.OPEN) continue;
|
|
1952
|
+
const response = serializeControl({
|
|
1953
|
+
...result.message,
|
|
1954
|
+
requestId: target.requestId
|
|
1955
|
+
});
|
|
1956
|
+
if (chaos) {
|
|
1957
|
+
chaos.send(target.clientWs, response, {
|
|
1958
|
+
direction: "proxy_to_client",
|
|
1959
|
+
type: result.message.type
|
|
1960
|
+
});
|
|
1961
|
+
} else {
|
|
1962
|
+
target.clientWs.send(response);
|
|
1963
|
+
}
|
|
1964
|
+
delivered += 1;
|
|
1965
|
+
}
|
|
1966
|
+
logger.debug(
|
|
1967
|
+
{
|
|
1968
|
+
proxyId: proxyWs.proxyId,
|
|
1969
|
+
upstreamRequestId: result.message.requestId,
|
|
1970
|
+
waiterCount: route.targets.length,
|
|
1971
|
+
delivered,
|
|
1972
|
+
success: result.message.success
|
|
1973
|
+
},
|
|
1974
|
+
"Session history response fanned out"
|
|
1975
|
+
);
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
logger.debug(
|
|
1979
|
+
{
|
|
1980
|
+
proxyId: proxyWs.proxyId,
|
|
1981
|
+
requestId: result.message.requestId,
|
|
1982
|
+
route: route.kind
|
|
1983
|
+
},
|
|
1984
|
+
"Unmatched session history response dropped"
|
|
1985
|
+
);
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1934
1988
|
if (isProxyToClientRelayControlType(result.message.type)) {
|
|
1935
1989
|
if (!proxyWs.proxyId) {
|
|
1936
1990
|
rejectNotRegistered(proxyWs);
|
|
@@ -1990,6 +2044,7 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1990
2044
|
return;
|
|
1991
2045
|
}
|
|
1992
2046
|
ptySnapshotRoutes.clearProxy(proxyWs.proxyId);
|
|
2047
|
+
sessionHistoryRoutes.clearProxy(proxyWs.proxyId);
|
|
1993
2048
|
notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);
|
|
1994
2049
|
try {
|
|
1995
2050
|
registry.transitionProxy(proxyWs.proxyId, "online", "offline");
|
|
@@ -2910,7 +2965,7 @@ function sendRemoteFileUploadUrlFailure(ws, requestId, sessionId, error, errorCo
|
|
|
2910
2965
|
})
|
|
2911
2966
|
);
|
|
2912
2967
|
}
|
|
2913
|
-
function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, chaos, voiceConfigStore, voiceProviders, remoteFileBridge, connectionInfo = {}) {
|
|
2968
|
+
function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, sessionHistoryRoutes, chaos, voiceConfigStore, voiceProviders, remoteFileBridge, connectionInfo = {}) {
|
|
2914
2969
|
const clientWs = ws;
|
|
2915
2970
|
clientWs.isAlive = true;
|
|
2916
2971
|
registry.addClientWs(clientWs, connectionInfo);
|
|
@@ -3150,6 +3205,60 @@ function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, chaos,
|
|
|
3150
3205
|
}
|
|
3151
3206
|
const proxyWs = registry.getProxy(targetProxyId);
|
|
3152
3207
|
if (proxyWs && proxyWs.readyState === WebSocket7.OPEN) {
|
|
3208
|
+
if (msg.type === "session_history_request") {
|
|
3209
|
+
const registration = sessionHistoryRoutes.register(
|
|
3210
|
+
targetProxyId,
|
|
3211
|
+
msg.requestId,
|
|
3212
|
+
clientWs,
|
|
3213
|
+
proxyWs
|
|
3214
|
+
);
|
|
3215
|
+
if (registration.kind === "duplicate") {
|
|
3216
|
+
logger.debug(
|
|
3217
|
+
{ proxyId: targetProxyId, requestId: msg.requestId },
|
|
3218
|
+
"Duplicate session history request suppressed"
|
|
3219
|
+
);
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
if (registration.kind === "joined") {
|
|
3223
|
+
logger.debug(
|
|
3224
|
+
{
|
|
3225
|
+
proxyId: targetProxyId,
|
|
3226
|
+
requestId: msg.requestId,
|
|
3227
|
+
upstreamRequestId: registration.upstreamRequestId
|
|
3228
|
+
},
|
|
3229
|
+
"Session history request joined active upstream flight"
|
|
3230
|
+
);
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
if (registration.kind !== "leader") {
|
|
3234
|
+
logger.warn(
|
|
3235
|
+
{ proxyId: targetProxyId, requestId: msg.requestId, registration },
|
|
3236
|
+
"Session history response route rejected"
|
|
3237
|
+
);
|
|
3238
|
+
clientWs.send(
|
|
3239
|
+
JSON.stringify({
|
|
3240
|
+
type: "relay_error",
|
|
3241
|
+
requestId: msg.requestId,
|
|
3242
|
+
code: RelayErrorCode.INVALID_MESSAGE,
|
|
3243
|
+
message: registration.kind === "collision" ? "Session history requestId is already in use" : "Too many pending session history requests"
|
|
3244
|
+
})
|
|
3245
|
+
);
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
const upstreamRequest = serializeControl({
|
|
3249
|
+
type: "session_history_request",
|
|
3250
|
+
requestId: registration.upstreamRequestId
|
|
3251
|
+
});
|
|
3252
|
+
if (chaos) {
|
|
3253
|
+
chaos.send(proxyWs, upstreamRequest, {
|
|
3254
|
+
direction: "client_to_proxy",
|
|
3255
|
+
type: msg.type
|
|
3256
|
+
});
|
|
3257
|
+
} else {
|
|
3258
|
+
proxyWs.send(upstreamRequest);
|
|
3259
|
+
}
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3153
3262
|
if (msg.type === "session_subscribe") {
|
|
3154
3263
|
const registration = ptySnapshotRoutes.register(
|
|
3155
3264
|
targetProxyId,
|
|
@@ -3269,6 +3378,7 @@ function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, chaos,
|
|
|
3269
3378
|
});
|
|
3270
3379
|
clientWs.on("close", (code, reason) => {
|
|
3271
3380
|
ptySnapshotRoutes.abandonSocket(clientWs);
|
|
3381
|
+
sessionHistoryRoutes.abandonSocket(clientWs);
|
|
3272
3382
|
registry.removeClientWs(clientWs);
|
|
3273
3383
|
if (clientWs.clientId) {
|
|
3274
3384
|
registry.unbindClientSocket(clientWs.clientId, clientWs);
|
|
@@ -4608,6 +4718,217 @@ var PtySnapshotRouteRegistry = class {
|
|
|
4608
4718
|
}
|
|
4609
4719
|
};
|
|
4610
4720
|
|
|
4721
|
+
// src/session-history-route-registry.ts
|
|
4722
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
4723
|
+
var DEFAULT_MAX_ENTRIES2 = 4096;
|
|
4724
|
+
var DEFAULT_MAX_PENDING_PER_CLIENT2 = 64;
|
|
4725
|
+
var DEFAULT_PENDING_TTL_MS2 = 2e4;
|
|
4726
|
+
var DEFAULT_JOIN_WINDOW_MS = 8e3;
|
|
4727
|
+
var DEFAULT_TOMBSTONE_TTL_MS2 = 6e4;
|
|
4728
|
+
function routeKey2(proxyId, requestId) {
|
|
4729
|
+
return JSON.stringify([proxyId, requestId]);
|
|
4730
|
+
}
|
|
4731
|
+
var SessionHistoryRouteRegistry = class {
|
|
4732
|
+
routes = /* @__PURE__ */ new Map();
|
|
4733
|
+
flights = /* @__PURE__ */ new Map();
|
|
4734
|
+
flightTombstones = /* @__PURE__ */ new Map();
|
|
4735
|
+
maxEntries;
|
|
4736
|
+
maxPendingPerClient;
|
|
4737
|
+
pendingTtlMs;
|
|
4738
|
+
joinWindowMs;
|
|
4739
|
+
tombstoneTtlMs;
|
|
4740
|
+
now;
|
|
4741
|
+
upstreamRequestIdFactory;
|
|
4742
|
+
upstreamRequestSequence = 0;
|
|
4743
|
+
constructor(options = {}) {
|
|
4744
|
+
this.maxEntries = Math.max(1, options.maxEntries ?? DEFAULT_MAX_ENTRIES2);
|
|
4745
|
+
this.maxPendingPerClient = Math.max(
|
|
4746
|
+
1,
|
|
4747
|
+
options.maxPendingPerClient ?? DEFAULT_MAX_PENDING_PER_CLIENT2
|
|
4748
|
+
);
|
|
4749
|
+
this.pendingTtlMs = Math.max(1, options.pendingTtlMs ?? DEFAULT_PENDING_TTL_MS2);
|
|
4750
|
+
this.joinWindowMs = Math.max(
|
|
4751
|
+
1,
|
|
4752
|
+
Math.min(options.joinWindowMs ?? DEFAULT_JOIN_WINDOW_MS, this.pendingTtlMs)
|
|
4753
|
+
);
|
|
4754
|
+
this.tombstoneTtlMs = Math.max(1, options.tombstoneTtlMs ?? DEFAULT_TOMBSTONE_TTL_MS2);
|
|
4755
|
+
this.now = options.now ?? Date.now;
|
|
4756
|
+
this.upstreamRequestIdFactory = options.upstreamRequestIdFactory ?? (() => {
|
|
4757
|
+
this.upstreamRequestSequence += 1;
|
|
4758
|
+
return `relay-history-${this.upstreamRequestSequence.toString(36)}-${nanoid4(12)}`;
|
|
4759
|
+
});
|
|
4760
|
+
}
|
|
4761
|
+
register(proxyId, requestId, clientWs, proxyWs) {
|
|
4762
|
+
const now = this.now();
|
|
4763
|
+
this.pruneExpired(now);
|
|
4764
|
+
const key = routeKey2(proxyId, requestId);
|
|
4765
|
+
const existing = this.routes.get(key);
|
|
4766
|
+
if (existing) {
|
|
4767
|
+
if (existing.state === "pending" && existing.clientWs === clientWs && existing.proxyWs === proxyWs) {
|
|
4768
|
+
return { kind: "duplicate" };
|
|
4769
|
+
}
|
|
4770
|
+
return { kind: "collision" };
|
|
4771
|
+
}
|
|
4772
|
+
let clientPendingCount = 0;
|
|
4773
|
+
for (const route of this.routes.values()) {
|
|
4774
|
+
if (route.state === "pending" && route.clientWs === clientWs) {
|
|
4775
|
+
clientPendingCount += 1;
|
|
4776
|
+
if (clientPendingCount >= this.maxPendingPerClient) {
|
|
4777
|
+
return { kind: "client_capacity_exceeded" };
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
if (this.routes.size >= this.maxEntries && !this.evictOldestRouteTombstone()) {
|
|
4782
|
+
return { kind: "capacity_exceeded" };
|
|
4783
|
+
}
|
|
4784
|
+
let flight = this.flights.get(proxyId);
|
|
4785
|
+
if (flight && flight.proxyWs !== proxyWs) {
|
|
4786
|
+
this.expireFlight(flight, now);
|
|
4787
|
+
flight = void 0;
|
|
4788
|
+
}
|
|
4789
|
+
let kind;
|
|
4790
|
+
if (!flight || flight.joinUntil <= now) {
|
|
4791
|
+
const replacement = this.createFlight(proxyId, proxyWs, now);
|
|
4792
|
+
if (flight) this.migrateFlight(flight, replacement, now);
|
|
4793
|
+
this.flights.set(proxyId, replacement);
|
|
4794
|
+
flight = replacement;
|
|
4795
|
+
kind = "leader";
|
|
4796
|
+
} else {
|
|
4797
|
+
kind = "joined";
|
|
4798
|
+
}
|
|
4799
|
+
const routeExpiresAt = now + this.pendingTtlMs;
|
|
4800
|
+
flight.requestIds.add(requestId);
|
|
4801
|
+
flight.expiresAt = Math.max(flight.expiresAt, routeExpiresAt);
|
|
4802
|
+
this.routes.set(key, {
|
|
4803
|
+
state: "pending",
|
|
4804
|
+
proxyId,
|
|
4805
|
+
proxyWs,
|
|
4806
|
+
clientWs,
|
|
4807
|
+
upstreamRequestId: flight.upstreamRequestId,
|
|
4808
|
+
expiresAt: routeExpiresAt
|
|
4809
|
+
});
|
|
4810
|
+
return { kind, upstreamRequestId: flight.upstreamRequestId };
|
|
4811
|
+
}
|
|
4812
|
+
resolve(proxyId, requestId, proxyWs) {
|
|
4813
|
+
const now = this.now();
|
|
4814
|
+
this.pruneExpired(now);
|
|
4815
|
+
const flight = this.flights.get(proxyId);
|
|
4816
|
+
if (!flight || flight.upstreamRequestId !== requestId) {
|
|
4817
|
+
return this.flightTombstones.has(routeKey2(proxyId, requestId)) ? { kind: "tombstone" } : { kind: "unmatched" };
|
|
4818
|
+
}
|
|
4819
|
+
if (flight.proxyWs !== proxyWs) return { kind: "stale_proxy" };
|
|
4820
|
+
this.flights.delete(proxyId);
|
|
4821
|
+
this.setFlightTombstone(flight, now);
|
|
4822
|
+
const targets = [];
|
|
4823
|
+
for (const externalRequestId of flight.requestIds) {
|
|
4824
|
+
const key = routeKey2(proxyId, externalRequestId);
|
|
4825
|
+
const route = this.routes.get(key);
|
|
4826
|
+
if (!route || route.state !== "pending" || route.proxyWs !== proxyWs || route.upstreamRequestId !== requestId) {
|
|
4827
|
+
continue;
|
|
4828
|
+
}
|
|
4829
|
+
this.routes.set(key, {
|
|
4830
|
+
state: "tombstone",
|
|
4831
|
+
proxyId,
|
|
4832
|
+
expiresAt: now + this.tombstoneTtlMs
|
|
4833
|
+
});
|
|
4834
|
+
targets.push({ clientWs: route.clientWs, requestId: externalRequestId });
|
|
4835
|
+
}
|
|
4836
|
+
return { kind: "matched", targets };
|
|
4837
|
+
}
|
|
4838
|
+
abandonSocket(clientWs) {
|
|
4839
|
+
const now = this.now();
|
|
4840
|
+
this.pruneExpired(now);
|
|
4841
|
+
for (const [key, route] of this.routes) {
|
|
4842
|
+
if (route.state === "pending" && route.clientWs === clientWs) {
|
|
4843
|
+
this.routes.set(key, {
|
|
4844
|
+
state: "tombstone",
|
|
4845
|
+
proxyId: route.proxyId,
|
|
4846
|
+
expiresAt: now + this.tombstoneTtlMs
|
|
4847
|
+
});
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
clearProxy(proxyId) {
|
|
4852
|
+
for (const [key, route] of this.routes) {
|
|
4853
|
+
if (route.proxyId === proxyId) this.routes.delete(key);
|
|
4854
|
+
}
|
|
4855
|
+
this.flights.delete(proxyId);
|
|
4856
|
+
for (const [key, tombstone] of this.flightTombstones) {
|
|
4857
|
+
if (tombstone.proxyId === proxyId) this.flightTombstones.delete(key);
|
|
4858
|
+
}
|
|
4859
|
+
}
|
|
4860
|
+
createFlight(proxyId, proxyWs, now) {
|
|
4861
|
+
return {
|
|
4862
|
+
proxyId,
|
|
4863
|
+
proxyWs,
|
|
4864
|
+
upstreamRequestId: this.upstreamRequestIdFactory(),
|
|
4865
|
+
requestIds: /* @__PURE__ */ new Set(),
|
|
4866
|
+
joinUntil: now + this.joinWindowMs,
|
|
4867
|
+
expiresAt: now + this.pendingTtlMs
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
migrateFlight(previous, replacement, now) {
|
|
4871
|
+
if (this.flights.get(previous.proxyId) === previous) this.flights.delete(previous.proxyId);
|
|
4872
|
+
this.setFlightTombstone(previous, now);
|
|
4873
|
+
for (const externalRequestId of previous.requestIds) {
|
|
4874
|
+
const key = routeKey2(previous.proxyId, externalRequestId);
|
|
4875
|
+
const route = this.routes.get(key);
|
|
4876
|
+
if (!route || route.state !== "pending" || route.proxyWs !== previous.proxyWs || route.upstreamRequestId !== previous.upstreamRequestId) {
|
|
4877
|
+
continue;
|
|
4878
|
+
}
|
|
4879
|
+
route.upstreamRequestId = replacement.upstreamRequestId;
|
|
4880
|
+
replacement.requestIds.add(externalRequestId);
|
|
4881
|
+
replacement.expiresAt = Math.max(replacement.expiresAt, route.expiresAt);
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
expireFlight(flight, now) {
|
|
4885
|
+
if (this.flights.get(flight.proxyId) === flight) this.flights.delete(flight.proxyId);
|
|
4886
|
+
this.setFlightTombstone(flight, now);
|
|
4887
|
+
for (const externalRequestId of flight.requestIds) {
|
|
4888
|
+
const key = routeKey2(flight.proxyId, externalRequestId);
|
|
4889
|
+
const route = this.routes.get(key);
|
|
4890
|
+
if (route?.state === "pending" && route.upstreamRequestId === flight.upstreamRequestId) {
|
|
4891
|
+
this.routes.delete(key);
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
}
|
|
4895
|
+
setFlightTombstone(flight, now) {
|
|
4896
|
+
const key = routeKey2(flight.proxyId, flight.upstreamRequestId);
|
|
4897
|
+
if (!this.flightTombstones.has(key) && this.flightTombstones.size >= this.maxEntries) {
|
|
4898
|
+
const oldest = this.flightTombstones.keys().next().value;
|
|
4899
|
+
if (oldest !== void 0) this.flightTombstones.delete(oldest);
|
|
4900
|
+
}
|
|
4901
|
+
this.flightTombstones.set(key, {
|
|
4902
|
+
proxyId: flight.proxyId,
|
|
4903
|
+
expiresAt: now + this.tombstoneTtlMs
|
|
4904
|
+
});
|
|
4905
|
+
}
|
|
4906
|
+
evictOldestRouteTombstone() {
|
|
4907
|
+
let oldestTombstoneKey;
|
|
4908
|
+
let oldestTombstoneExpiry = Number.POSITIVE_INFINITY;
|
|
4909
|
+
for (const [candidateKey, route] of this.routes) {
|
|
4910
|
+
if (route.state === "tombstone" && route.expiresAt < oldestTombstoneExpiry) {
|
|
4911
|
+
oldestTombstoneKey = candidateKey;
|
|
4912
|
+
oldestTombstoneExpiry = route.expiresAt;
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
if (!oldestTombstoneKey) return false;
|
|
4916
|
+
this.routes.delete(oldestTombstoneKey);
|
|
4917
|
+
return true;
|
|
4918
|
+
}
|
|
4919
|
+
pruneExpired(now) {
|
|
4920
|
+
for (const flight of this.flights.values()) {
|
|
4921
|
+
if (flight.expiresAt <= now) this.expireFlight(flight, now);
|
|
4922
|
+
}
|
|
4923
|
+
for (const [key, route] of this.routes) {
|
|
4924
|
+
if (route.expiresAt <= now) this.routes.delete(key);
|
|
4925
|
+
}
|
|
4926
|
+
for (const [key, tombstone] of this.flightTombstones) {
|
|
4927
|
+
if (tombstone.expiresAt <= now) this.flightTombstones.delete(key);
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
};
|
|
4931
|
+
|
|
4611
4932
|
// src/server.ts
|
|
4612
4933
|
var MODULE_DIR = dirname3(fileURLToPath2(import.meta.url));
|
|
4613
4934
|
var PACKAGED_FONTS_DIR = resolve(MODULE_DIR, "../assets/fonts");
|
|
@@ -4653,6 +4974,7 @@ function createRelayServer(options) {
|
|
|
4653
4974
|
}
|
|
4654
4975
|
const registry = new RelayRegistry();
|
|
4655
4976
|
const ptySnapshotRoutes = new PtySnapshotRouteRegistry();
|
|
4977
|
+
const sessionHistoryRoutes = new SessionHistoryRouteRegistry();
|
|
4656
4978
|
const remoteFileBridge = new RemoteFileBridge({ registry, logger });
|
|
4657
4979
|
const voiceConfigStore = createVoiceConfigStore({
|
|
4658
4980
|
dataDir,
|
|
@@ -4796,7 +5118,15 @@ function createRelayServer(options) {
|
|
|
4796
5118
|
socket.destroy();
|
|
4797
5119
|
});
|
|
4798
5120
|
proxyWss.on("connection", (ws) => {
|
|
4799
|
-
handleProxyConnection(
|
|
5121
|
+
handleProxyConnection(
|
|
5122
|
+
ws,
|
|
5123
|
+
registry,
|
|
5124
|
+
logger,
|
|
5125
|
+
ptySnapshotRoutes,
|
|
5126
|
+
sessionHistoryRoutes,
|
|
5127
|
+
relayChaos,
|
|
5128
|
+
remoteFileBridge
|
|
5129
|
+
);
|
|
4800
5130
|
});
|
|
4801
5131
|
clientWss.on("connection", (ws, request) => {
|
|
4802
5132
|
handleClientConnection(
|
|
@@ -4804,6 +5134,7 @@ function createRelayServer(options) {
|
|
|
4804
5134
|
registry,
|
|
4805
5135
|
logger,
|
|
4806
5136
|
ptySnapshotRoutes,
|
|
5137
|
+
sessionHistoryRoutes,
|
|
4807
5138
|
relayChaos,
|
|
4808
5139
|
voiceConfigStore,
|
|
4809
5140
|
voiceProviders,
|
|
@@ -4899,4 +5230,4 @@ export {
|
|
|
4899
5230
|
parseRelayChaosFromEnv,
|
|
4900
5231
|
createRelayServer
|
|
4901
5232
|
};
|
|
4902
|
-
//# sourceMappingURL=chunk-
|
|
5233
|
+
//# sourceMappingURL=chunk-TWQN6HNC.js.map
|