rnxsim 0.1.427 → 0.1.429
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/cli/commands/control.ts +17 -1
- package/cli/commands/flow.ts +5 -9
- package/cli/commands/inspect/core.ts +1 -0
- package/cli/outbound-endpoints.ts +5 -2
- package/cli/self-invocation.ts +24 -0
- package/detox/index.ts +10 -36
- package/dist-lib/agent-daemon-client.cjs +48 -24
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +100 -76
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +2 -25
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +324 -218
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +162 -78
- package/dist-lib/index.cjs +1 -1
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +43 -24
- package/dist-lib/metro-production-bundle.mjs +43 -24
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +452 -336
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/src/host/websocket-proxy.ts +197 -92
- package/src/metro-production-bundle.ts +55 -30
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.429 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -104,9 +104,9 @@ __export(bridge_host_exports, {
|
|
|
104
104
|
module.exports = __toCommonJS(bridge_host_exports);
|
|
105
105
|
var import_child_process3 = require("child_process");
|
|
106
106
|
var import_fs2 = __toESM(require("fs"), 1);
|
|
107
|
-
var
|
|
107
|
+
var import_http4 = require("http");
|
|
108
108
|
var import_path2 = __toESM(require("path"), 1);
|
|
109
|
-
var
|
|
109
|
+
var import_ws = require("ws");
|
|
110
110
|
|
|
111
111
|
// scripts/dev-server-scanner.ts
|
|
112
112
|
var import_child_process = require("child_process");
|
|
@@ -2054,23 +2054,47 @@ var RNX_RUNTIME_PRODUCT = {
|
|
|
2054
2054
|
var rnxRuntime = createRuntimeDelivery(RNX_RUNTIME_PRODUCT);
|
|
2055
2055
|
|
|
2056
2056
|
// src/host/agent-host.ts
|
|
2057
|
-
var
|
|
2058
|
-
var
|
|
2057
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
2058
|
+
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
2059
2059
|
|
|
2060
2060
|
// src/agent-sessions.ts
|
|
2061
2061
|
var import_node_child_process2 = require("node:child_process");
|
|
2062
2062
|
var import_node_crypto4 = require("node:crypto");
|
|
2063
|
-
var
|
|
2064
|
-
var
|
|
2063
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
2064
|
+
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
2065
2065
|
var import_node_readline = __toESM(require("node:readline"), 1);
|
|
2066
2066
|
|
|
2067
|
+
// cli/self-invocation.ts
|
|
2068
|
+
var import_node_fs7 = require("node:fs");
|
|
2069
|
+
var import_node_path7 = require("node:path");
|
|
2070
|
+
var import_node_url3 = require("node:url");
|
|
2071
|
+
|
|
2067
2072
|
// cli/standalone.ts
|
|
2068
2073
|
var IS_STANDALONE = typeof __SOOTSIM_STANDALONE__ !== "undefined" && __SOOTSIM_STANDALONE__;
|
|
2069
2074
|
|
|
2070
2075
|
// cli/self-invocation.ts
|
|
2076
|
+
function sourceEntryPath() {
|
|
2077
|
+
try {
|
|
2078
|
+
const candidate = (0, import_node_path7.join)((0, import_node_path7.dirname)((0, import_node_url3.fileURLToPath)(__sootsim_import_meta_url)), "bin.ts");
|
|
2079
|
+
return (0, import_node_fs7.existsSync)(candidate) ? candidate : null;
|
|
2080
|
+
} catch {
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
function isCliEntry(path12) {
|
|
2085
|
+
const name2 = (0, import_node_path7.basename)(path12);
|
|
2086
|
+
return name2 === "bin.ts" || name2 === "bin.js";
|
|
2087
|
+
}
|
|
2071
2088
|
function rnxSelfInvocation() {
|
|
2072
2089
|
if (IS_STANDALONE) return { executable: process.execPath, prefixArgs: [] };
|
|
2073
2090
|
const entry = process.argv[1];
|
|
2091
|
+
if (entry && isCliEntry(entry)) {
|
|
2092
|
+
return { executable: process.execPath, prefixArgs: [entry] };
|
|
2093
|
+
}
|
|
2094
|
+
const sourceEntry = sourceEntryPath();
|
|
2095
|
+
if (sourceEntry) {
|
|
2096
|
+
return { executable: process.execPath, prefixArgs: [sourceEntry] };
|
|
2097
|
+
}
|
|
2074
2098
|
if (!entry) {
|
|
2075
2099
|
throw new Error(`could not locate the ${rnxPublicBrand.commandName} entry script`);
|
|
2076
2100
|
}
|
|
@@ -2118,8 +2142,8 @@ function encodeAgentPromptEnvelope(input) {
|
|
|
2118
2142
|
|
|
2119
2143
|
// src/attached-projects.ts
|
|
2120
2144
|
var import_node_crypto3 = require("node:crypto");
|
|
2121
|
-
var
|
|
2122
|
-
var
|
|
2145
|
+
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
2146
|
+
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
2123
2147
|
var overrideDir = null;
|
|
2124
2148
|
function userDataDir() {
|
|
2125
2149
|
if (overrideDir) return overrideDir;
|
|
@@ -2131,7 +2155,7 @@ function getUserDataDir() {
|
|
|
2131
2155
|
return userDataDir();
|
|
2132
2156
|
}
|
|
2133
2157
|
function storeFile() {
|
|
2134
|
-
return
|
|
2158
|
+
return import_node_path8.default.join(userDataDir(), "attached-projects.json");
|
|
2135
2159
|
}
|
|
2136
2160
|
function cloneEmpty() {
|
|
2137
2161
|
return {
|
|
@@ -2145,7 +2169,7 @@ function loadStore() {
|
|
|
2145
2169
|
const file = storeFile();
|
|
2146
2170
|
let raw;
|
|
2147
2171
|
try {
|
|
2148
|
-
raw =
|
|
2172
|
+
raw = import_node_fs8.default.readFileSync(file, "utf8");
|
|
2149
2173
|
} catch (err) {
|
|
2150
2174
|
if (err.code === "ENOENT") return cloneEmpty();
|
|
2151
2175
|
throw err;
|
|
@@ -2162,7 +2186,7 @@ function loadStore() {
|
|
|
2162
2186
|
} catch (err) {
|
|
2163
2187
|
const quarantine = `${file}.corrupt-${Date.now()}`;
|
|
2164
2188
|
try {
|
|
2165
|
-
|
|
2189
|
+
import_node_fs8.default.renameSync(file, quarantine);
|
|
2166
2190
|
console.warn(
|
|
2167
2191
|
`[sootsim] attached-projects.json was unparseable; quarantined to ${quarantine}. original error: ${err.message}`
|
|
2168
2192
|
);
|
|
@@ -2173,16 +2197,16 @@ function loadStore() {
|
|
|
2173
2197
|
}
|
|
2174
2198
|
function writeStore(store) {
|
|
2175
2199
|
const file = storeFile();
|
|
2176
|
-
|
|
2200
|
+
import_node_fs8.default.mkdirSync(import_node_path8.default.dirname(file), { recursive: true });
|
|
2177
2201
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
2178
|
-
const fd =
|
|
2202
|
+
const fd = import_node_fs8.default.openSync(tmp, "w", 384);
|
|
2179
2203
|
try {
|
|
2180
|
-
|
|
2181
|
-
|
|
2204
|
+
import_node_fs8.default.writeFileSync(fd, JSON.stringify(store, null, 2));
|
|
2205
|
+
import_node_fs8.default.fsyncSync(fd);
|
|
2182
2206
|
} finally {
|
|
2183
|
-
|
|
2207
|
+
import_node_fs8.default.closeSync(fd);
|
|
2184
2208
|
}
|
|
2185
|
-
|
|
2209
|
+
import_node_fs8.default.renameSync(tmp, file);
|
|
2186
2210
|
}
|
|
2187
2211
|
function mutateStore(fn) {
|
|
2188
2212
|
const store = loadStore();
|
|
@@ -2191,13 +2215,13 @@ function mutateStore(fn) {
|
|
|
2191
2215
|
return store;
|
|
2192
2216
|
}
|
|
2193
2217
|
function projectIdForCwd(cwd) {
|
|
2194
|
-
return (0, import_node_crypto3.createHash)("sha256").update(
|
|
2218
|
+
return (0, import_node_crypto3.createHash)("sha256").update(import_node_path8.default.resolve(cwd)).digest("hex").slice(0, 16);
|
|
2195
2219
|
}
|
|
2196
2220
|
function newSessionId() {
|
|
2197
2221
|
return `s_${(0, import_node_crypto3.randomBytes)(10).toString("hex")}`;
|
|
2198
2222
|
}
|
|
2199
2223
|
function upsertProject(input) {
|
|
2200
|
-
const cwd =
|
|
2224
|
+
const cwd = import_node_path8.default.resolve(input.cwd);
|
|
2201
2225
|
const id = projectIdForCwd(cwd);
|
|
2202
2226
|
let result;
|
|
2203
2227
|
mutateStore((store) => {
|
|
@@ -2223,7 +2247,7 @@ function upsertProject(input) {
|
|
|
2223
2247
|
const now = Date.now();
|
|
2224
2248
|
const created = {
|
|
2225
2249
|
id,
|
|
2226
|
-
name: input.name ??
|
|
2250
|
+
name: input.name ?? import_node_path8.default.basename(cwd),
|
|
2227
2251
|
cwd,
|
|
2228
2252
|
repoRoot: input.repoRoot,
|
|
2229
2253
|
sourceRoots: input.sourceRoots ?? [cwd],
|
|
@@ -2369,8 +2393,8 @@ async function seedFromDemoAppRegistry() {
|
|
|
2369
2393
|
if (apps.length === 0) return;
|
|
2370
2394
|
mutateStore((store) => {
|
|
2371
2395
|
for (const app of apps) {
|
|
2372
|
-
if (!
|
|
2373
|
-
const cwd =
|
|
2396
|
+
if (!import_node_fs8.default.existsSync(app.dir)) continue;
|
|
2397
|
+
const cwd = import_node_path8.default.resolve(app.dir);
|
|
2374
2398
|
const id = projectIdForCwd(cwd);
|
|
2375
2399
|
if (store.attachedProjects.some((p) => p.id === id)) continue;
|
|
2376
2400
|
const now = Date.now();
|
|
@@ -2394,16 +2418,16 @@ async function seedFromDemoAppRegistry() {
|
|
|
2394
2418
|
|
|
2395
2419
|
// src/agent-sessions.ts
|
|
2396
2420
|
function sessionDir(sessionId) {
|
|
2397
|
-
return
|
|
2421
|
+
return import_node_path9.default.join(getUserDataDir(), "sessions", sessionId);
|
|
2398
2422
|
}
|
|
2399
2423
|
function promptFifoPath(sessionId) {
|
|
2400
|
-
return
|
|
2424
|
+
return import_node_path9.default.join(sessionDir(sessionId), "prompt.in");
|
|
2401
2425
|
}
|
|
2402
2426
|
function eventsFifoPath(sessionId) {
|
|
2403
|
-
return
|
|
2427
|
+
return import_node_path9.default.join(sessionDir(sessionId), "events.out");
|
|
2404
2428
|
}
|
|
2405
2429
|
function transcriptPath(sessionId) {
|
|
2406
|
-
return
|
|
2430
|
+
return import_node_path9.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
|
|
2407
2431
|
}
|
|
2408
2432
|
function pidIsAlive(pid, sessionId) {
|
|
2409
2433
|
if (!pid) return false;
|
|
@@ -2413,7 +2437,7 @@ function pidIsAlive(pid, sessionId) {
|
|
|
2413
2437
|
return false;
|
|
2414
2438
|
}
|
|
2415
2439
|
if (sessionId) {
|
|
2416
|
-
if (!
|
|
2440
|
+
if (!import_node_fs9.default.existsSync(sessionDir(sessionId))) return false;
|
|
2417
2441
|
}
|
|
2418
2442
|
return true;
|
|
2419
2443
|
}
|
|
@@ -2426,11 +2450,11 @@ function resolveSootsimInvocation() {
|
|
|
2426
2450
|
const resourcesPath = process.resourcesPath;
|
|
2427
2451
|
if (resourcesPath) {
|
|
2428
2452
|
const candidates = [
|
|
2429
|
-
|
|
2430
|
-
|
|
2453
|
+
import_node_path9.default.join(resourcesPath, "bin", "sootsim"),
|
|
2454
|
+
import_node_path9.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
|
|
2431
2455
|
];
|
|
2432
2456
|
for (const c of candidates) {
|
|
2433
|
-
if (
|
|
2457
|
+
if (import_node_fs9.default.existsSync(c)) return { executable: c, prefixArgs: [] };
|
|
2434
2458
|
}
|
|
2435
2459
|
}
|
|
2436
2460
|
}
|
|
@@ -2449,15 +2473,15 @@ function tryWorkspaceSootsim() {
|
|
|
2449
2473
|
const sootsimDir = resolveSootsimPackageDir();
|
|
2450
2474
|
if (!sootsimDir) return null;
|
|
2451
2475
|
const binaryName = `rnx-${process.platform === "win32" ? "windows" : process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`;
|
|
2452
|
-
const distBinary =
|
|
2453
|
-
if (
|
|
2454
|
-
const distBin =
|
|
2455
|
-
if (
|
|
2476
|
+
const distBinary = import_node_path9.default.join(sootsimDir, "dist-bin", binaryName);
|
|
2477
|
+
if (import_node_fs9.default.existsSync(distBinary)) return { executable: distBinary, prefixArgs: [] };
|
|
2478
|
+
const distBin = import_node_path9.default.join(sootsimDir, "dist-cli", "bin.js");
|
|
2479
|
+
if (import_node_fs9.default.existsSync(distBin)) {
|
|
2456
2480
|
try {
|
|
2457
|
-
const src =
|
|
2458
|
-
if (
|
|
2459
|
-
const srcMtime =
|
|
2460
|
-
const buildMtime =
|
|
2481
|
+
const src = import_node_path9.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
|
|
2482
|
+
if (import_node_fs9.default.existsSync(src)) {
|
|
2483
|
+
const srcMtime = import_node_fs9.default.statSync(src).mtimeMs;
|
|
2484
|
+
const buildMtime = import_node_fs9.default.statSync(distBin).mtimeMs;
|
|
2461
2485
|
if (buildMtime < srcMtime) {
|
|
2462
2486
|
console.warn(
|
|
2463
2487
|
`[rnx] dist-cli/bin.js is older than agent-wrapper.ts \u2014 rebuild with \`bun run --cwd packages/sootsim build:cli\`.`
|
|
@@ -2476,22 +2500,22 @@ function tryWorkspaceSootsim() {
|
|
|
2476
2500
|
function resolveSootsimPackageDir() {
|
|
2477
2501
|
try {
|
|
2478
2502
|
const resolved = require.resolve(`${rnxPublicBrand.packageName}/package.json`);
|
|
2479
|
-
return
|
|
2503
|
+
return import_node_path9.default.dirname(resolved);
|
|
2480
2504
|
} catch {
|
|
2481
2505
|
}
|
|
2482
2506
|
const here = fileFromImportMeta();
|
|
2483
2507
|
if (!here) return null;
|
|
2484
|
-
let cur =
|
|
2508
|
+
let cur = import_node_path9.default.dirname(here);
|
|
2485
2509
|
for (let i = 0; i < 8; i++) {
|
|
2486
|
-
const pkg =
|
|
2510
|
+
const pkg = import_node_path9.default.join(cur, "package.json");
|
|
2487
2511
|
try {
|
|
2488
|
-
if (
|
|
2489
|
-
const parsed = JSON.parse(
|
|
2512
|
+
if (import_node_fs9.default.existsSync(pkg)) {
|
|
2513
|
+
const parsed = JSON.parse(import_node_fs9.default.readFileSync(pkg, "utf8"));
|
|
2490
2514
|
if (parsed.name === rnxPublicBrand.packageName) return cur;
|
|
2491
2515
|
}
|
|
2492
2516
|
} catch {
|
|
2493
2517
|
}
|
|
2494
|
-
const parent =
|
|
2518
|
+
const parent = import_node_path9.default.dirname(cur);
|
|
2495
2519
|
if (parent === cur) break;
|
|
2496
2520
|
cur = parent;
|
|
2497
2521
|
}
|
|
@@ -2507,28 +2531,28 @@ function fileFromImportMeta() {
|
|
|
2507
2531
|
}
|
|
2508
2532
|
}
|
|
2509
2533
|
async function withStartLock(projectId, provider, fn) {
|
|
2510
|
-
const lockDir =
|
|
2511
|
-
|
|
2534
|
+
const lockDir = import_node_path9.default.join(getUserDataDir(), "locks");
|
|
2535
|
+
import_node_fs9.default.mkdirSync(lockDir, { recursive: true });
|
|
2512
2536
|
try {
|
|
2513
|
-
|
|
2537
|
+
import_node_fs9.default.chmodSync(lockDir, 448);
|
|
2514
2538
|
} catch {
|
|
2515
2539
|
}
|
|
2516
|
-
const lockPath =
|
|
2540
|
+
const lockPath = import_node_path9.default.join(lockDir, `start-${projectId}-${provider}.lock`);
|
|
2517
2541
|
const deadline = Date.now() + 4e3;
|
|
2518
2542
|
let fd = null;
|
|
2519
2543
|
while (fd === null) {
|
|
2520
2544
|
try {
|
|
2521
|
-
fd =
|
|
2545
|
+
fd = import_node_fs9.default.openSync(
|
|
2522
2546
|
lockPath,
|
|
2523
|
-
|
|
2547
|
+
import_node_fs9.constants.O_WRONLY | import_node_fs9.constants.O_CREAT | import_node_fs9.constants.O_EXCL,
|
|
2524
2548
|
384
|
|
2525
2549
|
);
|
|
2526
2550
|
} catch (err) {
|
|
2527
2551
|
if (err.code !== "EEXIST") throw err;
|
|
2528
2552
|
try {
|
|
2529
|
-
const stale = Number(
|
|
2553
|
+
const stale = Number(import_node_fs9.default.readFileSync(lockPath, "utf8").trim());
|
|
2530
2554
|
if (stale && !isProcessAlive(stale)) {
|
|
2531
|
-
|
|
2555
|
+
import_node_fs9.default.unlinkSync(lockPath);
|
|
2532
2556
|
continue;
|
|
2533
2557
|
}
|
|
2534
2558
|
} catch {
|
|
@@ -2542,15 +2566,15 @@ async function withStartLock(projectId, provider, fn) {
|
|
|
2542
2566
|
}
|
|
2543
2567
|
}
|
|
2544
2568
|
try {
|
|
2545
|
-
|
|
2569
|
+
import_node_fs9.default.writeFileSync(fd, String(process.pid));
|
|
2546
2570
|
return await fn();
|
|
2547
2571
|
} finally {
|
|
2548
2572
|
try {
|
|
2549
|
-
|
|
2573
|
+
import_node_fs9.default.closeSync(fd);
|
|
2550
2574
|
} catch {
|
|
2551
2575
|
}
|
|
2552
2576
|
try {
|
|
2553
|
-
|
|
2577
|
+
import_node_fs9.default.unlinkSync(lockPath);
|
|
2554
2578
|
} catch {
|
|
2555
2579
|
}
|
|
2556
2580
|
}
|
|
@@ -2564,25 +2588,25 @@ function isProcessAlive(pid) {
|
|
|
2564
2588
|
}
|
|
2565
2589
|
}
|
|
2566
2590
|
function mkfifoSync(p) {
|
|
2567
|
-
const parent =
|
|
2568
|
-
|
|
2591
|
+
const parent = import_node_path9.default.dirname(p);
|
|
2592
|
+
import_node_fs9.default.mkdirSync(parent, { recursive: true });
|
|
2569
2593
|
try {
|
|
2570
|
-
|
|
2594
|
+
import_node_fs9.default.chmodSync(parent, 448);
|
|
2571
2595
|
} catch {
|
|
2572
2596
|
}
|
|
2573
|
-
if (
|
|
2597
|
+
if (import_node_fs9.default.existsSync(p)) {
|
|
2574
2598
|
try {
|
|
2575
|
-
const stat2 =
|
|
2599
|
+
const stat2 = import_node_fs9.default.statSync(p);
|
|
2576
2600
|
if (stat2.isFIFO()) {
|
|
2577
2601
|
try {
|
|
2578
|
-
|
|
2602
|
+
import_node_fs9.default.chmodSync(p, 384);
|
|
2579
2603
|
} catch {
|
|
2580
2604
|
}
|
|
2581
2605
|
return;
|
|
2582
2606
|
}
|
|
2583
|
-
|
|
2607
|
+
import_node_fs9.default.unlinkSync(p);
|
|
2584
2608
|
} catch {
|
|
2585
|
-
|
|
2609
|
+
import_node_fs9.default.unlinkSync(p);
|
|
2586
2610
|
}
|
|
2587
2611
|
}
|
|
2588
2612
|
const result = (0, import_node_child_process2.spawnSync)("mkfifo", ["-m", "600", p]);
|
|
@@ -2629,10 +2653,10 @@ async function startSession(opts) {
|
|
|
2629
2653
|
const transcript = transcriptPath(session.id);
|
|
2630
2654
|
mkfifoSync(promptIn);
|
|
2631
2655
|
mkfifoSync(eventsOut);
|
|
2632
|
-
const transcriptDir =
|
|
2633
|
-
|
|
2656
|
+
const transcriptDir = import_node_path9.default.dirname(transcript);
|
|
2657
|
+
import_node_fs9.default.mkdirSync(transcriptDir, { recursive: true });
|
|
2634
2658
|
try {
|
|
2635
|
-
|
|
2659
|
+
import_node_fs9.default.chmodSync(transcriptDir, 448);
|
|
2636
2660
|
} catch {
|
|
2637
2661
|
}
|
|
2638
2662
|
const { executable, prefixArgs } = resolveSootsimInvocation();
|
|
@@ -2683,7 +2707,7 @@ async function startSession(opts) {
|
|
|
2683
2707
|
}
|
|
2684
2708
|
}
|
|
2685
2709
|
try {
|
|
2686
|
-
|
|
2710
|
+
import_node_fs9.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
|
|
2687
2711
|
} catch {
|
|
2688
2712
|
}
|
|
2689
2713
|
updateSessionStatus(session.id, { status: "ended" });
|
|
@@ -2711,18 +2735,18 @@ async function sendPrompt(sessionId, prompt) {
|
|
|
2711
2735
|
);
|
|
2712
2736
|
}
|
|
2713
2737
|
const fifo = promptFifoPath(sessionId);
|
|
2714
|
-
if (!
|
|
2738
|
+
if (!import_node_fs9.default.existsSync(fifo)) {
|
|
2715
2739
|
throw new AgentSessionError("NO_FIFO", `prompt FIFO missing: ${fifo}`);
|
|
2716
2740
|
}
|
|
2717
|
-
const fd =
|
|
2741
|
+
const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_WRONLY);
|
|
2718
2742
|
try {
|
|
2719
2743
|
const wireText = encodeAgentPromptEnvelope(prompt);
|
|
2720
2744
|
if (!wireText) {
|
|
2721
2745
|
throw new AgentSessionError("EMPTY_PROMPT", "prompt text is empty");
|
|
2722
2746
|
}
|
|
2723
|
-
|
|
2747
|
+
import_node_fs9.default.writeSync(fd, wireText + "\n");
|
|
2724
2748
|
} finally {
|
|
2725
|
-
|
|
2749
|
+
import_node_fs9.default.closeSync(fd);
|
|
2726
2750
|
}
|
|
2727
2751
|
updateSessionStatus(sessionId, {
|
|
2728
2752
|
lastPrompt: prompt.displayText ?? prompt.text,
|
|
@@ -2744,7 +2768,7 @@ async function endSession(sessionId) {
|
|
|
2744
2768
|
const base = getUserDataDir();
|
|
2745
2769
|
if (dir.startsWith(base)) {
|
|
2746
2770
|
try {
|
|
2747
|
-
|
|
2771
|
+
import_node_fs9.default.rmSync(dir, { recursive: true, force: true });
|
|
2748
2772
|
} catch {
|
|
2749
2773
|
}
|
|
2750
2774
|
}
|
|
@@ -2752,11 +2776,11 @@ async function endSession(sessionId) {
|
|
|
2752
2776
|
}
|
|
2753
2777
|
function subscribeEvents(sessionId, onEvent) {
|
|
2754
2778
|
const fifo = eventsFifoPath(sessionId);
|
|
2755
|
-
if (!
|
|
2779
|
+
if (!import_node_fs9.default.existsSync(fifo)) {
|
|
2756
2780
|
throw new AgentSessionError("NO_FIFO", `events FIFO missing: ${fifo}`);
|
|
2757
2781
|
}
|
|
2758
|
-
const fd =
|
|
2759
|
-
const stream =
|
|
2782
|
+
const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_RDWR);
|
|
2783
|
+
const stream = import_node_fs9.default.createReadStream("", { fd, autoClose: true });
|
|
2760
2784
|
const rl = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
|
|
2761
2785
|
rl.on("line", (line) => {
|
|
2762
2786
|
const event = parseAgentEventLine(line);
|
|
@@ -2767,7 +2791,7 @@ function subscribeEvents(sessionId, onEvent) {
|
|
|
2767
2791
|
if (closed) return;
|
|
2768
2792
|
closed = true;
|
|
2769
2793
|
try {
|
|
2770
|
-
|
|
2794
|
+
import_node_fs9.default.writeSync(fd, "\n");
|
|
2771
2795
|
} catch {
|
|
2772
2796
|
}
|
|
2773
2797
|
try {
|
|
@@ -2781,7 +2805,7 @@ function subscribeEvents(sessionId, onEvent) {
|
|
|
2781
2805
|
};
|
|
2782
2806
|
}
|
|
2783
2807
|
async function waitForFirstEvent(fifo, predicate, timeoutMs) {
|
|
2784
|
-
const fd =
|
|
2808
|
+
const fd = import_node_fs9.default.openSync(fifo, import_node_fs9.constants.O_RDWR | import_node_fs9.constants.O_NONBLOCK);
|
|
2785
2809
|
const buf = Buffer.alloc(8192);
|
|
2786
2810
|
let leftover = "";
|
|
2787
2811
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -2789,7 +2813,7 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
|
|
|
2789
2813
|
while (Date.now() < deadline) {
|
|
2790
2814
|
let n = 0;
|
|
2791
2815
|
try {
|
|
2792
|
-
n =
|
|
2816
|
+
n = import_node_fs9.default.readSync(fd, buf, 0, buf.length, null);
|
|
2793
2817
|
} catch (err) {
|
|
2794
2818
|
if (err.code !== "EAGAIN") throw err;
|
|
2795
2819
|
n = 0;
|
|
@@ -2809,7 +2833,7 @@ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
|
|
|
2809
2833
|
}
|
|
2810
2834
|
return null;
|
|
2811
2835
|
} finally {
|
|
2812
|
-
|
|
2836
|
+
import_node_fs9.default.closeSync(fd);
|
|
2813
2837
|
}
|
|
2814
2838
|
}
|
|
2815
2839
|
|
|
@@ -3087,7 +3111,7 @@ var AgentHost = class {
|
|
|
3087
3111
|
);
|
|
3088
3112
|
const project = upsertProject({
|
|
3089
3113
|
cwd: match.cwd,
|
|
3090
|
-
name: match.projectName ??
|
|
3114
|
+
name: match.projectName ?? import_node_path10.default.basename(match.cwd),
|
|
3091
3115
|
preferredProvider: input.provider ?? existing?.preferredProvider,
|
|
3092
3116
|
sourceRoots: existing?.sourceRoots ?? [match.cwd],
|
|
3093
3117
|
knownBundleUrls,
|
|
@@ -3098,18 +3122,18 @@ var AgentHost = class {
|
|
|
3098
3122
|
}
|
|
3099
3123
|
getTranscript(sessionId) {
|
|
3100
3124
|
const p = transcriptPath(sessionId);
|
|
3101
|
-
if (!
|
|
3125
|
+
if (!import_node_fs10.default.existsSync(p)) {
|
|
3102
3126
|
return { error: "transcript not found", code: "NO_TRANSCRIPT" };
|
|
3103
3127
|
}
|
|
3104
|
-
return
|
|
3128
|
+
return import_node_fs10.default.readFileSync(p, "utf8");
|
|
3105
3129
|
}
|
|
3106
3130
|
getPaths() {
|
|
3107
3131
|
const dir = getUserDataDir();
|
|
3108
3132
|
return {
|
|
3109
3133
|
userDataDir: dir,
|
|
3110
|
-
storeFile:
|
|
3111
|
-
sessionsDir:
|
|
3112
|
-
transcriptsDir:
|
|
3134
|
+
storeFile: import_node_path10.default.join(dir, "attached-projects.json"),
|
|
3135
|
+
sessionsDir: import_node_path10.default.join(dir, "sessions"),
|
|
3136
|
+
transcriptsDir: import_node_path10.default.join(dir, "transcripts")
|
|
3113
3137
|
};
|
|
3114
3138
|
}
|
|
3115
3139
|
// --- subscription management ---
|
|
@@ -3812,8 +3836,8 @@ async function openUrl(url, options = {}) {
|
|
|
3812
3836
|
}
|
|
3813
3837
|
|
|
3814
3838
|
// src/host/plane-host.ts
|
|
3815
|
-
var
|
|
3816
|
-
var
|
|
3839
|
+
var import_node_fs12 = __toESM(require("node:fs"), 1);
|
|
3840
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
3817
3841
|
|
|
3818
3842
|
// ../rnx-box/src/planeProtocol.ts
|
|
3819
3843
|
var PLANE_FLAG_READ_ONLY = 1;
|
|
@@ -3893,8 +3917,8 @@ async function dispatchPlaneCommand(plane, type, msg, mutationOptions) {
|
|
|
3893
3917
|
|
|
3894
3918
|
// cli/commands/box/checkout-plane.ts
|
|
3895
3919
|
var import_node_child_process4 = require("node:child_process");
|
|
3896
|
-
var
|
|
3897
|
-
var
|
|
3920
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
3921
|
+
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
3898
3922
|
var CheckoutRequiresGitError = class extends Error {
|
|
3899
3923
|
};
|
|
3900
3924
|
var BINARY_SNIFF_BYTES = 8192;
|
|
@@ -3905,14 +3929,14 @@ function looksBinary(filePath, size) {
|
|
|
3905
3929
|
if (size === 0) return false;
|
|
3906
3930
|
let fd;
|
|
3907
3931
|
try {
|
|
3908
|
-
fd =
|
|
3932
|
+
fd = import_node_fs11.default.openSync(filePath, "r");
|
|
3909
3933
|
const buffer = Buffer.alloc(Math.min(BINARY_SNIFF_BYTES, size));
|
|
3910
|
-
const read =
|
|
3934
|
+
const read = import_node_fs11.default.readSync(fd, buffer, 0, buffer.length, 0);
|
|
3911
3935
|
return buffer.subarray(0, read).includes(0);
|
|
3912
3936
|
} catch {
|
|
3913
3937
|
return false;
|
|
3914
3938
|
} finally {
|
|
3915
|
-
if (fd !== void 0)
|
|
3939
|
+
if (fd !== void 0) import_node_fs11.default.closeSync(fd);
|
|
3916
3940
|
}
|
|
3917
3941
|
}
|
|
3918
3942
|
var CheckoutFilePlane = class {
|
|
@@ -3925,7 +3949,7 @@ var CheckoutFilePlane = class {
|
|
|
3925
3949
|
listTimeoutMs;
|
|
3926
3950
|
trackedOnly;
|
|
3927
3951
|
constructor(root, options = {}) {
|
|
3928
|
-
this.root =
|
|
3952
|
+
this.root = import_node_path11.default.resolve(root);
|
|
3929
3953
|
this.listTimeoutMs = options.listTimeoutMs ?? LIST_FILES_TIMEOUT_MS;
|
|
3930
3954
|
this.trackedOnly = options.trackedOnly ?? false;
|
|
3931
3955
|
}
|
|
@@ -3987,13 +4011,13 @@ var CheckoutFilePlane = class {
|
|
|
3987
4011
|
if (!relative) continue;
|
|
3988
4012
|
let stat2;
|
|
3989
4013
|
try {
|
|
3990
|
-
stat2 =
|
|
4014
|
+
stat2 = import_node_fs11.default.statSync(import_node_path11.default.join(this.root, relative));
|
|
3991
4015
|
} catch {
|
|
3992
4016
|
continue;
|
|
3993
4017
|
}
|
|
3994
4018
|
if (!stat2.isFile()) continue;
|
|
3995
4019
|
const seen = this.sniffed.get(relative);
|
|
3996
|
-
const binary = seen && seen.size === stat2.size && seen.mtimeMs === stat2.mtimeMs ? seen.binary : looksBinary(
|
|
4020
|
+
const binary = seen && seen.size === stat2.size && seen.mtimeMs === stat2.mtimeMs ? seen.binary : looksBinary(import_node_path11.default.join(this.root, relative), stat2.size);
|
|
3997
4021
|
nextSniffed.set(relative, { size: stat2.size, mtimeMs: stat2.mtimeMs, binary });
|
|
3998
4022
|
next.set(relative, {
|
|
3999
4023
|
size: stat2.size,
|
|
@@ -4007,22 +4031,22 @@ var CheckoutFilePlane = class {
|
|
|
4007
4031
|
this.sniffed = nextSniffed;
|
|
4008
4032
|
}
|
|
4009
4033
|
absolute(planePath) {
|
|
4010
|
-
const resolved =
|
|
4011
|
-
if (resolved !== this.root && !resolved.startsWith(`${this.root}${
|
|
4034
|
+
const resolved = import_node_path11.default.resolve(this.root, planePath);
|
|
4035
|
+
if (resolved !== this.root && !resolved.startsWith(`${this.root}${import_node_path11.default.sep}`)) {
|
|
4012
4036
|
throw new Error(`path escapes the project: ${planePath}`);
|
|
4013
4037
|
}
|
|
4014
4038
|
return resolved;
|
|
4015
4039
|
}
|
|
4016
4040
|
async read(planePath) {
|
|
4017
|
-
return
|
|
4041
|
+
return import_node_fs11.default.promises.readFile(this.absolute(planePath), "utf8");
|
|
4018
4042
|
}
|
|
4019
4043
|
async readBytes(planePath) {
|
|
4020
|
-
return
|
|
4044
|
+
return import_node_fs11.default.promises.readFile(this.absolute(planePath));
|
|
4021
4045
|
}
|
|
4022
4046
|
async write(planePath, content) {
|
|
4023
4047
|
const absolute = this.absolute(planePath);
|
|
4024
|
-
await
|
|
4025
|
-
await
|
|
4048
|
+
await import_node_fs11.default.promises.mkdir(import_node_path11.default.dirname(absolute), { recursive: true });
|
|
4049
|
+
await import_node_fs11.default.promises.writeFile(absolute, content);
|
|
4026
4050
|
this.entries.set(planePath, {
|
|
4027
4051
|
size: content.byteLength,
|
|
4028
4052
|
readOnly: false,
|
|
@@ -4031,7 +4055,7 @@ var CheckoutFilePlane = class {
|
|
|
4031
4055
|
});
|
|
4032
4056
|
}
|
|
4033
4057
|
async delete(planePath) {
|
|
4034
|
-
await
|
|
4058
|
+
await import_node_fs11.default.promises.rm(this.absolute(planePath), { force: true });
|
|
4035
4059
|
this.entries.delete(planePath);
|
|
4036
4060
|
}
|
|
4037
4061
|
};
|
|
@@ -4157,14 +4181,14 @@ var PlaneHost = class {
|
|
|
4157
4181
|
// root and an opened one cannot disagree about the same directory.
|
|
4158
4182
|
realRoot(root) {
|
|
4159
4183
|
if (!root) throw new Error("root is required");
|
|
4160
|
-
const resolved =
|
|
4184
|
+
const resolved = import_node_path12.default.resolve(root);
|
|
4161
4185
|
let real;
|
|
4162
4186
|
try {
|
|
4163
|
-
real =
|
|
4187
|
+
real = import_node_fs12.default.realpathSync(resolved);
|
|
4164
4188
|
} catch {
|
|
4165
4189
|
throw new Error(`no such directory: ${resolved}`);
|
|
4166
4190
|
}
|
|
4167
|
-
if (!
|
|
4191
|
+
if (!import_node_fs12.default.statSync(real).isDirectory()) throw new Error(`not a directory: ${real}`);
|
|
4168
4192
|
return real;
|
|
4169
4193
|
}
|
|
4170
4194
|
respond(ws, id, result) {
|
|
@@ -4185,11 +4209,11 @@ var PlaneHost = class {
|
|
|
4185
4209
|
|
|
4186
4210
|
// src/host/replacement-module-handler.ts
|
|
4187
4211
|
var import_promises5 = __toESM(require("node:fs/promises"), 1);
|
|
4188
|
-
var
|
|
4212
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
4189
4213
|
var import_esbuild = require("esbuild");
|
|
4190
4214
|
var REPLACEMENT_MODULE_ROUTE = "/__sootsim-replacement-module";
|
|
4191
4215
|
function getReplacementLoader(filePath) {
|
|
4192
|
-
const ext =
|
|
4216
|
+
const ext = import_node_path13.default.extname(filePath).toLowerCase();
|
|
4193
4217
|
switch (ext) {
|
|
4194
4218
|
case ".js":
|
|
4195
4219
|
return "js";
|
|
@@ -4271,7 +4295,7 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
|
|
|
4271
4295
|
res.end("replacement files must be .js, .jsx, .ts, or .tsx");
|
|
4272
4296
|
return true;
|
|
4273
4297
|
}
|
|
4274
|
-
if (!
|
|
4298
|
+
if (!import_node_path13.default.isAbsolute(targetPath)) {
|
|
4275
4299
|
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
|
|
4276
4300
|
res.end("replacement file path must be absolute");
|
|
4277
4301
|
return true;
|
|
@@ -4279,10 +4303,10 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
|
|
|
4279
4303
|
const runtimePackages = new Set(url.searchParams.getAll("runtimePackage"));
|
|
4280
4304
|
let resolvedProjectRoot;
|
|
4281
4305
|
if (options.projectRoot) {
|
|
4282
|
-
const lexicalProjectRoot =
|
|
4306
|
+
const lexicalProjectRoot = import_node_path13.default.resolve(options.projectRoot);
|
|
4283
4307
|
resolvedProjectRoot = await import_promises5.default.realpath(options.projectRoot);
|
|
4284
|
-
const lexicalTarget =
|
|
4285
|
-
if (lexicalTarget.startsWith("..") ||
|
|
4308
|
+
const lexicalTarget = import_node_path13.default.relative(lexicalProjectRoot, targetPath);
|
|
4309
|
+
if (lexicalTarget.startsWith("..") || import_node_path13.default.isAbsolute(lexicalTarget)) {
|
|
4286
4310
|
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
|
|
4287
4311
|
res.end("replacement file must be inside the project root");
|
|
4288
4312
|
return true;
|
|
@@ -4298,8 +4322,8 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
|
|
|
4298
4322
|
return true;
|
|
4299
4323
|
}
|
|
4300
4324
|
if (resolvedProjectRoot) {
|
|
4301
|
-
const relativeTarget =
|
|
4302
|
-
if (relativeTarget.startsWith("..") ||
|
|
4325
|
+
const relativeTarget = import_node_path13.default.relative(resolvedProjectRoot, resolvedTargetPath);
|
|
4326
|
+
if (relativeTarget.startsWith("..") || import_node_path13.default.isAbsolute(relativeTarget)) {
|
|
4303
4327
|
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
|
|
4304
4328
|
res.end("replacement file must be inside the project root");
|
|
4305
4329
|
return true;
|
|
@@ -4386,8 +4410,13 @@ exports.jsxDEV = exports.jsx`,
|
|
|
4386
4410
|
}
|
|
4387
4411
|
|
|
4388
4412
|
// src/host/websocket-proxy.ts
|
|
4389
|
-
var
|
|
4413
|
+
var import_crypto = require("crypto");
|
|
4414
|
+
var import_http3 = require("http");
|
|
4415
|
+
var import_https2 = require("https");
|
|
4390
4416
|
var WEBSOCKET_PROXY_PATH = "/__websocket-proxy";
|
|
4417
|
+
var WEBSOCKET_PROXY_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
4418
|
+
var WEBSOCKET_PROTOCOL_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
4419
|
+
var WEBSOCKET_KEY = /^[+/0-9A-Za-z]{22}==$/;
|
|
4391
4420
|
var STRIP_UPSTREAM_HEADERS = /* @__PURE__ */ new Set([
|
|
4392
4421
|
"host",
|
|
4393
4422
|
"connection",
|
|
@@ -4400,20 +4429,62 @@ var STRIP_UPSTREAM_HEADERS = /* @__PURE__ */ new Set([
|
|
|
4400
4429
|
"sec-websocket-protocol",
|
|
4401
4430
|
"sec-websocket-version"
|
|
4402
4431
|
]);
|
|
4403
|
-
function rejectUpgrade(socket, status, message) {
|
|
4432
|
+
function rejectUpgrade(socket, status, message, headers = {}) {
|
|
4404
4433
|
try {
|
|
4434
|
+
const extraHeaders = Object.entries(headers).map(([name2, value]) => `${name2}: ${value}\r
|
|
4435
|
+
`).join("");
|
|
4405
4436
|
socket.write(
|
|
4406
4437
|
`HTTP/1.1 ${status} ${message}\r
|
|
4407
4438
|
Connection: close\r
|
|
4408
4439
|
Content-Type: text/plain\r
|
|
4409
4440
|
Content-Length: ${message.length}\r
|
|
4410
|
-
\r
|
|
4441
|
+
${extraHeaders}\r
|
|
4411
4442
|
${message}`
|
|
4412
4443
|
);
|
|
4413
4444
|
} catch {
|
|
4414
4445
|
}
|
|
4415
4446
|
socket.destroy();
|
|
4416
4447
|
}
|
|
4448
|
+
function hasHeaderToken(value, token) {
|
|
4449
|
+
return (value || "").split(",").some((part) => part.trim().toLowerCase() === token);
|
|
4450
|
+
}
|
|
4451
|
+
function hasValidProtocolOffer(value) {
|
|
4452
|
+
if (value === void 0) return true;
|
|
4453
|
+
if (!WEBSOCKET_PROTOCOL_TOKEN.test(value[0] || "")) return false;
|
|
4454
|
+
if (!WEBSOCKET_PROTOCOL_TOKEN.test(value[value.length - 1] || "")) return false;
|
|
4455
|
+
const protocols = /* @__PURE__ */ new Set();
|
|
4456
|
+
for (const part of value.split(",")) {
|
|
4457
|
+
const protocol = part.trim();
|
|
4458
|
+
if (!WEBSOCKET_PROTOCOL_TOKEN.test(protocol) || protocols.has(protocol)) {
|
|
4459
|
+
return false;
|
|
4460
|
+
}
|
|
4461
|
+
protocols.add(protocol);
|
|
4462
|
+
}
|
|
4463
|
+
return true;
|
|
4464
|
+
}
|
|
4465
|
+
function validateUpgradeRequest(req) {
|
|
4466
|
+
if (req.method !== "GET") {
|
|
4467
|
+
return { status: 405, message: "invalid websocket http method" };
|
|
4468
|
+
}
|
|
4469
|
+
if (!hasHeaderToken(req.headers.connection, "upgrade") || !hasHeaderToken(req.headers.upgrade, "websocket")) {
|
|
4470
|
+
return { status: 400, message: "invalid websocket upgrade headers" };
|
|
4471
|
+
}
|
|
4472
|
+
const key = req.headers["sec-websocket-key"];
|
|
4473
|
+
if (typeof key !== "string" || !WEBSOCKET_KEY.test(key) || Buffer.from(key, "base64").byteLength !== 16 || Buffer.from(key, "base64").toString("base64") !== key) {
|
|
4474
|
+
return { status: 400, message: "invalid websocket key" };
|
|
4475
|
+
}
|
|
4476
|
+
if (req.headers["sec-websocket-version"] !== "13") {
|
|
4477
|
+
return {
|
|
4478
|
+
status: 400,
|
|
4479
|
+
message: "invalid websocket version",
|
|
4480
|
+
headers: { "Sec-WebSocket-Version": "13" }
|
|
4481
|
+
};
|
|
4482
|
+
}
|
|
4483
|
+
if (!hasValidProtocolOffer(req.headers["sec-websocket-protocol"])) {
|
|
4484
|
+
return { status: 400, message: "invalid websocket protocol offer" };
|
|
4485
|
+
}
|
|
4486
|
+
return null;
|
|
4487
|
+
}
|
|
4417
4488
|
function isSameOriginUpgrade(req) {
|
|
4418
4489
|
const origin = req.headers.origin;
|
|
4419
4490
|
const host = req.headers.host;
|
|
@@ -4443,61 +4514,29 @@ function getDefaultWebSocketOrigin(targetUrl) {
|
|
|
4443
4514
|
origin.protocol = targetUrl.protocol === "wss:" ? "https:" : "http:";
|
|
4444
4515
|
return origin.origin;
|
|
4445
4516
|
}
|
|
4446
|
-
function
|
|
4447
|
-
const header = req.headers["sec-websocket-protocol"];
|
|
4448
|
-
const value = Array.isArray(header) ? header.join(",") : header || "";
|
|
4449
|
-
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
4450
|
-
}
|
|
4451
|
-
function safeClose(ws, code, reason) {
|
|
4452
|
-
if (ws.readyState === import_ws.WebSocket.CLOSED || ws.readyState === import_ws.WebSocket.CLOSING) {
|
|
4453
|
-
return;
|
|
4454
|
-
}
|
|
4455
|
-
try {
|
|
4456
|
-
ws.close(code, reason);
|
|
4457
|
-
} catch {
|
|
4458
|
-
ws.terminate();
|
|
4459
|
-
}
|
|
4460
|
-
}
|
|
4461
|
-
function connectProxyPair(clientWs, upstream) {
|
|
4462
|
-
let closing = false;
|
|
4463
|
-
const closeBoth = (source, target, code, reason) => {
|
|
4464
|
-
if (closing) return;
|
|
4465
|
-
closing = true;
|
|
4466
|
-
safeClose(target, code, reason.toString());
|
|
4467
|
-
if (source.readyState === import_ws.WebSocket.OPEN) {
|
|
4468
|
-
safeClose(source, code, reason.toString());
|
|
4469
|
-
}
|
|
4470
|
-
};
|
|
4471
|
-
clientWs.on("message", (data, isBinary) => {
|
|
4472
|
-
if (upstream.readyState === import_ws.WebSocket.OPEN) {
|
|
4473
|
-
upstream.send(data, { binary: isBinary });
|
|
4474
|
-
}
|
|
4475
|
-
});
|
|
4476
|
-
upstream.on("message", (data, isBinary) => {
|
|
4477
|
-
if (clientWs.readyState === import_ws.WebSocket.OPEN) {
|
|
4478
|
-
clientWs.send(data, { binary: isBinary });
|
|
4479
|
-
}
|
|
4480
|
-
});
|
|
4481
|
-
clientWs.on("close", (code, reason) => closeBoth(clientWs, upstream, code, reason));
|
|
4482
|
-
upstream.on("close", (code, reason) => closeBoth(upstream, clientWs, code, reason));
|
|
4483
|
-
clientWs.on("error", () => safeClose(upstream, 1011, "proxy client error"));
|
|
4484
|
-
upstream.on("error", () => safeClose(clientWs, 1011, "upstream websocket error"));
|
|
4485
|
-
}
|
|
4486
|
-
function createUpstreamWebSocket(targetUrl, protocols, headers, forwardOrigin) {
|
|
4517
|
+
function createUpstreamHeaders(req, targetUrl, headers, forwardOrigin) {
|
|
4487
4518
|
const upstreamHeaders = { ...headers };
|
|
4488
4519
|
const originKeys = Object.keys(upstreamHeaders).filter(
|
|
4489
|
-
(
|
|
4520
|
+
(key2) => key2.toLowerCase() === "origin"
|
|
4490
4521
|
);
|
|
4491
4522
|
if (forwardOrigin && originKeys.length === 0) {
|
|
4492
4523
|
upstreamHeaders.origin = getDefaultWebSocketOrigin(targetUrl);
|
|
4493
4524
|
} else if (!forwardOrigin) {
|
|
4494
|
-
for (const
|
|
4495
|
-
}
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4525
|
+
for (const key2 of originKeys) delete upstreamHeaders[key2];
|
|
4526
|
+
}
|
|
4527
|
+
upstreamHeaders.connection = "Upgrade";
|
|
4528
|
+
upstreamHeaders.upgrade = "websocket";
|
|
4529
|
+
const key = req.headers["sec-websocket-key"];
|
|
4530
|
+
if (typeof key === "string") upstreamHeaders["sec-websocket-key"] = key;
|
|
4531
|
+
const version = req.headers["sec-websocket-version"];
|
|
4532
|
+
if (typeof version === "string") upstreamHeaders["sec-websocket-version"] = version;
|
|
4533
|
+
const protocol = req.headers["sec-websocket-protocol"];
|
|
4534
|
+
if (typeof protocol === "string") upstreamHeaders["sec-websocket-protocol"] = protocol;
|
|
4535
|
+
const extensions = req.headers["sec-websocket-extensions"];
|
|
4536
|
+
if (typeof extensions === "string") {
|
|
4537
|
+
upstreamHeaders["sec-websocket-extensions"] = extensions;
|
|
4538
|
+
}
|
|
4539
|
+
return upstreamHeaders;
|
|
4501
4540
|
}
|
|
4502
4541
|
function isWebSocketProxyRequestUrl(rawUrl) {
|
|
4503
4542
|
if (!rawUrl) return false;
|
|
@@ -4513,6 +4552,16 @@ function handleWebSocketProxyUpgrade(req, socket, head, forwardOrigin = true) {
|
|
|
4513
4552
|
rejectUpgrade(socket, 403, "forbidden websocket proxy origin");
|
|
4514
4553
|
return true;
|
|
4515
4554
|
}
|
|
4555
|
+
const upgradeRejection = validateUpgradeRequest(req);
|
|
4556
|
+
if (upgradeRejection) {
|
|
4557
|
+
rejectUpgrade(
|
|
4558
|
+
socket,
|
|
4559
|
+
upgradeRejection.status,
|
|
4560
|
+
upgradeRejection.message,
|
|
4561
|
+
upgradeRejection.headers
|
|
4562
|
+
);
|
|
4563
|
+
return true;
|
|
4564
|
+
}
|
|
4516
4565
|
let targetUrl;
|
|
4517
4566
|
let headers;
|
|
4518
4567
|
try {
|
|
@@ -4532,37 +4581,94 @@ function handleWebSocketProxyUpgrade(req, socket, head, forwardOrigin = true) {
|
|
|
4532
4581
|
rejectUpgrade(socket, 400, "invalid websocket proxy request");
|
|
4533
4582
|
return true;
|
|
4534
4583
|
}
|
|
4535
|
-
const
|
|
4536
|
-
|
|
4537
|
-
|
|
4584
|
+
const upstreamHeaders = createUpstreamHeaders(req, targetUrl, headers, forwardOrigin);
|
|
4585
|
+
let settled = false;
|
|
4586
|
+
const request = targetUrl.protocol === "wss:" ? import_https2.request : import_http3.request;
|
|
4587
|
+
const upstreamRequestUrl = new URL(targetUrl);
|
|
4588
|
+
upstreamRequestUrl.protocol = targetUrl.protocol === "wss:" ? "https:" : "http:";
|
|
4589
|
+
const upstreamRequest = request(upstreamRequestUrl, {
|
|
4590
|
+
method: "GET",
|
|
4591
|
+
headers: upstreamHeaders
|
|
4592
|
+
});
|
|
4593
|
+
let destroyUpstreamSocket = () => {
|
|
4594
|
+
};
|
|
4595
|
+
upstreamRequest.once("socket", (upstreamSocket) => {
|
|
4596
|
+
destroyUpstreamSocket = () => upstreamSocket.destroy();
|
|
4597
|
+
});
|
|
4598
|
+
const fail = (status, message) => {
|
|
4599
|
+
if (settled) return;
|
|
4600
|
+
settled = true;
|
|
4601
|
+
clearTimeout(timer);
|
|
4602
|
+
upstreamRequest.destroy();
|
|
4603
|
+
destroyUpstreamSocket();
|
|
4604
|
+
rejectUpgrade(socket, status, message);
|
|
4605
|
+
};
|
|
4606
|
+
const timer = setTimeout(() => {
|
|
4607
|
+
fail(504, "upstream websocket handshake timeout");
|
|
4608
|
+
}, WEBSOCKET_PROXY_HANDSHAKE_TIMEOUT_MS);
|
|
4538
4609
|
socket.once("close", () => {
|
|
4539
|
-
if (!
|
|
4610
|
+
if (!settled) {
|
|
4611
|
+
settled = true;
|
|
4612
|
+
clearTimeout(timer);
|
|
4613
|
+
}
|
|
4614
|
+
upstreamRequest.destroy();
|
|
4615
|
+
destroyUpstreamSocket();
|
|
4540
4616
|
});
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
const selectedProtocol = upstream.protocol;
|
|
4545
|
-
const proxyServer = new import_ws.WebSocketServer({
|
|
4546
|
-
noServer: true,
|
|
4547
|
-
clientTracking: false,
|
|
4548
|
-
handleProtocols(requestedProtocols) {
|
|
4549
|
-
return selectedProtocol || requestedProtocols.values().next().value || false;
|
|
4550
|
-
}
|
|
4551
|
-
});
|
|
4552
|
-
proxyServer.handleUpgrade(req, socket, head, (clientWs) => {
|
|
4553
|
-
connectProxyPair(clientWs, upstream);
|
|
4554
|
-
});
|
|
4617
|
+
upstreamRequest.once("response", (response) => {
|
|
4618
|
+
response.resume();
|
|
4619
|
+
fail(502, "upstream websocket rejected upgrade");
|
|
4555
4620
|
});
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
completed = true;
|
|
4559
|
-
rejectUpgrade(socket, 502, "upstream websocket error");
|
|
4621
|
+
upstreamRequest.once("error", () => {
|
|
4622
|
+
fail(502, "upstream websocket error");
|
|
4560
4623
|
});
|
|
4561
|
-
|
|
4562
|
-
if (
|
|
4563
|
-
|
|
4564
|
-
|
|
4624
|
+
upstreamRequest.once("upgrade", (response, upstreamSocket, upstreamHead) => {
|
|
4625
|
+
if (settled) {
|
|
4626
|
+
upstreamSocket.destroy();
|
|
4627
|
+
return;
|
|
4628
|
+
}
|
|
4629
|
+
const accept = response.headers["sec-websocket-accept"];
|
|
4630
|
+
const upgrade = response.headers.upgrade;
|
|
4631
|
+
const requestKey = req.headers["sec-websocket-key"];
|
|
4632
|
+
const expectedAccept = typeof requestKey === "string" ? (0, import_crypto.createHash)("sha1").update(`${requestKey}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest("base64") : "";
|
|
4633
|
+
if (typeof accept !== "string" || accept !== expectedAccept || typeof upgrade !== "string" || upgrade.toLowerCase() !== "websocket") {
|
|
4634
|
+
upstreamSocket.destroy();
|
|
4635
|
+
fail(502, "upstream websocket handshake is invalid");
|
|
4636
|
+
return;
|
|
4637
|
+
}
|
|
4638
|
+
const selectedProtocol = response.headers["sec-websocket-protocol"];
|
|
4639
|
+
const requestedProtocols = (req.headers["sec-websocket-protocol"] || "").split(",").map((protocol) => protocol.trim());
|
|
4640
|
+
if (typeof selectedProtocol === "string" && !requestedProtocols.includes(selectedProtocol)) {
|
|
4641
|
+
upstreamSocket.destroy();
|
|
4642
|
+
fail(502, "upstream websocket selected an unrequested protocol");
|
|
4643
|
+
return;
|
|
4644
|
+
}
|
|
4645
|
+
settled = true;
|
|
4646
|
+
clearTimeout(timer);
|
|
4647
|
+
const responseHeaders = [
|
|
4648
|
+
"HTTP/1.1 101 Switching Protocols",
|
|
4649
|
+
"Upgrade: websocket",
|
|
4650
|
+
"Connection: Upgrade",
|
|
4651
|
+
`Sec-WebSocket-Accept: ${accept}`
|
|
4652
|
+
];
|
|
4653
|
+
if (typeof selectedProtocol === "string") {
|
|
4654
|
+
responseHeaders.push(`Sec-WebSocket-Protocol: ${selectedProtocol}`);
|
|
4655
|
+
}
|
|
4656
|
+
const selectedExtensions = response.headers["sec-websocket-extensions"];
|
|
4657
|
+
if (typeof selectedExtensions === "string") {
|
|
4658
|
+
responseHeaders.push(`Sec-WebSocket-Extensions: ${selectedExtensions}`);
|
|
4659
|
+
}
|
|
4660
|
+
socket.write(`${responseHeaders.join("\r\n")}\r
|
|
4661
|
+
\r
|
|
4662
|
+
`);
|
|
4663
|
+
if (head.length > 0) upstreamSocket.write(head);
|
|
4664
|
+
if (upstreamHead.length > 0) socket.write(upstreamHead);
|
|
4665
|
+
socket.once("close", () => upstreamSocket.destroy());
|
|
4666
|
+
socket.once("error", () => upstreamSocket.destroy());
|
|
4667
|
+
upstreamSocket.once("error", () => socket.destroy());
|
|
4668
|
+
socket.pipe(upstreamSocket);
|
|
4669
|
+
upstreamSocket.pipe(socket);
|
|
4565
4670
|
});
|
|
4671
|
+
upstreamRequest.end();
|
|
4566
4672
|
return true;
|
|
4567
4673
|
}
|
|
4568
4674
|
|
|
@@ -4807,7 +4913,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
4807
4913
|
}
|
|
4808
4914
|
bindOnce(port, _silent) {
|
|
4809
4915
|
return new Promise((resolve2, reject) => {
|
|
4810
|
-
const server = (0,
|
|
4916
|
+
const server = (0, import_http4.createServer)((req, res) => this.handleHttpRequest(req, res));
|
|
4811
4917
|
let settled = false;
|
|
4812
4918
|
const onError = (err) => {
|
|
4813
4919
|
if (settled) return;
|
|
@@ -4830,7 +4936,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
4830
4936
|
`);
|
|
4831
4937
|
});
|
|
4832
4938
|
this.httpServer = server;
|
|
4833
|
-
this.wss = new
|
|
4939
|
+
this.wss = new import_ws.WebSocketServer({ noServer: true });
|
|
4834
4940
|
this.wireWebSocketServer();
|
|
4835
4941
|
server.on("upgrade", (req, socket, head) => {
|
|
4836
4942
|
if (handleWebSocketProxyUpgrade(req, socket, head)) return;
|
|
@@ -5068,7 +5174,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5068
5174
|
const entry = this.cliBySentId.get(msg.id);
|
|
5069
5175
|
if (entry) {
|
|
5070
5176
|
this.cliBySentId.delete(msg.id);
|
|
5071
|
-
if (entry.ws.readyState ===
|
|
5177
|
+
if (entry.ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5072
5178
|
const otherCliCount = this.getOtherCliIdentityCount(entry.ws, entry.simId);
|
|
5073
5179
|
const response = otherCliCount > 0 ? { ...msg, id: entry.originalId, _otherCliCount: otherCliCount } : { ...msg, id: entry.originalId };
|
|
5074
5180
|
entry.ws.send(JSON.stringify(response));
|
|
@@ -5096,7 +5202,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5096
5202
|
if (typeof msg.cliLabel === "string" && msg.cliLabel.trim()) {
|
|
5097
5203
|
this.cliLabelBySocket.set(ws, msg.cliLabel.trim());
|
|
5098
5204
|
}
|
|
5099
|
-
if (ws.readyState ===
|
|
5205
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5100
5206
|
ws.send(
|
|
5101
5207
|
JSON.stringify({
|
|
5102
5208
|
id: msg.id,
|
|
@@ -5111,7 +5217,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5111
5217
|
return;
|
|
5112
5218
|
}
|
|
5113
5219
|
if (msg.type === "bridge:list-sims") {
|
|
5114
|
-
if (ws.readyState ===
|
|
5220
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5115
5221
|
ws.send(
|
|
5116
5222
|
JSON.stringify({
|
|
5117
5223
|
id: msg.id,
|
|
@@ -5127,7 +5233,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5127
5233
|
force: msg.force === true
|
|
5128
5234
|
});
|
|
5129
5235
|
if (!outcome.granted) {
|
|
5130
|
-
if (ws.readyState ===
|
|
5236
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5131
5237
|
ws.send(
|
|
5132
5238
|
JSON.stringify({
|
|
5133
5239
|
id: msg.id,
|
|
@@ -5143,7 +5249,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5143
5249
|
targetSim2.id,
|
|
5144
5250
|
outcome.bootedCount > 0 ? `cli force-claimed sim (booted ${outcome.bootedCount})` : "cli claimed sim"
|
|
5145
5251
|
);
|
|
5146
|
-
if (ws.readyState ===
|
|
5252
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5147
5253
|
ws.send(
|
|
5148
5254
|
JSON.stringify({
|
|
5149
5255
|
id: msg.id,
|
|
@@ -5162,7 +5268,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5162
5268
|
if (shouldAcquireLease(msg)) {
|
|
5163
5269
|
const outcome = this.tryAcquireLease(ws, targetSim);
|
|
5164
5270
|
if (!outcome.granted) {
|
|
5165
|
-
if (ws.readyState ===
|
|
5271
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5166
5272
|
ws.send(
|
|
5167
5273
|
JSON.stringify({
|
|
5168
5274
|
id: msg.id,
|
|
@@ -5188,7 +5294,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5188
5294
|
targetSim.ws.send(JSON.stringify({ ...forwarded, id: sentId }));
|
|
5189
5295
|
if (forwarded.type === "close") {
|
|
5190
5296
|
this.cliBySentId.delete(sentId);
|
|
5191
|
-
if (ws.readyState ===
|
|
5297
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5192
5298
|
ws.send(
|
|
5193
5299
|
JSON.stringify({
|
|
5194
5300
|
id: msg.id,
|
|
@@ -5203,7 +5309,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5203
5309
|
unrefTimer(closeTimer);
|
|
5204
5310
|
}
|
|
5205
5311
|
} catch (err) {
|
|
5206
|
-
if (ws.readyState ===
|
|
5312
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5207
5313
|
ws.send(
|
|
5208
5314
|
JSON.stringify({
|
|
5209
5315
|
id: msg.id,
|
|
@@ -5229,7 +5335,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5229
5335
|
}
|
|
5230
5336
|
for (const [sentId, entry] of this.cliBySentId) {
|
|
5231
5337
|
if (entry.simId !== sim.id) continue;
|
|
5232
|
-
if (entry.ws.readyState ===
|
|
5338
|
+
if (entry.ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5233
5339
|
entry.ws.send(
|
|
5234
5340
|
JSON.stringify({
|
|
5235
5341
|
id: entry.originalId,
|
|
@@ -5601,7 +5707,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5601
5707
|
if (runtimeVersionFromHostname(simUrl.hostname)) continue;
|
|
5602
5708
|
} catch {
|
|
5603
5709
|
}
|
|
5604
|
-
if (sim.ws.readyState ===
|
|
5710
|
+
if (sim.ws.readyState === import_ws.WebSocket.OPEN) {
|
|
5605
5711
|
try {
|
|
5606
5712
|
sim.ws.send(payload);
|
|
5607
5713
|
} catch {
|
|
@@ -5985,7 +6091,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
5985
6091
|
sweepDeadWebSockets() {
|
|
5986
6092
|
if (!this.wss) return;
|
|
5987
6093
|
for (const ws of this.wss.clients) {
|
|
5988
|
-
if (ws.readyState !==
|
|
6094
|
+
if (ws.readyState !== import_ws.WebSocket.OPEN) continue;
|
|
5989
6095
|
const alive = this.wsIsAlive.get(ws);
|
|
5990
6096
|
if (alive === false) {
|
|
5991
6097
|
try {
|
|
@@ -6006,7 +6112,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6006
6112
|
}
|
|
6007
6113
|
}
|
|
6008
6114
|
closeSimSocketFromHost(ws) {
|
|
6009
|
-
if (ws.readyState !==
|
|
6115
|
+
if (ws.readyState !== import_ws.WebSocket.OPEN) return;
|
|
6010
6116
|
try {
|
|
6011
6117
|
ws.close(SOOTSIM_BRIDGE_SIM_CLOSE_CODE, SOOTSIM_BRIDGE_SIM_CLOSE_REASON);
|
|
6012
6118
|
} catch {
|
|
@@ -6017,7 +6123,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6017
6123
|
return;
|
|
6018
6124
|
}
|
|
6019
6125
|
const terminateTimer = setTimeout(() => {
|
|
6020
|
-
if (ws.readyState ===
|
|
6126
|
+
if (ws.readyState === import_ws.WebSocket.CLOSED) return;
|
|
6021
6127
|
try {
|
|
6022
6128
|
ws.terminate();
|
|
6023
6129
|
} catch {
|
|
@@ -6180,7 +6286,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6180
6286
|
try {
|
|
6181
6287
|
readyState = sim.ws.readyState;
|
|
6182
6288
|
} catch {
|
|
6183
|
-
readyState =
|
|
6289
|
+
readyState = import_ws.WebSocket.CLOSED;
|
|
6184
6290
|
}
|
|
6185
6291
|
const lease = this.getActiveLease(sim);
|
|
6186
6292
|
return {
|
|
@@ -6193,7 +6299,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6193
6299
|
lastSeenAt: sim.lastSeenAt,
|
|
6194
6300
|
lastActiveAt: sim.lastActiveAt || void 0,
|
|
6195
6301
|
isPrimary: sim.id === this.primarySimId,
|
|
6196
|
-
readyState: readyState ===
|
|
6302
|
+
readyState: readyState === import_ws.WebSocket.OPEN ? "open" : readyState === import_ws.WebSocket.CLOSING ? "closing" : "closed",
|
|
6197
6303
|
attachedCliCount: this.getAttachedCliCount(sim.id),
|
|
6198
6304
|
lockedBy: lease ? lease.cliLabel || lease.cliIdentityKey : void 0,
|
|
6199
6305
|
lockedByKind: lease ? lease.kind : void 0,
|
|
@@ -6310,7 +6416,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6310
6416
|
getOpenSim(simId) {
|
|
6311
6417
|
if (simId) {
|
|
6312
6418
|
const sim = this.sims.get(simId);
|
|
6313
|
-
if (sim?.ws.readyState ===
|
|
6419
|
+
if (sim?.ws.readyState === import_ws.WebSocket.OPEN) return sim;
|
|
6314
6420
|
return null;
|
|
6315
6421
|
}
|
|
6316
6422
|
const candidates = this.getDefaultSimCandidates();
|
|
@@ -6318,7 +6424,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6318
6424
|
}
|
|
6319
6425
|
getDefaultSimCandidates() {
|
|
6320
6426
|
const open = Array.from(this.sims.values()).filter(
|
|
6321
|
-
(sim) => sim.ws.readyState ===
|
|
6427
|
+
(sim) => sim.ws.readyState === import_ws.WebSocket.OPEN
|
|
6322
6428
|
);
|
|
6323
6429
|
const registered = open.filter((sim) => sim.url);
|
|
6324
6430
|
return registered.length > 0 ? registered : open;
|
|
@@ -6351,7 +6457,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6351
6457
|
shouldPromoteSim(sim) {
|
|
6352
6458
|
const current = this.primarySimId ? this.sims.get(this.primarySimId) : null;
|
|
6353
6459
|
if (!sim.url) return !current;
|
|
6354
|
-
const currentAlive = current?.ws.readyState ===
|
|
6460
|
+
const currentAlive = current?.ws.readyState === import_ws.WebSocket.OPEN;
|
|
6355
6461
|
if (!current || !currentAlive || !current.url) return true;
|
|
6356
6462
|
const isPrimaryCandidate = sim.origin?.includes(":5173");
|
|
6357
6463
|
const currentIsPrimary = current.origin?.includes(":5173");
|
|
@@ -6359,7 +6465,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6359
6465
|
}
|
|
6360
6466
|
broadcastSimAssignments() {
|
|
6361
6467
|
for (const sim of this.sims.values()) {
|
|
6362
|
-
if (sim.ws.readyState !==
|
|
6468
|
+
if (sim.ws.readyState !== import_ws.WebSocket.OPEN) continue;
|
|
6363
6469
|
sim.ws.send(
|
|
6364
6470
|
JSON.stringify({
|
|
6365
6471
|
type: "bridge:welcome",
|
|
@@ -6376,7 +6482,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6376
6482
|
config: next
|
|
6377
6483
|
});
|
|
6378
6484
|
for (const peer of this.sims.values()) {
|
|
6379
|
-
if (peer.ws.readyState !==
|
|
6485
|
+
if (peer.ws.readyState !== import_ws.WebSocket.OPEN) continue;
|
|
6380
6486
|
try {
|
|
6381
6487
|
peer.ws.send(payload);
|
|
6382
6488
|
} catch {
|
|
@@ -6386,7 +6492,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6386
6492
|
}
|
|
6387
6493
|
broadcastSimClientStates() {
|
|
6388
6494
|
for (const sim of this.sims.values()) {
|
|
6389
|
-
if (sim.ws.readyState !==
|
|
6495
|
+
if (sim.ws.readyState !== import_ws.WebSocket.OPEN) continue;
|
|
6390
6496
|
const lease = this.getActiveLease(sim);
|
|
6391
6497
|
const message = {
|
|
6392
6498
|
type: "bridge:client-state",
|
|
@@ -6457,7 +6563,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6457
6563
|
const keys = /* @__PURE__ */ new Set();
|
|
6458
6564
|
for (const [ws, attachedSimId] of this.cliSimBySocket) {
|
|
6459
6565
|
if (attachedSimId !== simId) continue;
|
|
6460
|
-
if (ws.readyState !==
|
|
6566
|
+
if (ws.readyState !== import_ws.WebSocket.OPEN) continue;
|
|
6461
6567
|
const key = this.cliIdentityKeyBySocket.get(ws);
|
|
6462
6568
|
keys.add(key ?? `ws-unknown-${keys.size}`);
|
|
6463
6569
|
}
|
|
@@ -6470,7 +6576,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6470
6576
|
const keys = /* @__PURE__ */ new Set();
|
|
6471
6577
|
for (const [ws, attachedSimId] of this.cliSimBySocket) {
|
|
6472
6578
|
if (attachedSimId !== simId) continue;
|
|
6473
|
-
if (ws.readyState !==
|
|
6579
|
+
if (ws.readyState !== import_ws.WebSocket.OPEN) continue;
|
|
6474
6580
|
const key = this.cliIdentityKeyBySocket.get(ws);
|
|
6475
6581
|
if (key && key === selfKey) continue;
|
|
6476
6582
|
keys.add(key ?? `ws-unknown-${keys.size}`);
|
|
@@ -6495,7 +6601,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
6495
6601
|
const nextId = requestedId?.trim();
|
|
6496
6602
|
if (!nextId || nextId === sim.id) return false;
|
|
6497
6603
|
const existing = this.sims.get(nextId);
|
|
6498
|
-
if (existing && existing !== sim && existing.ws.readyState ===
|
|
6604
|
+
if (existing && existing !== sim && existing.ws.readyState === import_ws.WebSocket.OPEN) {
|
|
6499
6605
|
return false;
|
|
6500
6606
|
}
|
|
6501
6607
|
const restorable = this.getRestorableSimState(nextId);
|