peachsim 0.1.625 → 0.1.627
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/README.md +3 -2
- package/cli/commands/box/bundle-plane.ts +7 -2
- package/cli/commands/box.ts +9 -2
- package/cli/commands/control.ts +32 -1
- package/cli/commands/flow.ts +17 -6
- package/cli/commands/maestro-generate.ts +5 -12
- package/cli/commands/maestro.ts +16 -6
- package/cli/commands/platform.ts +1 -0
- package/cli/commands/test.ts +14 -0
- package/cli/commands/upload.ts +18 -8
- package/cli/drivers/playwright-sim-host.ts +10 -3
- package/cli/drivers/playwright.ts +8 -5
- package/cli/run-rnx.ts +8 -4
- 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 +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +239 -102
- 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 +19 -8
- 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 +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 +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- 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 +215 -28
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +292 -128
- 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 +14488 -14349
- package/dist-lib/vite-kotlin.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +2 -1
- package/install.mjs +16 -0
- package/install.sh +199 -0
- package/package.json +6 -1
- package/scripts/dev-server-scanner.ts +77 -46
- package/src/bridge-constants.ts +11 -0
- package/src/capture-contract.ts +40 -7
- package/src/host/bridge-host.ts +24 -103
package/src/host/bridge-host.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from '../../scripts/dev-server-scanner.ts'
|
|
10
10
|
import { buildBrowserAssetProxyUrl } from '../backend-origin.ts'
|
|
11
11
|
import {
|
|
12
|
+
AUTOMATION_SIM_IDLE_REAP_TTL_MS,
|
|
12
13
|
DEFAULT_SOOTSIM_BRIDGE_PORT,
|
|
13
14
|
SOOTSIM_BRIDGE_SIM_CLOSE_CODE,
|
|
14
15
|
SOOTSIM_BRIDGE_SIM_CLOSE_REASON,
|
|
@@ -260,23 +261,6 @@ export interface BridgeHostOptions {
|
|
|
260
261
|
* `peach serve` command resolves this with the same probe the
|
|
261
262
|
* upload itself uses (`resolveDefaultUploadOrigin`). */
|
|
262
263
|
contrastOrigin?: string
|
|
263
|
-
/** override the abandoned-sim GC TTL (ms). defaults to
|
|
264
|
-
* SIM_IDLE_REAP_TTL_MS (30 min). exists so the reaper integration test
|
|
265
|
-
* can drive the real timer path deterministically instead of mocking
|
|
266
|
-
* time. production callers should never set this. */
|
|
267
|
-
simIdleReapTtlMs?: number
|
|
268
|
-
/** override the idle GC TTL (ms) for automation sims (playwright-driver,
|
|
269
|
-
* capture/factory). defaults to AUTOMATION_SIM_IDLE_REAP_TTL_MS (10 min).
|
|
270
|
-
* test-only, like simIdleReapTtlMs. */
|
|
271
|
-
automationSimIdleReapTtlMs?: number
|
|
272
|
-
/** cap on concurrent non-primary automation sims before the reaper drops
|
|
273
|
-
* the most-idle overflow. defaults to MAX_CONCURRENT_AUTOMATION_SIMS (6).
|
|
274
|
-
* test-only. */
|
|
275
|
-
maxConcurrentAutomationSims?: number
|
|
276
|
-
/** grace (ms) below which an automation sim is never reaped by the cap,
|
|
277
|
-
* even when over it. defaults to AUTOMATION_SIM_ACTIVE_GRACE_MS (60 s).
|
|
278
|
-
* test-only. */
|
|
279
|
-
automationSimActiveGraceMs?: number
|
|
280
264
|
}
|
|
281
265
|
|
|
282
266
|
const DAEMON_HEARTBEAT_INTERVAL_MS = 5_000
|
|
@@ -417,25 +401,9 @@ export class SootSimBridgeHost {
|
|
|
417
401
|
// terminal 4001 code, so the client closes the abandoned window instead
|
|
418
402
|
// of reconnecting the zombie straight back in.
|
|
419
403
|
private static SIM_IDLE_REAP_TTL_MS = 30 * 60_000
|
|
420
|
-
// automation sims (spawned by the playwright driver: `peach open --driver
|
|
421
|
-
// playwright`, the factory/screenshot capture path — `meta.sootsimHostDriver
|
|
422
|
-
// === 'playwright'`) are unattended one-shot browsers, never a human's tab. a
|
|
423
|
-
// capture run opens one per slide/app/retry and the 30-min human/dev TTL lets
|
|
424
|
-
// hundreds pile up inside the window — orphaned ms-playwright chromiums that
|
|
425
|
-
// drove the box to swap-thrash (load hit 157 in one incident). give them a far
|
|
426
|
-
// shorter idle TTL, and cap how many can sit concurrently: reap the most-idle
|
|
427
|
-
// overflow, but never a sim active within ACTIVE_GRACE (an agent composing
|
|
428
|
-
// between captures), nor primary / cli-attached / leased sims.
|
|
429
|
-
private static AUTOMATION_SIM_IDLE_REAP_TTL_MS = 10 * 60_000
|
|
430
|
-
private static MAX_CONCURRENT_AUTOMATION_SIMS = 6
|
|
431
|
-
private static AUTOMATION_SIM_ACTIVE_GRACE_MS = 60_000
|
|
432
404
|
|
|
433
405
|
private preferredPort: number
|
|
434
406
|
private portFallbackCount: number
|
|
435
|
-
private simIdleReapTtlMs: number
|
|
436
|
-
private automationSimIdleReapTtlMs: number
|
|
437
|
-
private maxConcurrentAutomationSims: number
|
|
438
|
-
private automationSimActiveGraceMs: number
|
|
439
407
|
private shouldWriteLockfile: boolean
|
|
440
408
|
private shouldWriteDevLockfile: boolean
|
|
441
409
|
private getShellPort: (() => number | null) | null
|
|
@@ -486,14 +454,6 @@ export class SootSimBridgeHost {
|
|
|
486
454
|
getExcludePorts: opts.agentScanExcludes,
|
|
487
455
|
})
|
|
488
456
|
this.contrastOrigin = opts.contrastOrigin?.replace(/\/$/, '') || null
|
|
489
|
-
this.simIdleReapTtlMs =
|
|
490
|
-
opts.simIdleReapTtlMs ?? SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS
|
|
491
|
-
this.automationSimIdleReapTtlMs =
|
|
492
|
-
opts.automationSimIdleReapTtlMs ?? SootSimBridgeHost.AUTOMATION_SIM_IDLE_REAP_TTL_MS
|
|
493
|
-
this.maxConcurrentAutomationSims =
|
|
494
|
-
opts.maxConcurrentAutomationSims ?? SootSimBridgeHost.MAX_CONCURRENT_AUTOMATION_SIMS
|
|
495
|
-
this.automationSimActiveGraceMs =
|
|
496
|
-
opts.automationSimActiveGraceMs ?? SootSimBridgeHost.AUTOMATION_SIM_ACTIVE_GRACE_MS
|
|
497
457
|
}
|
|
498
458
|
|
|
499
459
|
/** expose the agent host so tests and embedders can inspect state or
|
|
@@ -502,14 +462,6 @@ export class SootSimBridgeHost {
|
|
|
502
462
|
return this.agentHost
|
|
503
463
|
}
|
|
504
464
|
|
|
505
|
-
/** run the abandoned-sim GC pass on demand. the reaper normally fires off
|
|
506
|
-
* the 30s idle-sweep timer; this lets the F21-3 integration test drive
|
|
507
|
-
* the real path without waiting for the timer. not part of the public
|
|
508
|
-
* WS protocol. */
|
|
509
|
-
reapIdleSimsForTest(now = Date.now()): void {
|
|
510
|
-
this.reapIdleSims(now)
|
|
511
|
-
}
|
|
512
|
-
|
|
513
465
|
/** synchronous wrapper around startAsync for callers that don't care
|
|
514
466
|
* about port fallback outcomes. returns immediately; actual binding
|
|
515
467
|
* happens on the event loop. callers that need to know the bound port
|
|
@@ -816,6 +768,13 @@ export class SootSimBridgeHost {
|
|
|
816
768
|
return
|
|
817
769
|
}
|
|
818
770
|
|
|
771
|
+
// a capture the page is still running after its cli exited (`peach
|
|
772
|
+
// record start`) is use the idle reaper cannot see in cli traffic.
|
|
773
|
+
if (msg.type === 'bridge:capture-active' && sim) {
|
|
774
|
+
sim.lastActiveAt = Date.now()
|
|
775
|
+
return
|
|
776
|
+
}
|
|
777
|
+
|
|
819
778
|
// write a partial patch into ~/.peach/config.json. fired by the
|
|
820
779
|
// engine's settingsStore when a persisted key flips in a browser
|
|
821
780
|
// tab (which has no fs access of its own). after the merge, we
|
|
@@ -2025,71 +1984,33 @@ export class SootSimBridgeHost {
|
|
|
2025
1984
|
// automation sims are spawned by the playwright driver (`peach open
|
|
2026
1985
|
// --driver playwright`, the factory/screenshot capture path), which tags them
|
|
2027
1986
|
// with `meta.sootsimHostDriver === 'playwright'` via the host's addInitScript.
|
|
2028
|
-
// they are unattended one-shot browsers, never a human's tab, so they get a
|
|
2029
|
-
// shorter idle TTL and a concurrent cap.
|
|
2030
1987
|
private isAutomationSim(sim: BridgeSimConnection): boolean {
|
|
2031
1988
|
return sim.meta?.sootsimHostDriver === 'playwright'
|
|
2032
1989
|
}
|
|
2033
1990
|
|
|
2034
|
-
// an automation sim whose spawning playwright host process is still alive is
|
|
2035
|
-
// an ACTIVE run, not an abandoned tab. mirrors the cli's playwrightHostPidForSim
|
|
2036
|
-
// (control.ts): extract the host pid from meta, then check liveness so the
|
|
2037
|
-
// host-side idle GC never kills a live capture/factory sim mid-run.
|
|
2038
|
-
private automationSimOwnerAlive(sim: BridgeSimConnection): boolean {
|
|
2039
|
-
const pid = Number(sim.meta?.sootsimHostPid)
|
|
2040
|
-
if (!Number.isInteger(pid) || pid <= 1 || pid === process.pid) return false
|
|
2041
|
-
try {
|
|
2042
|
-
process.kill(pid, 0) // ESRCH → dead; EPERM → alive but unsignalable
|
|
2043
|
-
return true
|
|
2044
|
-
} catch (err) {
|
|
2045
|
-
return (err as NodeJS.ErrnoException).code === 'EPERM'
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
1991
|
private reapIdleSims(now = Date.now()) {
|
|
2050
1992
|
const attachedSimIds = new Set(this.cliSimBySocket.values())
|
|
2051
|
-
// automation sims that pass every "someone cares" gate but are still under
|
|
2052
|
-
// their TTL — candidates for the concurrent-cap pass below.
|
|
2053
|
-
const automationLive: { sim: BridgeSimConnection; idleMs: number }[] = []
|
|
2054
1993
|
for (const sim of this.sims.values()) {
|
|
2055
|
-
if (sim.id === this.primarySimId) continue
|
|
2056
1994
|
if (attachedSimIds.has(sim.id)) continue
|
|
2057
|
-
if (this.getActiveLease(sim)) continue
|
|
2058
|
-
// never-CLI-driven sims have lastActiveAt 0 — fall back to connectedAt
|
|
2059
|
-
// so a freshly opened, not-yet-primary tab isn't reaped on the spot.
|
|
2060
|
-
const lastTouched = Math.max(sim.lastActiveAt, sim.connectedAt)
|
|
2061
|
-
const idleMs = now - lastTouched
|
|
2062
1995
|
const automation = this.isAutomationSim(sim)
|
|
2063
|
-
|
|
2064
|
-
//
|
|
2065
|
-
//
|
|
2066
|
-
//
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
const ttl = automation ? this.automationSimIdleReapTtlMs : this.simIdleReapTtlMs
|
|
2071
|
-
if (idleMs >= ttl) {
|
|
2072
|
-
// terminal close → client closes the window, does not reconnect; the
|
|
2073
|
-
// 'close' handler runs rememberDisconnectedSim → this.sims.delete.
|
|
2074
|
-
this.closeSimSocketFromHost(sim.ws)
|
|
1996
|
+
const lease = this.getActiveLease(sim)
|
|
1997
|
+
// an automation sim is nobody's tab, so being primary or holding a cli
|
|
1998
|
+
// lease (10 min from its last grant) says nothing about use: only a human
|
|
1999
|
+
// interacting with a headed window keeps one past its idle window.
|
|
2000
|
+
if (
|
|
2001
|
+
automation ? lease?.kind === 'user-active' : sim.id === this.primarySimId || lease
|
|
2002
|
+
) {
|
|
2075
2003
|
continue
|
|
2076
2004
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
for (const { sim, idleMs } of automationLive.slice(
|
|
2087
|
-
0,
|
|
2088
|
-
automationLive.length - cap,
|
|
2089
|
-
)) {
|
|
2090
|
-
if (idleMs < this.automationSimActiveGraceMs) continue
|
|
2091
|
-
this.closeSimSocketFromHost(sim.ws)
|
|
2092
|
-
}
|
|
2005
|
+
// never-CLI-driven sims have lastActiveAt 0 — fall back to connectedAt
|
|
2006
|
+
// so a freshly opened, not-yet-primary tab isn't reaped on the spot.
|
|
2007
|
+
const idleMs = now - Math.max(sim.lastActiveAt, sim.connectedAt)
|
|
2008
|
+
const ttl = automation
|
|
2009
|
+
? AUTOMATION_SIM_IDLE_REAP_TTL_MS
|
|
2010
|
+
: SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS
|
|
2011
|
+
// terminal close → client closes the window, does not reconnect; the
|
|
2012
|
+
// 'close' handler runs rememberDisconnectedSim → this.sims.delete.
|
|
2013
|
+
if (idleMs >= ttl) this.closeSimSocketFromHost(sim.ws)
|
|
2093
2014
|
}
|
|
2094
2015
|
}
|
|
2095
2016
|
|