@dev-anywhere/relay 0.5.47 → 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-B51vkASP.js → fvad-mBAhtF0y.js} +1 -1
- package/assets/web/assets/{index-BCWFUjda.js → index-CXT4gEaR.js} +166 -166
- package/assets/web/assets/{index-mGSY150B.css → index-Dmm_ocq_.css} +1 -1
- package/assets/web/index.html +2 -2
- package/assets/web/sw.js +1 -1
- package/dist/{chunk-4Q4EAZP6.js → chunk-TWQN6HNC.js} +343 -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-4Q4EAZP6.js.map +0 -1
package/assets/web/index.html
CHANGED
|
@@ -44,8 +44,8 @@
|
|
|
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-
|
|
48
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
47
|
+
<script type="module" crossorigin src="/assets/index-CXT4gEaR.js"></script>
|
|
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">
|
|
51
51
|
<div id="root"></div>
|
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
|
|
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,8 +888,13 @@ var relayControlDefinitions = [
|
|
|
888
888
|
sessionId: IdSchema.optional()
|
|
889
889
|
}, "client_to_proxy"),
|
|
890
890
|
// 会话历史浏览
|
|
891
|
-
control("session_history_request",
|
|
892
|
-
control("session_history_response", {
|
|
891
|
+
control("session_history_request", RequiredRequestIdShape, "client_to_proxy"),
|
|
892
|
+
control("session_history_response", {
|
|
893
|
+
...RequiredRequestIdShape,
|
|
894
|
+
...RequestErrorShape,
|
|
895
|
+
success: z9.boolean(),
|
|
896
|
+
sessions: z9.array(HistorySessionSchema)
|
|
897
|
+
}, "proxy_to_client"),
|
|
893
898
|
// PTY 语义状态,从 Envelope 迁移到 Control 层
|
|
894
899
|
control("pty_state", { sessionId: IdSchema, payload: PtyStatePayloadSchema }, "proxy_to_client"),
|
|
895
900
|
// Provider 语义状态,来自 Claude/Codex hook 等结构化事件,不从 PTY 字节推断
|
|
@@ -1776,7 +1781,7 @@ function rejectNotRegistered(ws) {
|
|
|
1776
1781
|
})
|
|
1777
1782
|
);
|
|
1778
1783
|
}
|
|
1779
|
-
function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, remoteFileBridge) {
|
|
1784
|
+
function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, sessionHistoryRoutes, chaos, remoteFileBridge) {
|
|
1780
1785
|
const proxyWs = ws;
|
|
1781
1786
|
proxyWs.isAlive = true;
|
|
1782
1787
|
proxyWs.on("pong", () => {
|
|
@@ -1824,7 +1829,10 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1824
1829
|
const { proxyId, name } = result.message;
|
|
1825
1830
|
const status = registry.registerProxy(proxyId, proxyWs, name);
|
|
1826
1831
|
proxyWs.proxyId = proxyId;
|
|
1827
|
-
if (status === "reconnected")
|
|
1832
|
+
if (status === "reconnected") {
|
|
1833
|
+
ptySnapshotRoutes.clearProxy(proxyId);
|
|
1834
|
+
sessionHistoryRoutes.clearProxy(proxyId);
|
|
1835
|
+
}
|
|
1828
1836
|
logger.info({ proxyId, status }, "Proxy registered");
|
|
1829
1837
|
proxyWs.send(
|
|
1830
1838
|
serializeControl({
|
|
@@ -1841,6 +1849,7 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1841
1849
|
if (result.kind === "control" && result.message.type === "proxy_disconnect") {
|
|
1842
1850
|
if (proxyWs.proxyId) {
|
|
1843
1851
|
ptySnapshotRoutes.clearProxy(proxyWs.proxyId);
|
|
1852
|
+
sessionHistoryRoutes.clearProxy(proxyWs.proxyId);
|
|
1844
1853
|
notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);
|
|
1845
1854
|
registry.unregisterProxy(proxyWs.proxyId);
|
|
1846
1855
|
logger.info(
|
|
@@ -1926,6 +1935,56 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1926
1935
|
);
|
|
1927
1936
|
return;
|
|
1928
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
|
+
}
|
|
1929
1988
|
if (isProxyToClientRelayControlType(result.message.type)) {
|
|
1930
1989
|
if (!proxyWs.proxyId) {
|
|
1931
1990
|
rejectNotRegistered(proxyWs);
|
|
@@ -1985,6 +2044,7 @@ function handleProxyConnection(ws, registry, logger, ptySnapshotRoutes, chaos, r
|
|
|
1985
2044
|
return;
|
|
1986
2045
|
}
|
|
1987
2046
|
ptySnapshotRoutes.clearProxy(proxyWs.proxyId);
|
|
2047
|
+
sessionHistoryRoutes.clearProxy(proxyWs.proxyId);
|
|
1988
2048
|
notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);
|
|
1989
2049
|
try {
|
|
1990
2050
|
registry.transitionProxy(proxyWs.proxyId, "online", "offline");
|
|
@@ -2905,7 +2965,7 @@ function sendRemoteFileUploadUrlFailure(ws, requestId, sessionId, error, errorCo
|
|
|
2905
2965
|
})
|
|
2906
2966
|
);
|
|
2907
2967
|
}
|
|
2908
|
-
function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, chaos, voiceConfigStore, voiceProviders, remoteFileBridge, connectionInfo = {}) {
|
|
2968
|
+
function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, sessionHistoryRoutes, chaos, voiceConfigStore, voiceProviders, remoteFileBridge, connectionInfo = {}) {
|
|
2909
2969
|
const clientWs = ws;
|
|
2910
2970
|
clientWs.isAlive = true;
|
|
2911
2971
|
registry.addClientWs(clientWs, connectionInfo);
|
|
@@ -3145,6 +3205,60 @@ function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, chaos,
|
|
|
3145
3205
|
}
|
|
3146
3206
|
const proxyWs = registry.getProxy(targetProxyId);
|
|
3147
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
|
+
}
|
|
3148
3262
|
if (msg.type === "session_subscribe") {
|
|
3149
3263
|
const registration = ptySnapshotRoutes.register(
|
|
3150
3264
|
targetProxyId,
|
|
@@ -3264,6 +3378,7 @@ function handleClientConnection(ws, registry, logger, ptySnapshotRoutes, chaos,
|
|
|
3264
3378
|
});
|
|
3265
3379
|
clientWs.on("close", (code, reason) => {
|
|
3266
3380
|
ptySnapshotRoutes.abandonSocket(clientWs);
|
|
3381
|
+
sessionHistoryRoutes.abandonSocket(clientWs);
|
|
3267
3382
|
registry.removeClientWs(clientWs);
|
|
3268
3383
|
if (clientWs.clientId) {
|
|
3269
3384
|
registry.unbindClientSocket(clientWs.clientId, clientWs);
|
|
@@ -4603,6 +4718,217 @@ var PtySnapshotRouteRegistry = class {
|
|
|
4603
4718
|
}
|
|
4604
4719
|
};
|
|
4605
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
|
+
|
|
4606
4932
|
// src/server.ts
|
|
4607
4933
|
var MODULE_DIR = dirname3(fileURLToPath2(import.meta.url));
|
|
4608
4934
|
var PACKAGED_FONTS_DIR = resolve(MODULE_DIR, "../assets/fonts");
|
|
@@ -4648,6 +4974,7 @@ function createRelayServer(options) {
|
|
|
4648
4974
|
}
|
|
4649
4975
|
const registry = new RelayRegistry();
|
|
4650
4976
|
const ptySnapshotRoutes = new PtySnapshotRouteRegistry();
|
|
4977
|
+
const sessionHistoryRoutes = new SessionHistoryRouteRegistry();
|
|
4651
4978
|
const remoteFileBridge = new RemoteFileBridge({ registry, logger });
|
|
4652
4979
|
const voiceConfigStore = createVoiceConfigStore({
|
|
4653
4980
|
dataDir,
|
|
@@ -4791,7 +5118,15 @@ function createRelayServer(options) {
|
|
|
4791
5118
|
socket.destroy();
|
|
4792
5119
|
});
|
|
4793
5120
|
proxyWss.on("connection", (ws) => {
|
|
4794
|
-
handleProxyConnection(
|
|
5121
|
+
handleProxyConnection(
|
|
5122
|
+
ws,
|
|
5123
|
+
registry,
|
|
5124
|
+
logger,
|
|
5125
|
+
ptySnapshotRoutes,
|
|
5126
|
+
sessionHistoryRoutes,
|
|
5127
|
+
relayChaos,
|
|
5128
|
+
remoteFileBridge
|
|
5129
|
+
);
|
|
4795
5130
|
});
|
|
4796
5131
|
clientWss.on("connection", (ws, request) => {
|
|
4797
5132
|
handleClientConnection(
|
|
@@ -4799,6 +5134,7 @@ function createRelayServer(options) {
|
|
|
4799
5134
|
registry,
|
|
4800
5135
|
logger,
|
|
4801
5136
|
ptySnapshotRoutes,
|
|
5137
|
+
sessionHistoryRoutes,
|
|
4802
5138
|
relayChaos,
|
|
4803
5139
|
voiceConfigStore,
|
|
4804
5140
|
voiceProviders,
|
|
@@ -4894,4 +5230,4 @@ export {
|
|
|
4894
5230
|
parseRelayChaosFromEnv,
|
|
4895
5231
|
createRelayServer
|
|
4896
5232
|
};
|
|
4897
|
-
//# sourceMappingURL=chunk-
|
|
5233
|
+
//# sourceMappingURL=chunk-TWQN6HNC.js.map
|