peachsim 0.1.626 → 0.1.628
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/LICENSE +1 -1
- package/README.md +3 -2
- package/cli/bin.ts +6 -2
- package/cli/bridge-flow-runner.ts +79 -72
- package/cli/commands/box/bundle-plane.ts +7 -2
- package/cli/commands/box.ts +9 -2
- package/cli/commands/cleanup.ts +6 -6
- package/cli/commands/control.ts +36 -6
- package/cli/commands/detox.ts +25 -11
- package/cli/commands/flow.ts +20 -8
- package/cli/commands/inspect.ts +22 -37
- package/cli/commands/maestro-generate.ts +5 -12
- package/cli/commands/maestro.ts +16 -6
- package/cli/commands/mode.ts +11 -7
- package/cli/commands/platform.ts +1 -0
- package/cli/commands/report-issue.ts +1 -1
- package/cli/commands/setup.ts +16 -11
- package/cli/commands/show-touches-mode.ts +33 -0
- package/cli/commands/skills.ts +16 -0
- package/cli/commands/test.ts +22 -1
- package/cli/commands/upload.ts +18 -8
- package/cli/commands/welcome.ts +3 -3
- package/cli/drivers/playwright-sim-host.ts +16 -8
- package/cli/drivers/playwright.ts +8 -5
- package/cli/help-core.ts +9 -4
- package/cli/internal-child.ts +14 -2
- package/cli/levenshtein.ts +27 -0
- package/cli/main.ts +28 -18
- package/cli/run-rnx.ts +8 -4
- package/cli/ws-bridge.ts +3 -2
- package/detox/index.ts +234 -7
- package/dist-lib/agent-daemon-client.cjs +2 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- 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 +4 -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 +18 -8
- package/dist-lib/capture-contract.mjs +18 -8
- 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-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.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 +170 -13
- package/dist-lib/dev-bundle-resolution.cjs +2 -2
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +280 -138
- 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 +1 -1
- package/dist-lib/index.cjs +62 -12
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +7 -1
- package/dist-lib/menu.mjs +7 -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 +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +43 -4
- 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 +215 -28
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +431 -276
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +14512 -14208
- package/dist-lib/vite-kotlin.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +44 -4
- package/install.mjs +16 -0
- package/install.sh +199 -0
- package/package.json +6 -1
- package/scripts/dev-server-scanner.ts +77 -46
- package/skills/contrast/SKILL.md +1 -1
- package/src/bridge-constants.ts +11 -0
- package/src/capture-contract.ts +40 -7
- package/src/dev-bundle-resolution.ts +1 -1
- package/src/host/bridge-host.ts +26 -137
- package/src/menu.ts +7 -0
- package/src/native-seam-manifest.ts +4 -16
- package/src/runtime-assets.ts +59 -2
- package/src/vite-plugin.ts +723 -25
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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;
|
|
@@ -587,8 +587,8 @@ var init_shared_session = __esm({
|
|
|
587
587
|
});
|
|
588
588
|
|
|
589
589
|
// cli/run-rnx.ts
|
|
590
|
-
function peachExit(code) {
|
|
591
|
-
throw new PeachExit(code);
|
|
590
|
+
function peachExit(code, reason) {
|
|
591
|
+
throw new PeachExit(code, reason);
|
|
592
592
|
}
|
|
593
593
|
var import_node_async_hooks, PeachExit, sinks, rnxExit;
|
|
594
594
|
var init_run_rnx = __esm({
|
|
@@ -597,10 +597,12 @@ var init_run_rnx = __esm({
|
|
|
597
597
|
import_node_async_hooks = require("node:async_hooks");
|
|
598
598
|
PeachExit = class extends Error {
|
|
599
599
|
code;
|
|
600
|
-
|
|
600
|
+
reason;
|
|
601
|
+
constructor(code, reason = null) {
|
|
601
602
|
super(`peach exited with code ${code}`);
|
|
602
603
|
this.name = "PeachExit";
|
|
603
604
|
this.code = code;
|
|
605
|
+
this.reason = reason;
|
|
604
606
|
}
|
|
605
607
|
};
|
|
606
608
|
sinks = new import_node_async_hooks.AsyncLocalStorage();
|
|
@@ -3253,6 +3255,8 @@ var init_metro_asset_path = __esm({
|
|
|
3253
3255
|
function matchesModuleEntry(modulePath, pattern) {
|
|
3254
3256
|
if (!modulePath || !pattern) return false;
|
|
3255
3257
|
if (modulePath === pattern) return true;
|
|
3258
|
+
if (!modulePath.includes(pattern)) return false;
|
|
3259
|
+
if (!modulePath.includes(`node_modules/${pattern}`)) return false;
|
|
3256
3260
|
if (modulePath === `node_modules/${pattern}`) return true;
|
|
3257
3261
|
if (modulePath === `node_modules/${pattern}.js`) return true;
|
|
3258
3262
|
if (modulePath === `node_modules/${pattern}.ts`) return true;
|
|
@@ -4675,6 +4679,161 @@ var import_ws = require("ws");
|
|
|
4675
4679
|
|
|
4676
4680
|
// scripts/dev-server-scanner.ts
|
|
4677
4681
|
var import_child_process = require("child_process");
|
|
4682
|
+
|
|
4683
|
+
// ../contrast/src/runtime/process-group.ts
|
|
4684
|
+
var import_node_child_process = require("node:child_process");
|
|
4685
|
+
|
|
4686
|
+
// ../contrast/src/sleep.ts
|
|
4687
|
+
function sleep(ms) {
|
|
4688
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
// ../contrast/src/runtime/process-group.ts
|
|
4692
|
+
var GRACEFUL_TIMEOUT_MS = 3e3;
|
|
4693
|
+
var FORCED_EXIT_TIMEOUT_MS = 1e3;
|
|
4694
|
+
function signalProcessGroup(child, signal) {
|
|
4695
|
+
if (!child.pid) return;
|
|
4696
|
+
try {
|
|
4697
|
+
process.kill(-child.pid, signal);
|
|
4698
|
+
} catch {
|
|
4699
|
+
try {
|
|
4700
|
+
process.kill(child.pid, signal);
|
|
4701
|
+
} catch {
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
function runningPids(pids) {
|
|
4706
|
+
return pids.filter((pid) => {
|
|
4707
|
+
try {
|
|
4708
|
+
process.kill(pid, 0);
|
|
4709
|
+
return true;
|
|
4710
|
+
} catch (error) {
|
|
4711
|
+
return error instanceof Error && "code" in error && error.code === "EPERM";
|
|
4712
|
+
}
|
|
4713
|
+
});
|
|
4714
|
+
}
|
|
4715
|
+
function groupIsEmpty(pgid) {
|
|
4716
|
+
try {
|
|
4717
|
+
process.kill(-pgid, 0);
|
|
4718
|
+
} catch (error) {
|
|
4719
|
+
if (!(error instanceof Error && "code" in error && error.code === "EPERM"))
|
|
4720
|
+
return true;
|
|
4721
|
+
}
|
|
4722
|
+
const result = (0, import_node_child_process.spawnSync)("ps", ["-A", "-o", "pgid=,stat="], {
|
|
4723
|
+
encoding: "utf8",
|
|
4724
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4725
|
+
});
|
|
4726
|
+
if (result.error || result.status !== 0) return false;
|
|
4727
|
+
return !result.stdout.split("\n").some((line) => {
|
|
4728
|
+
const [group, state] = line.trim().split(/\s+/);
|
|
4729
|
+
return Number(group) === pgid && state !== void 0 && !state.startsWith("Z");
|
|
4730
|
+
});
|
|
4731
|
+
}
|
|
4732
|
+
async function waitForExit(pids, pgid, timeoutMs) {
|
|
4733
|
+
const deadline = Date.now() + timeoutMs;
|
|
4734
|
+
const gone = () => runningPids(pids).length === 0 && groupIsEmpty(pgid);
|
|
4735
|
+
while (Date.now() < deadline) {
|
|
4736
|
+
if (gone()) return true;
|
|
4737
|
+
await sleep(50);
|
|
4738
|
+
}
|
|
4739
|
+
return gone();
|
|
4740
|
+
}
|
|
4741
|
+
async function terminateProcessGroup(child, signal = "SIGTERM", gracefulTimeoutMs = GRACEFUL_TIMEOUT_MS) {
|
|
4742
|
+
if (!child.pid) {
|
|
4743
|
+
try {
|
|
4744
|
+
child.kill(signal);
|
|
4745
|
+
} catch {
|
|
4746
|
+
}
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
signalProcessGroup(child, "SIGSTOP");
|
|
4750
|
+
const descendants = /* @__PURE__ */ new Set();
|
|
4751
|
+
let parents = [child.pid];
|
|
4752
|
+
let inspectionError = null;
|
|
4753
|
+
while (parents.length > 0) {
|
|
4754
|
+
const result = (0, import_node_child_process.spawnSync)("pgrep", ["-P", parents.join(",")], {
|
|
4755
|
+
encoding: "utf8",
|
|
4756
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4757
|
+
});
|
|
4758
|
+
if (result.error || result.status !== 0 && result.status !== 1) {
|
|
4759
|
+
inspectionError = new Error(
|
|
4760
|
+
`could not inspect descendants of processes ${parents.join(",")}: ${result.error?.message || result.stderr.trim() || `pgrep exited ${result.status}`}`
|
|
4761
|
+
);
|
|
4762
|
+
break;
|
|
4763
|
+
}
|
|
4764
|
+
const next = [];
|
|
4765
|
+
for (const line of result.stdout.split("\n")) {
|
|
4766
|
+
const descendantPid = Number(line.trim());
|
|
4767
|
+
if (Number.isInteger(descendantPid) && descendantPid > 1 && !descendants.has(descendantPid)) {
|
|
4768
|
+
descendants.add(descendantPid);
|
|
4769
|
+
next.push(descendantPid);
|
|
4770
|
+
try {
|
|
4771
|
+
process.kill(descendantPid, "SIGSTOP");
|
|
4772
|
+
} catch {
|
|
4773
|
+
}
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
parents = next;
|
|
4777
|
+
}
|
|
4778
|
+
const pids = [child.pid, ...descendants];
|
|
4779
|
+
const gracefulDeadline = Date.now() + gracefulTimeoutMs;
|
|
4780
|
+
try {
|
|
4781
|
+
process.kill(child.pid, signal);
|
|
4782
|
+
} catch {
|
|
4783
|
+
}
|
|
4784
|
+
signalProcessGroup(child, "SIGCONT");
|
|
4785
|
+
for (const pid of descendants) {
|
|
4786
|
+
try {
|
|
4787
|
+
process.kill(pid, "SIGCONT");
|
|
4788
|
+
} catch {
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4791
|
+
let rootExited = child.exitCode !== null || child.signalCode !== null;
|
|
4792
|
+
if (!rootExited) {
|
|
4793
|
+
rootExited = await new Promise((resolveExit) => {
|
|
4794
|
+
const onExit = () => {
|
|
4795
|
+
clearTimeout(deadlineTimer);
|
|
4796
|
+
resolveExit(true);
|
|
4797
|
+
};
|
|
4798
|
+
const deadlineTimer = setTimeout(
|
|
4799
|
+
() => {
|
|
4800
|
+
child.removeListener("exit", onExit);
|
|
4801
|
+
resolveExit(false);
|
|
4802
|
+
},
|
|
4803
|
+
Math.max(0, gracefulDeadline - Date.now())
|
|
4804
|
+
);
|
|
4805
|
+
child.once("exit", onExit);
|
|
4806
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
4807
|
+
child.removeListener("exit", onExit);
|
|
4808
|
+
onExit();
|
|
4809
|
+
}
|
|
4810
|
+
});
|
|
4811
|
+
}
|
|
4812
|
+
if (rootExited) {
|
|
4813
|
+
signalProcessGroup(child, signal);
|
|
4814
|
+
for (const pid of descendants) {
|
|
4815
|
+
try {
|
|
4816
|
+
process.kill(pid, signal);
|
|
4817
|
+
} catch {
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
if (await waitForExit(pids, child.pid, Math.max(0, gracefulDeadline - Date.now()))) {
|
|
4822
|
+
if (inspectionError) throw inspectionError;
|
|
4823
|
+
return;
|
|
4824
|
+
}
|
|
4825
|
+
for (const pid of pids) {
|
|
4826
|
+
try {
|
|
4827
|
+
process.kill(pid, "SIGKILL");
|
|
4828
|
+
} catch {
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
signalProcessGroup(child, "SIGKILL");
|
|
4832
|
+
await waitForExit(pids, child.pid, FORCED_EXIT_TIMEOUT_MS);
|
|
4833
|
+
if (inspectionError) throw inspectionError;
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
// scripts/dev-server-scanner.ts
|
|
4678
4837
|
var import_promises2 = require("fs/promises");
|
|
4679
4838
|
var import_http = __toESM(require("http"), 1);
|
|
4680
4839
|
var import_net = __toESM(require("net"), 1);
|
|
@@ -5088,29 +5247,51 @@ async function readAppSettingsBundle(projectRoot) {
|
|
|
5088
5247
|
|
|
5089
5248
|
// scripts/dev-server-scanner.ts
|
|
5090
5249
|
init_optional_demo_registry();
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5250
|
+
function execGuarded(command, timeout, extra = {}) {
|
|
5251
|
+
const maxBuffer = extra.maxBuffer ?? 1024 * 1024;
|
|
5252
|
+
return new Promise((resolve5, reject) => {
|
|
5253
|
+
const child = (0, import_child_process.spawn)("/bin/sh", ["-c", command], {
|
|
5254
|
+
detached: true,
|
|
5255
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5256
|
+
});
|
|
5257
|
+
let stdout = "";
|
|
5258
|
+
let settled = false;
|
|
5259
|
+
let killing = false;
|
|
5260
|
+
const settle = (err) => {
|
|
5261
|
+
if (settled) return;
|
|
5262
|
+
settled = true;
|
|
5263
|
+
clearTimeout(deadline);
|
|
5096
5264
|
if (err) reject(err);
|
|
5097
5265
|
else resolve5({ stdout });
|
|
5266
|
+
};
|
|
5267
|
+
const killThenSettle = (err) => {
|
|
5268
|
+
if (killing) return;
|
|
5269
|
+
killing = true;
|
|
5270
|
+
clearTimeout(deadline);
|
|
5271
|
+
terminateProcessGroup(child, "SIGKILL", 0).finally(() => settle(err));
|
|
5272
|
+
};
|
|
5273
|
+
const deadline = setTimeout(() => {
|
|
5274
|
+
killThenSettle(
|
|
5275
|
+
Object.assign(new Error(`\`${command}\` did not exit within ${timeout}ms`), {
|
|
5276
|
+
killed: true
|
|
5277
|
+
})
|
|
5278
|
+
);
|
|
5279
|
+
}, timeout);
|
|
5280
|
+
child.stdout.setEncoding("utf8");
|
|
5281
|
+
child.stdout.on("data", (chunk) => {
|
|
5282
|
+
stdout += chunk;
|
|
5283
|
+
if (stdout.length > maxBuffer) {
|
|
5284
|
+
killThenSettle(new Error(`\`${command}\` exceeded maxBuffer ${maxBuffer}`));
|
|
5285
|
+
}
|
|
5286
|
+
});
|
|
5287
|
+
child.on("error", (err) => {
|
|
5288
|
+
if (!killing) settle(err);
|
|
5289
|
+
});
|
|
5290
|
+
child.on("close", (code) => {
|
|
5291
|
+
if (killing) return;
|
|
5292
|
+
settle(code === 0 ? null : new Error(`\`${command}\` exited with ${code}`));
|
|
5098
5293
|
});
|
|
5099
5294
|
});
|
|
5100
|
-
let deadlineTimer;
|
|
5101
|
-
const deadline = new Promise((_resolve, reject) => {
|
|
5102
|
-
deadlineTimer = setTimeout(() => {
|
|
5103
|
-
child?.kill("SIGKILL");
|
|
5104
|
-
const err = new Error(`\`${command}\` did not exit within ${timeout}ms`);
|
|
5105
|
-
Object.assign(err, { killed: true });
|
|
5106
|
-
reject(err);
|
|
5107
|
-
}, timeout + EXEC_EXIT_GRACE_MS);
|
|
5108
|
-
});
|
|
5109
|
-
try {
|
|
5110
|
-
return await Promise.race([ran, deadline]);
|
|
5111
|
-
} finally {
|
|
5112
|
-
clearTimeout(deadlineTimer);
|
|
5113
|
-
}
|
|
5114
5295
|
}
|
|
5115
5296
|
var TIMEOUT_MS = 250;
|
|
5116
5297
|
var MANIFEST_TIMEOUT_MS = 1500;
|
|
@@ -5145,6 +5326,7 @@ async function tcpPing(port, timeout = TCP_GATE_MS) {
|
|
|
5145
5326
|
function httpGet(hostname, port, path12, method = "GET", timeout = TIMEOUT_MS, headers = {}) {
|
|
5146
5327
|
return new Promise((resolve5) => {
|
|
5147
5328
|
let settled = false;
|
|
5329
|
+
let killing = false;
|
|
5148
5330
|
const settle = (value) => {
|
|
5149
5331
|
if (settled) return;
|
|
5150
5332
|
settled = true;
|
|
@@ -5219,6 +5401,21 @@ function wasKilledByTimeout(err) {
|
|
|
5219
5401
|
}
|
|
5220
5402
|
async function scanListeningProcesses(excludePorts = []) {
|
|
5221
5403
|
const excluded = new Set(excludePorts);
|
|
5404
|
+
const listing = await listListenersShared();
|
|
5405
|
+
if (listing.state === "could-not-look") return listing;
|
|
5406
|
+
return {
|
|
5407
|
+
state: "looked",
|
|
5408
|
+
processes: listing.processes.filter((p) => acceptPort(p.port, excluded))
|
|
5409
|
+
};
|
|
5410
|
+
}
|
|
5411
|
+
var inflightListing = null;
|
|
5412
|
+
function listListenersShared() {
|
|
5413
|
+
inflightListing ??= listListeners().finally(() => {
|
|
5414
|
+
inflightListing = null;
|
|
5415
|
+
});
|
|
5416
|
+
return inflightListing;
|
|
5417
|
+
}
|
|
5418
|
+
async function listListeners() {
|
|
5222
5419
|
let timedOut = false;
|
|
5223
5420
|
try {
|
|
5224
5421
|
const { stdout } = await execGuarded(
|
|
@@ -5230,11 +5427,9 @@ async function scanListeningProcesses(excludePorts = []) {
|
|
|
5230
5427
|
const parts = line.trim().split(/\s+/);
|
|
5231
5428
|
if (parts.length < 9) continue;
|
|
5232
5429
|
const pid = Number(parts[1]);
|
|
5233
|
-
const
|
|
5234
|
-
const m = addr.match(/:(\d+)$/);
|
|
5430
|
+
const m = parts[8].match(/:(\d+)$/);
|
|
5235
5431
|
if (!m) continue;
|
|
5236
5432
|
const port = Number(m[1]);
|
|
5237
|
-
if (!acceptPort(port, excluded)) continue;
|
|
5238
5433
|
if (!seen.has(port)) seen.set(port, pid);
|
|
5239
5434
|
}
|
|
5240
5435
|
return {
|
|
@@ -5256,7 +5451,6 @@ async function scanListeningProcesses(excludePorts = []) {
|
|
|
5256
5451
|
if (!portMatch) continue;
|
|
5257
5452
|
const port = Number(portMatch[1]);
|
|
5258
5453
|
const pid = pidMatch ? Number(pidMatch[1]) : 0;
|
|
5259
|
-
if (!acceptPort(port, excluded)) continue;
|
|
5260
5454
|
if (!seen.has(port)) seen.set(port, pid);
|
|
5261
5455
|
}
|
|
5262
5456
|
return {
|
|
@@ -5267,10 +5461,7 @@ async function scanListeningProcesses(excludePorts = []) {
|
|
|
5267
5461
|
if (wasKilledByTimeout(err)) timedOut = true;
|
|
5268
5462
|
}
|
|
5269
5463
|
if (timedOut) return { state: "could-not-look" };
|
|
5270
|
-
return {
|
|
5271
|
-
state: "looked",
|
|
5272
|
-
processes: FALLBACK_PORTS.filter((p) => acceptPort(p.port, excluded))
|
|
5273
|
-
};
|
|
5464
|
+
return { state: "looked", processes: FALLBACK_PORTS };
|
|
5274
5465
|
}
|
|
5275
5466
|
function pidFromNetstat(stdout, port) {
|
|
5276
5467
|
for (const line of stdout.split("\n")) {
|
|
@@ -5776,6 +5967,7 @@ var DEFAULT_SOOTSIM_BRIDGE_PORT = 7668;
|
|
|
5776
5967
|
var DEFAULT_SOOTSIM_BRIDGE_URL = `http://localhost:${DEFAULT_SOOTSIM_BRIDGE_PORT}/`;
|
|
5777
5968
|
var SOOTSIM_BRIDGE_SIM_CLOSE_CODE = 4001;
|
|
5778
5969
|
var SOOTSIM_BRIDGE_SIM_CLOSE_REASON = "sootsim close";
|
|
5970
|
+
var AUTOMATION_SIM_IDLE_REAP_TTL_MS = 5 * 6e4;
|
|
5779
5971
|
|
|
5780
5972
|
// src/cli-version.ts
|
|
5781
5973
|
var import_node_fs3 = require("node:fs");
|
|
@@ -5817,7 +6009,7 @@ var import_path = __toESM(require("path"), 1);
|
|
|
5817
6009
|
var import_url = require("url");
|
|
5818
6010
|
|
|
5819
6011
|
// ../contrast-runtime-delivery/src/index.ts
|
|
5820
|
-
var
|
|
6012
|
+
var import_node_child_process2 = require("node:child_process");
|
|
5821
6013
|
var import_node_crypto2 = __toESM(require("node:crypto"), 1);
|
|
5822
6014
|
var import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
5823
6015
|
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
@@ -5969,10 +6161,12 @@ function createRuntimeDelivery(product) {
|
|
|
5969
6161
|
);
|
|
5970
6162
|
import_node_fs6.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
5971
6163
|
try {
|
|
5972
|
-
|
|
6164
|
+
if (!opts.quiet)
|
|
6165
|
+
process.stderr.write(`${product.id}: downloading runtime ${version}\u2026
|
|
5973
6166
|
`);
|
|
5974
6167
|
await downloadToFile(tarUrl, runtimeArchivePath);
|
|
5975
|
-
|
|
6168
|
+
if (!opts.quiet)
|
|
6169
|
+
process.stderr.write(`${product.id}: extracting runtime ${version}\u2026
|
|
5976
6170
|
`);
|
|
5977
6171
|
const actualSha = await sha256File(runtimeArchivePath);
|
|
5978
6172
|
if (actualSha !== entry.sha256) {
|
|
@@ -6216,7 +6410,7 @@ function sha256File(filePath) {
|
|
|
6216
6410
|
}
|
|
6217
6411
|
function extractTarball(tarPath, destDir) {
|
|
6218
6412
|
return new Promise((resolve5, reject) => {
|
|
6219
|
-
const child = (0,
|
|
6413
|
+
const child = (0, import_node_child_process2.spawn)("tar", ["-xzf", tarPath, "-C", destDir], {
|
|
6220
6414
|
stdio: ["ignore", "inherit", "inherit"]
|
|
6221
6415
|
});
|
|
6222
6416
|
child.on("error", reject);
|
|
@@ -6291,6 +6485,41 @@ function resolveRuntimeRootForHostname(hostname) {
|
|
|
6291
6485
|
const version = runtimeVersionFromHostname(hostname);
|
|
6292
6486
|
return version ? resolveInstalledRuntimeRoot(version) : null;
|
|
6293
6487
|
}
|
|
6488
|
+
function sendFileWithRanges(req, res, filePath, total, headers) {
|
|
6489
|
+
const head = (req?.method || "GET").toUpperCase() === "HEAD";
|
|
6490
|
+
const range = /^bytes=(\d*)-(\d*)$/.exec(req?.headers?.range || "");
|
|
6491
|
+
const invalid = range && range[1] && range[2] && Number(range[2]) < Number(range[1]);
|
|
6492
|
+
if (range && (range[1] || range[2]) && !invalid) {
|
|
6493
|
+
const [, startRaw, endRaw] = range;
|
|
6494
|
+
const start = startRaw ? Number(startRaw) : Math.max(0, total - Number(endRaw));
|
|
6495
|
+
const end = startRaw && endRaw ? Math.min(Number(endRaw), total - 1) : total - 1;
|
|
6496
|
+
if (start > end || start >= total) {
|
|
6497
|
+
res.writeHead(416, { ...headers, "Content-Range": `bytes */${total}` });
|
|
6498
|
+
res.end();
|
|
6499
|
+
return;
|
|
6500
|
+
}
|
|
6501
|
+
res.writeHead(206, {
|
|
6502
|
+
...headers,
|
|
6503
|
+
"Content-Range": `bytes ${start}-${end}/${total}`,
|
|
6504
|
+
"Accept-Ranges": "bytes",
|
|
6505
|
+
"Content-Length": String(end - start + 1)
|
|
6506
|
+
});
|
|
6507
|
+
if (head) res.end();
|
|
6508
|
+
else streamFile(res, import_fs.default.createReadStream(filePath, { start, end }));
|
|
6509
|
+
return;
|
|
6510
|
+
}
|
|
6511
|
+
res.writeHead(200, {
|
|
6512
|
+
...headers,
|
|
6513
|
+
"Accept-Ranges": "bytes",
|
|
6514
|
+
"Content-Length": String(total)
|
|
6515
|
+
});
|
|
6516
|
+
if (head || total === 0) res.end();
|
|
6517
|
+
else streamFile(res, import_fs.default.createReadStream(filePath, { start: 0, end: total - 1 }));
|
|
6518
|
+
}
|
|
6519
|
+
function streamFile(res, stream) {
|
|
6520
|
+
stream.on("error", (error) => res.destroy(error));
|
|
6521
|
+
stream.pipe(res);
|
|
6522
|
+
}
|
|
6294
6523
|
|
|
6295
6524
|
// src/runtime-delivery.ts
|
|
6296
6525
|
init_home_paths();
|
|
@@ -6326,7 +6555,7 @@ var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
|
6326
6555
|
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
6327
6556
|
|
|
6328
6557
|
// src/agent-sessions.ts
|
|
6329
|
-
var
|
|
6558
|
+
var import_node_child_process3 = require("node:child_process");
|
|
6330
6559
|
var import_node_crypto4 = require("node:crypto");
|
|
6331
6560
|
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
6332
6561
|
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
@@ -6880,7 +7109,7 @@ function mkfifoSync(p) {
|
|
|
6880
7109
|
import_node_fs9.default.unlinkSync(p);
|
|
6881
7110
|
}
|
|
6882
7111
|
}
|
|
6883
|
-
const result = (0,
|
|
7112
|
+
const result = (0, import_node_child_process3.spawnSync)("mkfifo", ["-m", "600", p]);
|
|
6884
7113
|
if (result.status !== 0) {
|
|
6885
7114
|
throw new Error(
|
|
6886
7115
|
`mkfifo(${p}) failed: ${result.stderr?.toString().trim() || "unknown error"}`
|
|
@@ -6955,7 +7184,7 @@ async function startSession(opts) {
|
|
|
6955
7184
|
if (claudeSessionUuid) {
|
|
6956
7185
|
wrapperArgs.push("--claude-session-uuid", claudeSessionUuid);
|
|
6957
7186
|
}
|
|
6958
|
-
const child = (0,
|
|
7187
|
+
const child = (0, import_node_child_process3.spawn)(executable, wrapperArgs, {
|
|
6959
7188
|
detached: true,
|
|
6960
7189
|
stdio: "ignore",
|
|
6961
7190
|
env: {
|
|
@@ -8167,7 +8396,7 @@ async function dispatchPlaneCommand(plane, type, msg, mutationOptions) {
|
|
|
8167
8396
|
}
|
|
8168
8397
|
|
|
8169
8398
|
// cli/commands/box/checkout-plane.ts
|
|
8170
|
-
var
|
|
8399
|
+
var import_node_child_process4 = require("node:child_process");
|
|
8171
8400
|
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
8172
8401
|
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
8173
8402
|
var CheckoutRequiresGitError = class extends Error {
|
|
@@ -8209,7 +8438,7 @@ var CheckoutFilePlane = class {
|
|
|
8209
8438
|
}
|
|
8210
8439
|
listFiles() {
|
|
8211
8440
|
const args = this.trackedOnly ? ["ls-files", "-z"] : ["ls-files", "-co", "--exclude-standard", "-z"];
|
|
8212
|
-
const stdout = (0,
|
|
8441
|
+
const stdout = (0, import_node_child_process4.execFileSync)("git", args, {
|
|
8213
8442
|
cwd: this.root,
|
|
8214
8443
|
encoding: "utf8",
|
|
8215
8444
|
maxBuffer: 64 * 1024 * 1024,
|
|
@@ -9250,24 +9479,8 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
9250
9479
|
// terminal 4001 code, so the client closes the abandoned window instead
|
|
9251
9480
|
// of reconnecting the zombie straight back in.
|
|
9252
9481
|
static SIM_IDLE_REAP_TTL_MS = 30 * 6e4;
|
|
9253
|
-
// automation sims (spawned by the playwright driver: `peach open --driver
|
|
9254
|
-
// playwright`, the factory/screenshot capture path — `meta.sootsimHostDriver
|
|
9255
|
-
// === 'playwright'`) are unattended one-shot browsers, never a human's tab. a
|
|
9256
|
-
// capture run opens one per slide/app/retry and the 30-min human/dev TTL lets
|
|
9257
|
-
// hundreds pile up inside the window — orphaned ms-playwright chromiums that
|
|
9258
|
-
// drove the box to swap-thrash (load hit 157 in one incident). give them a far
|
|
9259
|
-
// shorter idle TTL, and cap how many can sit concurrently: reap the most-idle
|
|
9260
|
-
// overflow, but never a sim active within ACTIVE_GRACE (an agent composing
|
|
9261
|
-
// between captures), nor primary / cli-attached / leased sims.
|
|
9262
|
-
static AUTOMATION_SIM_IDLE_REAP_TTL_MS = 10 * 6e4;
|
|
9263
|
-
static MAX_CONCURRENT_AUTOMATION_SIMS = 6;
|
|
9264
|
-
static AUTOMATION_SIM_ACTIVE_GRACE_MS = 6e4;
|
|
9265
9482
|
preferredPort;
|
|
9266
9483
|
portFallbackCount;
|
|
9267
|
-
simIdleReapTtlMs;
|
|
9268
|
-
automationSimIdleReapTtlMs;
|
|
9269
|
-
maxConcurrentAutomationSims;
|
|
9270
|
-
automationSimActiveGraceMs;
|
|
9271
9484
|
shouldWriteLockfile;
|
|
9272
9485
|
shouldWriteDevLockfile;
|
|
9273
9486
|
getShellPort;
|
|
@@ -9310,23 +9523,12 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
9310
9523
|
getExcludePorts: opts.agentScanExcludes
|
|
9311
9524
|
});
|
|
9312
9525
|
this.contrastOrigin = opts.contrastOrigin?.replace(/\/$/, "") || null;
|
|
9313
|
-
this.simIdleReapTtlMs = opts.simIdleReapTtlMs ?? _SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS;
|
|
9314
|
-
this.automationSimIdleReapTtlMs = opts.automationSimIdleReapTtlMs ?? _SootSimBridgeHost.AUTOMATION_SIM_IDLE_REAP_TTL_MS;
|
|
9315
|
-
this.maxConcurrentAutomationSims = opts.maxConcurrentAutomationSims ?? _SootSimBridgeHost.MAX_CONCURRENT_AUTOMATION_SIMS;
|
|
9316
|
-
this.automationSimActiveGraceMs = opts.automationSimActiveGraceMs ?? _SootSimBridgeHost.AUTOMATION_SIM_ACTIVE_GRACE_MS;
|
|
9317
9526
|
}
|
|
9318
9527
|
/** expose the agent host so tests and embedders can inspect state or
|
|
9319
9528
|
* inject behavior. not part of the public WS protocol. */
|
|
9320
9529
|
getAgentHost() {
|
|
9321
9530
|
return this.agentHost;
|
|
9322
9531
|
}
|
|
9323
|
-
/** run the abandoned-sim GC pass on demand. the reaper normally fires off
|
|
9324
|
-
* the 30s idle-sweep timer; this lets the F21-3 integration test drive
|
|
9325
|
-
* the real path without waiting for the timer. not part of the public
|
|
9326
|
-
* WS protocol. */
|
|
9327
|
-
reapIdleSimsForTest(now = Date.now()) {
|
|
9328
|
-
this.reapIdleSims(now);
|
|
9329
|
-
}
|
|
9330
9532
|
/** synchronous wrapper around startAsync for callers that don't care
|
|
9331
9533
|
* about port fallback outcomes. returns immediately; actual binding
|
|
9332
9534
|
* happens on the event loop. callers that need to know the bound port
|
|
@@ -9572,6 +9774,10 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
9572
9774
|
this.updateUserActivity(sim);
|
|
9573
9775
|
return;
|
|
9574
9776
|
}
|
|
9777
|
+
if (msg.type === "bridge:capture-active" && sim) {
|
|
9778
|
+
sim.lastActiveAt = Date.now();
|
|
9779
|
+
return;
|
|
9780
|
+
}
|
|
9575
9781
|
if (msg.type === "bridge:write-shared-config") {
|
|
9576
9782
|
if (!msg.patch || typeof msg.patch !== "object" || Array.isArray(msg.patch)) {
|
|
9577
9783
|
return;
|
|
@@ -10020,42 +10226,10 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
10020
10226
|
return;
|
|
10021
10227
|
}
|
|
10022
10228
|
const contentType = HTTP_MIME_TYPES[import_path3.default.extname(filePath).toLowerCase()] || "application/octet-stream";
|
|
10023
|
-
|
|
10024
|
-
const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range || "");
|
|
10025
|
-
if (range) {
|
|
10026
|
-
const [, startRaw, endRaw] = range;
|
|
10027
|
-
const start = startRaw ? Number(startRaw) : 0;
|
|
10028
|
-
const end = endRaw ? Math.min(Number(endRaw), total - 1) : total - 1;
|
|
10029
|
-
if (!Number.isFinite(start) || start > end || start >= total) {
|
|
10030
|
-
res.writeHead(416, { ...cors, "Content-Range": `bytes */${total}` });
|
|
10031
|
-
res.end();
|
|
10032
|
-
return;
|
|
10033
|
-
}
|
|
10034
|
-
res.writeHead(206, {
|
|
10035
|
-
...cors,
|
|
10036
|
-
"Content-Type": contentType,
|
|
10037
|
-
"Content-Range": `bytes ${start}-${end}/${total}`,
|
|
10038
|
-
"Accept-Ranges": "bytes",
|
|
10039
|
-
"Content-Length": String(end - start + 1)
|
|
10040
|
-
});
|
|
10041
|
-
if (method === "HEAD") {
|
|
10042
|
-
res.end();
|
|
10043
|
-
return;
|
|
10044
|
-
}
|
|
10045
|
-
import_fs3.default.createReadStream(filePath, { start, end }).pipe(res);
|
|
10046
|
-
return;
|
|
10047
|
-
}
|
|
10048
|
-
res.writeHead(200, {
|
|
10229
|
+
sendFileWithRanges(req, res, filePath, stats.size, {
|
|
10049
10230
|
...cors,
|
|
10050
|
-
"Content-Type": contentType
|
|
10051
|
-
"Accept-Ranges": "bytes",
|
|
10052
|
-
"Content-Length": String(total)
|
|
10231
|
+
"Content-Type": contentType
|
|
10053
10232
|
});
|
|
10054
|
-
if (method === "HEAD") {
|
|
10055
|
-
res.end();
|
|
10056
|
-
return;
|
|
10057
|
-
}
|
|
10058
|
-
import_fs3.default.createReadStream(filePath).pipe(res);
|
|
10059
10233
|
}
|
|
10060
10234
|
handleServerScan(res) {
|
|
10061
10235
|
const sendJson = (body) => {
|
|
@@ -10572,53 +10746,21 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
|
|
|
10572
10746
|
// automation sims are spawned by the playwright driver (`peach open
|
|
10573
10747
|
// --driver playwright`, the factory/screenshot capture path), which tags them
|
|
10574
10748
|
// with `meta.sootsimHostDriver === 'playwright'` via the host's addInitScript.
|
|
10575
|
-
// they are unattended one-shot browsers, never a human's tab, so they get a
|
|
10576
|
-
// shorter idle TTL and a concurrent cap.
|
|
10577
10749
|
isAutomationSim(sim) {
|
|
10578
10750
|
return sim.meta?.sootsimHostDriver === "playwright";
|
|
10579
10751
|
}
|
|
10580
|
-
// an automation sim whose spawning playwright host process is still alive is
|
|
10581
|
-
// an ACTIVE run, not an abandoned tab. mirrors the cli's playwrightHostPidForSim
|
|
10582
|
-
// (control.ts): extract the host pid from meta, then check liveness so the
|
|
10583
|
-
// host-side idle GC never kills a live capture/factory sim mid-run.
|
|
10584
|
-
automationSimOwnerAlive(sim) {
|
|
10585
|
-
const pid = Number(sim.meta?.sootsimHostPid);
|
|
10586
|
-
if (!Number.isInteger(pid) || pid <= 1 || pid === process.pid) return false;
|
|
10587
|
-
try {
|
|
10588
|
-
process.kill(pid, 0);
|
|
10589
|
-
return true;
|
|
10590
|
-
} catch (err) {
|
|
10591
|
-
return err.code === "EPERM";
|
|
10592
|
-
}
|
|
10593
|
-
}
|
|
10594
10752
|
reapIdleSims(now = Date.now()) {
|
|
10595
10753
|
const attachedSimIds = new Set(this.cliSimBySocket.values());
|
|
10596
|
-
const automationLive = [];
|
|
10597
10754
|
for (const sim of this.sims.values()) {
|
|
10598
|
-
if (sim.id === this.primarySimId) continue;
|
|
10599
10755
|
if (attachedSimIds.has(sim.id)) continue;
|
|
10600
|
-
if (this.getActiveLease(sim)) continue;
|
|
10601
|
-
const lastTouched = Math.max(sim.lastActiveAt, sim.connectedAt);
|
|
10602
|
-
const idleMs = now - lastTouched;
|
|
10603
10756
|
const automation = this.isAutomationSim(sim);
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
if (idleMs >= ttl) {
|
|
10607
|
-
this.closeSimSocketFromHost(sim.ws);
|
|
10757
|
+
const lease = this.getActiveLease(sim);
|
|
10758
|
+
if (automation ? lease?.kind === "user-active" : sim.id === this.primarySimId || lease) {
|
|
10608
10759
|
continue;
|
|
10609
10760
|
}
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
if (automationLive.length > cap) {
|
|
10614
|
-
automationLive.sort((a, b) => b.idleMs - a.idleMs);
|
|
10615
|
-
for (const { sim, idleMs } of automationLive.slice(
|
|
10616
|
-
0,
|
|
10617
|
-
automationLive.length - cap
|
|
10618
|
-
)) {
|
|
10619
|
-
if (idleMs < this.automationSimActiveGraceMs) continue;
|
|
10620
|
-
this.closeSimSocketFromHost(sim.ws);
|
|
10621
|
-
}
|
|
10761
|
+
const idleMs = now - Math.max(sim.lastActiveAt, sim.connectedAt);
|
|
10762
|
+
const ttl = automation ? AUTOMATION_SIM_IDLE_REAP_TTL_MS : _SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS;
|
|
10763
|
+
if (idleMs >= ttl) this.closeSimSocketFromHost(sim.ws);
|
|
10622
10764
|
}
|
|
10623
10765
|
}
|
|
10624
10766
|
// ping every connected ws; if the previous round's ping went unanswered,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
|
|
3
3
|
// src/host/fetch-proxy-overrides.ts
|
|
4
4
|
var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.628 | (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 __defProp = Object.defineProperty;
|