sootsim 0.1.296 → 0.1.298
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/detox/index.ts +380 -28
- package/detox/motion-change.cjs +109 -0
- package/detox/motion-change.d.cts +35 -0
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-3F4K6MQK.js → agent-U3HXOT6B.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-5LLVCQRZ.js → agent-wrapper-LJOPJUAN.js} +2 -2
- package/dist-cli/chunks/{app-fonts-LPTY7A5G.js → app-fonts-4EVLV2BX.js} +2 -2
- package/dist-cli/chunks/{assert-R2ZUXEMS.js → assert-NNKBTEO7.js} +2 -2
- package/dist-cli/chunks/{auth-CA62Q6IS.js → auth-SF2WR2K2.js} +2 -2
- package/dist-cli/chunks/{beta-4AXSOQLU.js → beta-SCLWNOHI.js} +2 -2
- package/dist-cli/chunks/{camera-Y4RBIONE.js → camera-TZJ2Z3TZ.js} +2 -2
- package/dist-cli/chunks/{chunk-MDBR4XTV.js → chunk-3R4W2SQB.js} +3 -3
- package/dist-cli/chunks/{chunk-X7B5KJ6N.js → chunk-4H3Y3TGT.js} +1 -1
- package/dist-cli/chunks/{chunk-4MDLYOEI.js → chunk-5VGTEGDT.js} +2 -2
- package/dist-cli/chunks/{chunk-F2MS365Z.js → chunk-6YIZZDG2.js} +2 -2
- package/dist-cli/chunks/{chunk-7V3JNANH.js → chunk-7ZOI5KJZ.js} +2 -2
- package/dist-cli/chunks/{chunk-QIDFGREY.js → chunk-AHMMVYVE.js} +1 -1
- package/dist-cli/chunks/{chunk-ZIBKGZJD.js → chunk-ANFOERLP.js} +1 -1
- package/dist-cli/chunks/{chunk-GG6XOQ5P.js → chunk-APNG6X2R.js} +2 -2
- package/dist-cli/chunks/{chunk-54TQ3ZGI.js → chunk-AT3NTZPP.js} +3 -3
- package/dist-cli/chunks/{chunk-CAAADPRH.js → chunk-BUM4RR4G.js} +2 -2
- package/dist-cli/chunks/{chunk-PEUWZRXA.js → chunk-CAEQ7AOI.js} +3 -3
- package/dist-cli/chunks/{chunk-C462URRQ.js → chunk-CK3MTK2D.js} +2 -2
- package/dist-cli/chunks/{chunk-NEARYPSC.js → chunk-CXSMZLI6.js} +3 -3
- package/dist-cli/chunks/{chunk-HSGS2U7P.js → chunk-EH4B5ZQ4.js} +2 -2
- package/dist-cli/chunks/{chunk-74FOMZP7.js → chunk-EYBWIEDO.js} +2 -2
- package/dist-cli/chunks/{chunk-B2FB6IYX.js → chunk-FTMTIUBK.js} +2 -2
- package/dist-cli/chunks/{chunk-FUPQIX4U.js → chunk-G3NMMKBL.js} +2 -2
- package/dist-cli/chunks/{chunk-MQ6QX46T.js → chunk-GM2TRB5Z.js} +2 -2
- package/dist-cli/chunks/{chunk-CS23DQB2.js → chunk-GR7USTCW.js} +2 -2
- package/dist-cli/chunks/{chunk-AUBP44CY.js → chunk-GXVG5LJG.js} +2 -2
- package/dist-cli/chunks/{chunk-7GQWF7HE.js → chunk-HAJSNFUY.js} +1 -1
- package/dist-cli/chunks/{chunk-222Y7NQA.js → chunk-HG63AS7K.js} +1 -1
- package/dist-cli/chunks/{chunk-LHUY46GE.js → chunk-HGSYUEQP.js} +2 -2
- package/dist-cli/chunks/{chunk-TIOPG6QZ.js → chunk-HOQD6TZN.js} +1 -1
- package/dist-cli/chunks/{chunk-CDCVWGNU.js → chunk-IBMZ7A6M.js} +2 -2
- package/dist-cli/chunks/{chunk-EISE56VD.js → chunk-IRMKZSWK.js} +2 -2
- package/dist-cli/chunks/{chunk-SUJSGGSS.js → chunk-J42Y66IM.js} +2 -2
- package/dist-cli/chunks/{chunk-FROCFR77.js → chunk-JM36NHNU.js} +1 -1
- package/dist-cli/chunks/{chunk-WJVBD2UL.js → chunk-KVYZYYSY.js} +1 -1
- package/dist-cli/chunks/{chunk-3PAZJSIG.js → chunk-MFMFNCBK.js} +2 -2
- package/dist-cli/chunks/{chunk-DUYKBCM7.js → chunk-MHCSRMK4.js} +1 -1
- package/dist-cli/chunks/{chunk-S5Z4HKWX.js → chunk-MTL4DPUK.js} +1 -1
- package/dist-cli/chunks/{chunk-ZPBIHFQW.js → chunk-NRC73NVF.js} +2 -2
- package/dist-cli/chunks/{chunk-6C24WCDK.js → chunk-PLEZAZTJ.js} +2 -2
- package/dist-cli/chunks/{chunk-IR3DUBRF.js → chunk-PYUI2PHI.js} +2 -2
- package/dist-cli/chunks/{chunk-R65XMN4D.js → chunk-QZCVY5BX.js} +1 -1
- package/dist-cli/chunks/{chunk-5FNPAMP4.js → chunk-SQR7NH32.js} +1 -1
- package/dist-cli/chunks/{chunk-XMVVZCIM.js → chunk-TB77ANGH.js} +2 -2
- package/dist-cli/chunks/chunk-THIBWEML.js +4 -0
- package/dist-cli/chunks/{chunk-N3FZRJT7.js → chunk-TTFO75GL.js} +1 -1
- package/dist-cli/chunks/{chunk-4NFGOGB7.js → chunk-U5TY536V.js} +2 -2
- package/dist-cli/chunks/{chunk-7YQXQSG7.js → chunk-UTUBMEFS.js} +1 -1
- package/dist-cli/chunks/{chunk-B2LDO55K.js → chunk-VFI23QUE.js} +1 -1
- package/dist-cli/chunks/{chunk-BVWIR4CD.js → chunk-WDKLXBQR.js} +1 -1
- package/dist-cli/chunks/{chunk-2XUKWIAL.js → chunk-WV6CBQYZ.js} +1 -1
- package/dist-cli/chunks/{chunk-2LQOLMRK.js → chunk-X3STT7I3.js} +1 -1
- package/dist-cli/chunks/{chunk-GG5PWLGC.js → chunk-XITV33JC.js} +2 -2
- package/dist-cli/chunks/{chunk-EJ6BAJKX.js → chunk-ZBETYTSD.js} +1 -1
- package/dist-cli/chunks/{chunk-G2SO5FPN.js → chunk-ZCHO67PX.js} +2 -2
- package/dist-cli/chunks/{cleanup-4RSKYPQ7.js → cleanup-YZPCNPWE.js} +2 -2
- package/dist-cli/chunks/{cli-version-VZFF7GGM.js → cli-version-ECA6G6JQ.js} +2 -2
- package/dist-cli/chunks/{compat-FABLN4UI.js → compat-OM7ODBMO.js} +2 -2
- package/dist-cli/chunks/{config-74J37LKC.js → config-ZIX6LPFG.js} +2 -2
- package/dist-cli/chunks/{control-VBK32Y7F.js → control-BY6UAMKN.js} +2 -2
- package/dist-cli/chunks/daemon-BQ2T53ZA.js +4 -0
- package/dist-cli/chunks/{debug-AOYA2CLR.js → debug-MVPFRIQA.js} +3 -3
- package/dist-cli/chunks/{desktop-G7XWDCTM.js → desktop-ZXH7NRHW.js} +3 -3
- package/dist-cli/chunks/{detox-QXJ3L2ZI.js → detox-4RZ5UER3.js} +2 -2
- package/dist-cli/chunks/{device-NZZ5ZT3Q.js → device-7R4BSFMS.js} +2 -2
- package/dist-cli/chunks/{diagnose-5CAEJ42M.js → diagnose-4JAFIXWD.js} +2 -2
- package/dist-cli/chunks/disk-cleanup-3E7GYLGW.js +4 -0
- package/dist-cli/chunks/drivers-2OW33GOL.js +4 -0
- package/dist-cli/chunks/{film-C25VRUNC.js → film-Q7KZIGBH.js} +3 -3
- package/dist-cli/chunks/flow-QTOSAQ5X.js +4 -0
- package/dist-cli/chunks/help-UE32FDEY.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-NSLIK6XT.js → hidden-runtime-alias-4ZUCLCVJ.js} +2 -2
- package/dist-cli/chunks/{home-paths-OC6JZXIM.js → home-paths-PVZPREN7.js} +2 -2
- package/dist-cli/chunks/inspect-QWR2U3DK.js +4 -0
- package/dist-cli/chunks/install-desktop-R2ZM5ZJH.js +4 -0
- package/dist-cli/chunks/{login-LB4TMM4T.js → login-UMP65DJG.js} +4 -4
- package/dist-cli/chunks/{logout-33IOBD4X.js → logout-F45WBVJV.js} +2 -2
- package/dist-cli/chunks/{maestro-EEVXX536.js → maestro-33RGEXP4.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-ICPWVXGA.js → maestro-generate-5SLZMOMB.js} +3 -3
- package/dist-cli/chunks/{mode-6IXOZDK3.js → mode-LZORZYWP.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-KBMC7SNI.js → optional-demo-registry-DIWO7DG5.js} +2 -2
- package/dist-cli/chunks/{perf-CTTJSAIJ.js → perf-VW3UTYCK.js} +2 -2
- package/dist-cli/chunks/{permissions-BFQS4DIY.js → permissions-7JIR6QPO.js} +2 -2
- package/dist-cli/chunks/{record-MJR5ELHE.js → record-A4MRKJ2E.js} +3 -3
- package/dist-cli/chunks/{report-issue-FMJRNDKH.js → report-issue-JUWTB66Z.js} +2 -2
- package/dist-cli/chunks/runtime-7LSOODXL.js +4 -0
- package/dist-cli/chunks/{screenshot-command-MGNASAXR.js → screenshot-command-6LROQJPJ.js} +3 -3
- package/dist-cli/chunks/{screenshots-capture-N6PANAVT.js → screenshots-capture-FMYE24EW.js} +2 -2
- package/dist-cli/chunks/{serve-RS6TIO7U.js → serve-64SRDCU5.js} +2 -2
- package/dist-cli/chunks/{setup-MFYGYR2F.js → setup-2CUXSDQH.js} +2 -2
- package/dist-cli/chunks/{skills-TD35LDKK.js → skills-IILJVUMS.js} +2 -2
- package/dist-cli/chunks/{storage-VCFXDJNO.js → storage-BUQQJAMD.js} +2 -2
- package/dist-cli/chunks/store-3Z2GSFGY.js +4 -0
- package/dist-cli/chunks/telemetry-2UMKQXU7.js +4 -0
- package/dist-cli/chunks/{timeline-UJCZAWJV.js → timeline-62RWUHSF.js} +2 -2
- package/dist-cli/chunks/{upgrade-S24NH4YG.js → upgrade-GHXDTO2A.js} +2 -2
- package/dist-cli/chunks/upload-H6RTYSLO.js +4 -0
- package/dist-cli/chunks/{version-IVPWT2ZQ.js → version-F4CKBMB6.js} +2 -2
- package/dist-cli/chunks/{web-6BMWAWJL.js → web-YM6GIFOU.js} +2 -2
- package/dist-cli/chunks/{what-happened-HRHB6ETL.js → what-happened-UKBSKIGA.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -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 +6 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +312 -27
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +1 -1
- 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/websocket-proxy.cjs +1 -1
- 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.cjs +1 -1
- package/dist-lib/profiles.cjs +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 +15 -11
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/src/bridge-constants.ts +9 -0
- package/dist-cli/chunks/chunk-P7HBJBHH.js +0 -4
- package/dist-cli/chunks/daemon-VFO7LRR4.js +0 -4
- package/dist-cli/chunks/disk-cleanup-GZSUZJ6Y.js +0 -4
- package/dist-cli/chunks/drivers-OLJCUSPO.js +0 -4
- package/dist-cli/chunks/flow-IW3WUF5D.js +0 -4
- package/dist-cli/chunks/help-GEVJF2DA.js +0 -4
- package/dist-cli/chunks/inspect-KXJNI7CU.js +0 -4
- package/dist-cli/chunks/install-desktop-IYWPRFPB.js +0 -4
- package/dist-cli/chunks/runtime-PV4HH2OB.js +0 -4
- package/dist-cli/chunks/store-VNSTKYBQ.js +0 -4
- package/dist-cli/chunks/telemetry-GPNHSYFF.js +0 -4
- package/dist-cli/chunks/upload-CCKAO2M5.js +0 -4
package/detox/index.ts
CHANGED
|
@@ -15,6 +15,12 @@ import {
|
|
|
15
15
|
waitForSootsimGestureHandler,
|
|
16
16
|
} from './gestures'
|
|
17
17
|
import { by, type Matcher } from './matchers'
|
|
18
|
+
import {
|
|
19
|
+
MOTION_CHANGE_DEFINITION,
|
|
20
|
+
measureMotionChange,
|
|
21
|
+
type MotionChangeDefinition,
|
|
22
|
+
type MotionChangeMeasurement,
|
|
23
|
+
} from './motion-change.cjs'
|
|
18
24
|
import type { SootSimElement } from './element-types'
|
|
19
25
|
import type { Browser, BrowserContext, Page } from 'playwright'
|
|
20
26
|
import type { SootsimEventMap, SootsimEventName } from 'sootsim-engine/sootsim-event'
|
|
@@ -554,6 +560,32 @@ function decodePngDataUrl(dataUrl: string): Buffer {
|
|
|
554
560
|
return Buffer.from(match[1], 'base64')
|
|
555
561
|
}
|
|
556
562
|
|
|
563
|
+
type FastScreenshotData = {
|
|
564
|
+
label: string
|
|
565
|
+
offsetMs: number
|
|
566
|
+
actualMs: number
|
|
567
|
+
dataUrl: string
|
|
568
|
+
frameIndex?: number
|
|
569
|
+
virtualTimestampMs?: number
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function writeFastScreenshotFrames<T extends FastScreenshotData>(
|
|
573
|
+
namePrefix: string,
|
|
574
|
+
frames: T[],
|
|
575
|
+
): Array<Omit<T, 'dataUrl'> & { path: string }> {
|
|
576
|
+
ensureScreenshotDir()
|
|
577
|
+
return frames.map((frame) => {
|
|
578
|
+
const safeName = `${namePrefix}-${frame.label}`.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
|
579
|
+
const filePath = path.join(SCREENSHOT_DIR, `${safeName}.png`)
|
|
580
|
+
fs.writeFileSync(filePath, decodePngDataUrl(frame.dataUrl))
|
|
581
|
+
const { dataUrl: _dataUrl, ...metadata } = frame
|
|
582
|
+
return {
|
|
583
|
+
...metadata,
|
|
584
|
+
path: filePath,
|
|
585
|
+
}
|
|
586
|
+
})
|
|
587
|
+
}
|
|
588
|
+
|
|
557
589
|
// single-shot canvas capture — no frame-stability poll. used for multi-stage
|
|
558
590
|
// animation captures where we explicitly want to sample mid-transition. the
|
|
559
591
|
// usual captureSootsimPng polls until two consecutive frames are byte-equal
|
|
@@ -1347,10 +1379,352 @@ export const device = {
|
|
|
1347
1379
|
await dispatchSootsimEventInPage('sootsim:menuAnimOverride', { value: progress })
|
|
1348
1380
|
},
|
|
1349
1381
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1382
|
+
// park every render clock before the interaction, detect the first changed
|
|
1383
|
+
// stepped frame inside the named component, then advance exact 60fps frames
|
|
1384
|
+
// to each requested offset. native extraction also chooses the first frame
|
|
1385
|
+
// at or after an offset, so both sides use the same rounding convention.
|
|
1386
|
+
async takeScreenshotStagesFromVisibleChangeFast(
|
|
1387
|
+
namePrefix: string,
|
|
1388
|
+
stages: Array<{ label: string; offsetMs: number }>,
|
|
1389
|
+
options: {
|
|
1390
|
+
detectionTimeoutMs?: number
|
|
1391
|
+
frame?: { height: number; width: number; x: number; y: number } | null
|
|
1392
|
+
rootFrame?: { height: number; width: number; x: number; y: number } | null
|
|
1393
|
+
trigger: () => unknown
|
|
1394
|
+
},
|
|
1395
|
+
): Promise<{
|
|
1396
|
+
frames: Array<{
|
|
1397
|
+
label: string
|
|
1398
|
+
offsetMs: number
|
|
1399
|
+
actualMs: number
|
|
1400
|
+
path: string
|
|
1401
|
+
frameIndex: number
|
|
1402
|
+
virtualTimestampMs: number
|
|
1403
|
+
}>
|
|
1404
|
+
triggerCompletedMs: number
|
|
1405
|
+
triggerStartMs: number
|
|
1406
|
+
visibleChangeDetection: {
|
|
1407
|
+
actualMs: number
|
|
1408
|
+
changedPixelPercent: number
|
|
1409
|
+
changedPixels: number
|
|
1410
|
+
channelThreshold: number
|
|
1411
|
+
frame: { height: number; width: number; x: number; y: number } | null
|
|
1412
|
+
minimumChangePercent: number
|
|
1413
|
+
rootFrame: { height: number; width: number; x: number; y: number } | null
|
|
1414
|
+
totalPixels: number
|
|
1415
|
+
frameIndex: number
|
|
1416
|
+
virtualTimestampMs: number
|
|
1417
|
+
}
|
|
1418
|
+
}> {
|
|
1419
|
+
const page = getPage()
|
|
1420
|
+
const token = `motion-${process.pid}-${Date.now()}-${Math.random()}`
|
|
1421
|
+
await page.evaluate(measureMotionChange, {
|
|
1422
|
+
definition: MOTION_CHANGE_DEFINITION,
|
|
1423
|
+
installGlobal: true,
|
|
1424
|
+
})
|
|
1425
|
+
const capturePromise: Promise<{
|
|
1426
|
+
frames: Array<
|
|
1427
|
+
FastScreenshotData & {
|
|
1428
|
+
frameIndex: number
|
|
1429
|
+
virtualTimestampMs: number
|
|
1430
|
+
}
|
|
1431
|
+
>
|
|
1432
|
+
visibleChangeDetection: {
|
|
1433
|
+
actualMs: number
|
|
1434
|
+
changedPixelPercent: number
|
|
1435
|
+
changedPixels: number
|
|
1436
|
+
channelThreshold: number
|
|
1437
|
+
frameIndex: number
|
|
1438
|
+
minimumChangePercent: number
|
|
1439
|
+
totalPixels: number
|
|
1440
|
+
virtualTimestampMs: number
|
|
1441
|
+
}
|
|
1442
|
+
}> = page.evaluate(
|
|
1443
|
+
async (input) => {
|
|
1444
|
+
type FrameReceipt = { frameIndex: number; virtualTimestampMs: number }
|
|
1445
|
+
type CaptureState = {
|
|
1446
|
+
cancelled: boolean
|
|
1447
|
+
ready: boolean
|
|
1448
|
+
triggerCompleted: boolean
|
|
1449
|
+
}
|
|
1450
|
+
type CaptureWindow = Window & {
|
|
1451
|
+
__sootsimMeasureMotionChange?: (input: {
|
|
1452
|
+
baseline: Uint8ClampedArray
|
|
1453
|
+
candidate: Uint8ClampedArray
|
|
1454
|
+
definition: MotionChangeDefinition
|
|
1455
|
+
height: number
|
|
1456
|
+
label: string
|
|
1457
|
+
width: number
|
|
1458
|
+
}) => MotionChangeMeasurement
|
|
1459
|
+
__sootsimVisibleChangeCaptures?: Record<string, CaptureState>
|
|
1460
|
+
SootSim?: {
|
|
1461
|
+
bridges?: {
|
|
1462
|
+
liveComposite?: {
|
|
1463
|
+
captureFresh: () => Promise<{
|
|
1464
|
+
canvas: HTMLCanvasElement | null
|
|
1465
|
+
frameToken: number
|
|
1466
|
+
}>
|
|
1467
|
+
}
|
|
1468
|
+
lockstepCapture?: {
|
|
1469
|
+
begin: (fps: number) => Promise<{
|
|
1470
|
+
stepFrame: () => Promise<FrameReceipt>
|
|
1471
|
+
stepToOffset: (
|
|
1472
|
+
anchor: FrameReceipt,
|
|
1473
|
+
offsetMs: number,
|
|
1474
|
+
) => Promise<FrameReceipt>
|
|
1475
|
+
end: () => void
|
|
1476
|
+
}>
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
type Snapshot = {
|
|
1482
|
+
actualMs: number
|
|
1483
|
+
bitmap: ImageBitmap
|
|
1484
|
+
label: string
|
|
1485
|
+
offsetMs: number
|
|
1486
|
+
frameIndex: number
|
|
1487
|
+
virtualTimestampMs: number
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
const captureWindow = window as unknown as CaptureWindow
|
|
1491
|
+
const live = captureWindow.SootSim?.bridges?.liveComposite
|
|
1492
|
+
const lockstep = captureWindow.SootSim?.bridges?.lockstepCapture
|
|
1493
|
+
if (!live || typeof live.captureFresh !== 'function') {
|
|
1494
|
+
throw new Error('SootSim live composite bridge is not installed')
|
|
1495
|
+
}
|
|
1496
|
+
if (!lockstep || typeof lockstep.begin !== 'function') {
|
|
1497
|
+
throw new Error('SootSim lockstep capture bridge is not installed')
|
|
1498
|
+
}
|
|
1499
|
+
const registry =
|
|
1500
|
+
captureWindow.__sootsimVisibleChangeCaptures ??
|
|
1501
|
+
(captureWindow.__sootsimVisibleChangeCaptures = {})
|
|
1502
|
+
const state: CaptureState = {
|
|
1503
|
+
cancelled: false,
|
|
1504
|
+
ready: false,
|
|
1505
|
+
triggerCompleted: false,
|
|
1506
|
+
}
|
|
1507
|
+
registry[input.token] = state
|
|
1508
|
+
const measureChange = captureWindow.__sootsimMeasureMotionChange
|
|
1509
|
+
if (!measureChange) {
|
|
1510
|
+
throw new Error('SootSim motion change definition is not installed')
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
const captureBitmap = async () => {
|
|
1514
|
+
const { canvas } = await live.captureFresh()
|
|
1515
|
+
if (!canvas) throw new Error('SootSim live composite produced no canvas')
|
|
1516
|
+
return createImageBitmap(canvas)
|
|
1517
|
+
}
|
|
1518
|
+
const boundsFor = (bitmap: ImageBitmap) => {
|
|
1519
|
+
const scale =
|
|
1520
|
+
input.rootFrame && input.rootFrame.width > 0
|
|
1521
|
+
? bitmap.width / input.rootFrame.width
|
|
1522
|
+
: 1
|
|
1523
|
+
return {
|
|
1524
|
+
height: input.frame
|
|
1525
|
+
? Math.max(
|
|
1526
|
+
1,
|
|
1527
|
+
Math.min(
|
|
1528
|
+
bitmap.height,
|
|
1529
|
+
Math.ceil((input.frame.y + input.frame.height) * scale),
|
|
1530
|
+
) - Math.max(0, Math.floor(input.frame.y * scale)),
|
|
1531
|
+
)
|
|
1532
|
+
: bitmap.height,
|
|
1533
|
+
left: input.frame ? Math.max(0, Math.floor(input.frame.x * scale)) : 0,
|
|
1534
|
+
top: input.frame ? Math.max(0, Math.floor(input.frame.y * scale)) : 0,
|
|
1535
|
+
width: input.frame
|
|
1536
|
+
? Math.max(
|
|
1537
|
+
1,
|
|
1538
|
+
Math.min(
|
|
1539
|
+
bitmap.width,
|
|
1540
|
+
Math.ceil((input.frame.x + input.frame.width) * scale),
|
|
1541
|
+
) - Math.max(0, Math.floor(input.frame.x * scale)),
|
|
1542
|
+
)
|
|
1543
|
+
: bitmap.width,
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
const regionPixels = (bitmap: ImageBitmap) => {
|
|
1547
|
+
const bounds = boundsFor(bitmap)
|
|
1548
|
+
const canvas = document.createElement('canvas')
|
|
1549
|
+
canvas.width = bounds.width
|
|
1550
|
+
canvas.height = bounds.height
|
|
1551
|
+
const context = canvas.getContext('2d', { willReadFrequently: true })
|
|
1552
|
+
if (!context) throw new Error('SootSim motion detector needs a 2d canvas')
|
|
1553
|
+
context.drawImage(
|
|
1554
|
+
bitmap,
|
|
1555
|
+
bounds.left,
|
|
1556
|
+
bounds.top,
|
|
1557
|
+
bounds.width,
|
|
1558
|
+
bounds.height,
|
|
1559
|
+
0,
|
|
1560
|
+
0,
|
|
1561
|
+
bounds.width,
|
|
1562
|
+
bounds.height,
|
|
1563
|
+
)
|
|
1564
|
+
return context.getImageData(0, 0, bounds.width, bounds.height).data
|
|
1565
|
+
}
|
|
1566
|
+
const encode = (snapshot: Snapshot) => {
|
|
1567
|
+
const canvas = document.createElement('canvas')
|
|
1568
|
+
canvas.width = snapshot.bitmap.width
|
|
1569
|
+
canvas.height = snapshot.bitmap.height
|
|
1570
|
+
const context = canvas.getContext('2d')
|
|
1571
|
+
if (!context) throw new Error('SootSim motion capture needs a 2d canvas')
|
|
1572
|
+
context.drawImage(snapshot.bitmap, 0, 0)
|
|
1573
|
+
snapshot.bitmap.close()
|
|
1574
|
+
return {
|
|
1575
|
+
actualMs: snapshot.actualMs,
|
|
1576
|
+
dataUrl: canvas.toDataURL('image/png'),
|
|
1577
|
+
label: snapshot.label,
|
|
1578
|
+
offsetMs: snapshot.offsetMs,
|
|
1579
|
+
frameIndex: snapshot.frameIndex,
|
|
1580
|
+
virtualTimestampMs: snapshot.virtualTimestampMs,
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
const controller = await lockstep.begin(60)
|
|
1585
|
+
try {
|
|
1586
|
+
const baselineBitmap = await captureBitmap()
|
|
1587
|
+
const baseline = regionPixels(baselineBitmap)
|
|
1588
|
+
baselineBitmap.close()
|
|
1589
|
+
state.ready = true
|
|
1590
|
+
while (!state.triggerCompleted) {
|
|
1591
|
+
if (state.cancelled) throw new Error('SootSim motion capture was cancelled')
|
|
1592
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
let visibleStart: FrameReceipt | null = null
|
|
1596
|
+
let detectedMeasurement: MotionChangeMeasurement | null = null
|
|
1597
|
+
const detectionFrameLimit = Math.ceil((input.detectionTimeoutMs * 60) / 1000)
|
|
1598
|
+
for (let frame = 0; frame < detectionFrameLimit; frame++) {
|
|
1599
|
+
if (state.cancelled) throw new Error('SootSim motion capture was cancelled')
|
|
1600
|
+
const receipt = await controller.stepFrame()
|
|
1601
|
+
const candidateBitmap = await captureBitmap()
|
|
1602
|
+
const bounds = boundsFor(candidateBitmap)
|
|
1603
|
+
const measurement = measureChange({
|
|
1604
|
+
baseline,
|
|
1605
|
+
candidate: regionPixels(candidateBitmap),
|
|
1606
|
+
definition: input.motionChangeDefinition,
|
|
1607
|
+
height: bounds.height,
|
|
1608
|
+
label: 'SootSim visible-change detector',
|
|
1609
|
+
width: bounds.width,
|
|
1610
|
+
})
|
|
1611
|
+
candidateBitmap.close()
|
|
1612
|
+
if (measurement.passedMinimum) {
|
|
1613
|
+
visibleStart = receipt
|
|
1614
|
+
detectedMeasurement = measurement
|
|
1615
|
+
break
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
if (!visibleStart || !detectedMeasurement) {
|
|
1619
|
+
throw new Error(
|
|
1620
|
+
`SootSim motion had no visible change before ${input.detectionTimeoutMs}ms`,
|
|
1621
|
+
)
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
const snapshots: Snapshot[] = []
|
|
1625
|
+
for (const stage of input.stages) {
|
|
1626
|
+
if (state.cancelled) throw new Error('SootSim motion capture was cancelled')
|
|
1627
|
+
const receipt = await controller.stepToOffset(visibleStart, stage.offsetMs)
|
|
1628
|
+
snapshots.push({
|
|
1629
|
+
actualMs: receipt.virtualTimestampMs - visibleStart.virtualTimestampMs,
|
|
1630
|
+
bitmap: await captureBitmap(),
|
|
1631
|
+
frameIndex: receipt.frameIndex,
|
|
1632
|
+
label: stage.label,
|
|
1633
|
+
offsetMs: stage.offsetMs,
|
|
1634
|
+
virtualTimestampMs: receipt.virtualTimestampMs,
|
|
1635
|
+
})
|
|
1636
|
+
}
|
|
1637
|
+
return {
|
|
1638
|
+
frames: snapshots.map(encode),
|
|
1639
|
+
visibleChangeDetection: {
|
|
1640
|
+
actualMs: (visibleStart.frameIndex * 1000) / 60,
|
|
1641
|
+
changedPixelPercent: detectedMeasurement.changePercent,
|
|
1642
|
+
changedPixels: detectedMeasurement.changePixels,
|
|
1643
|
+
channelThreshold: input.motionChangeDefinition.channelThreshold,
|
|
1644
|
+
minimumChangePercent: input.motionChangeDefinition.minimumChangePercent,
|
|
1645
|
+
totalPixels: detectedMeasurement.totalPixels,
|
|
1646
|
+
frameIndex: visibleStart.frameIndex,
|
|
1647
|
+
virtualTimestampMs: visibleStart.virtualTimestampMs,
|
|
1648
|
+
},
|
|
1649
|
+
}
|
|
1650
|
+
} finally {
|
|
1651
|
+
controller.end()
|
|
1652
|
+
delete registry[input.token]
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
detectionTimeoutMs: options.detectionTimeoutMs ?? 3000,
|
|
1657
|
+
frame: options.frame ?? null,
|
|
1658
|
+
motionChangeDefinition: MOTION_CHANGE_DEFINITION,
|
|
1659
|
+
rootFrame: options.rootFrame ?? null,
|
|
1660
|
+
stages,
|
|
1661
|
+
token,
|
|
1662
|
+
},
|
|
1663
|
+
)
|
|
1664
|
+
|
|
1665
|
+
await page.waitForFunction(
|
|
1666
|
+
(captureToken) => {
|
|
1667
|
+
const registry = (
|
|
1668
|
+
window as Window & {
|
|
1669
|
+
__sootsimVisibleChangeCaptures?: Record<
|
|
1670
|
+
string,
|
|
1671
|
+
{ cancelled: boolean; ready: boolean; triggerCompleted: boolean }
|
|
1672
|
+
>
|
|
1673
|
+
}
|
|
1674
|
+
).__sootsimVisibleChangeCaptures
|
|
1675
|
+
return registry?.[captureToken]?.ready === true
|
|
1676
|
+
},
|
|
1677
|
+
token,
|
|
1678
|
+
{ timeout: 5000 },
|
|
1679
|
+
)
|
|
1680
|
+
|
|
1681
|
+
const triggerStartMs = Date.now()
|
|
1682
|
+
let triggerCompletedMs = 0
|
|
1683
|
+
try {
|
|
1684
|
+
await Promise.resolve(options.trigger())
|
|
1685
|
+
triggerCompletedMs = Date.now()
|
|
1686
|
+
await page.evaluate((captureToken) => {
|
|
1687
|
+
const registry = (
|
|
1688
|
+
window as Window & {
|
|
1689
|
+
__sootsimVisibleChangeCaptures?: Record<
|
|
1690
|
+
string,
|
|
1691
|
+
{ cancelled: boolean; ready: boolean; triggerCompleted: boolean }
|
|
1692
|
+
>
|
|
1693
|
+
}
|
|
1694
|
+
).__sootsimVisibleChangeCaptures
|
|
1695
|
+
const capture = registry?.[captureToken]
|
|
1696
|
+
if (!capture) throw new Error('SootSim motion capture disarmed before trigger')
|
|
1697
|
+
capture.triggerCompleted = true
|
|
1698
|
+
}, token)
|
|
1699
|
+
const capture = await capturePromise
|
|
1700
|
+
return {
|
|
1701
|
+
frames: writeFastScreenshotFrames(namePrefix, capture.frames),
|
|
1702
|
+
triggerCompletedMs,
|
|
1703
|
+
triggerStartMs,
|
|
1704
|
+
visibleChangeDetection: {
|
|
1705
|
+
...capture.visibleChangeDetection,
|
|
1706
|
+
frame: options.frame ?? null,
|
|
1707
|
+
rootFrame: options.rootFrame ?? null,
|
|
1708
|
+
},
|
|
1709
|
+
}
|
|
1710
|
+
} catch (error) {
|
|
1711
|
+
await page
|
|
1712
|
+
.evaluate((captureToken) => {
|
|
1713
|
+
const registry = (
|
|
1714
|
+
window as Window & {
|
|
1715
|
+
__sootsimVisibleChangeCaptures?: Record<
|
|
1716
|
+
string,
|
|
1717
|
+
{ cancelled: boolean; ready: boolean; triggerCompleted: boolean }
|
|
1718
|
+
>
|
|
1719
|
+
}
|
|
1720
|
+
).__sootsimVisibleChangeCaptures
|
|
1721
|
+
const capture = registry?.[captureToken]
|
|
1722
|
+
if (capture) capture.cancelled = true
|
|
1723
|
+
}, token)
|
|
1724
|
+
.catch(() => {})
|
|
1725
|
+
await capturePromise.catch(() => {})
|
|
1726
|
+
throw error
|
|
1727
|
+
}
|
|
1354
1728
|
},
|
|
1355
1729
|
|
|
1356
1730
|
// FAST multi-stage capture for sub-second animation diffs. each stage
|
|
@@ -1490,18 +1864,7 @@ export const device = {
|
|
|
1490
1864
|
},
|
|
1491
1865
|
{ stages, skipWarmup: options?.skipWarmup === true },
|
|
1492
1866
|
)
|
|
1493
|
-
|
|
1494
|
-
return frames.map((f) => {
|
|
1495
|
-
const safeName = `${namePrefix}-${f.label}`.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
|
1496
|
-
const filePath = path.join(SCREENSHOT_DIR, `${safeName}.png`)
|
|
1497
|
-
fs.writeFileSync(filePath, decodePngDataUrl(f.dataUrl))
|
|
1498
|
-
return {
|
|
1499
|
-
label: f.label,
|
|
1500
|
-
offsetMs: f.offsetMs,
|
|
1501
|
-
actualMs: f.actualMs,
|
|
1502
|
-
path: filePath,
|
|
1503
|
-
}
|
|
1504
|
-
})
|
|
1867
|
+
return writeFastScreenshotFrames(namePrefix, frames)
|
|
1505
1868
|
},
|
|
1506
1869
|
|
|
1507
1870
|
// batched multi-stage capture for animation diffs. all N captures happen
|
|
@@ -1549,18 +1912,7 @@ export const device = {
|
|
|
1549
1912
|
},
|
|
1550
1913
|
{ stages },
|
|
1551
1914
|
)
|
|
1552
|
-
|
|
1553
|
-
return frames.map((f) => {
|
|
1554
|
-
const safeName = `${namePrefix}-${f.label}`.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
|
1555
|
-
const filePath = path.join(SCREENSHOT_DIR, `${safeName}.png`)
|
|
1556
|
-
fs.writeFileSync(filePath, decodePngDataUrl(f.dataUrl))
|
|
1557
|
-
return {
|
|
1558
|
-
label: f.label,
|
|
1559
|
-
offsetMs: f.offsetMs,
|
|
1560
|
-
actualMs: f.actualMs,
|
|
1561
|
-
path: filePath,
|
|
1562
|
-
}
|
|
1563
|
-
})
|
|
1915
|
+
return writeFastScreenshotFrames(namePrefix, frames)
|
|
1564
1916
|
},
|
|
1565
1917
|
|
|
1566
1918
|
async shake() {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// native recordings and SootSim live frames must use this exact definition.
|
|
2
|
+
// retained static Android crops measured as high as 0.0331% after the channel
|
|
3
|
+
// filter. the smallest corrected component transition with visible motion was
|
|
4
|
+
// 0.1748%, so a 0.1% floor separates every measured static and moving frame.
|
|
5
|
+
const MOTION_CHANGE_DEFINITION = Object.freeze({
|
|
6
|
+
channelThreshold: 24,
|
|
7
|
+
minimumChangePercent: 0.1,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
function measureMotionChange(input) {
|
|
11
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
12
|
+
throw new Error('motion change measurement requires an input object')
|
|
13
|
+
}
|
|
14
|
+
if (input.installGlobal === true) {
|
|
15
|
+
globalThis.__sootsimMeasureMotionChange = measureMotionChange
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const definition = input.definition
|
|
20
|
+
if (
|
|
21
|
+
!definition ||
|
|
22
|
+
typeof definition !== 'object' ||
|
|
23
|
+
!Number.isInteger(definition.channelThreshold) ||
|
|
24
|
+
definition.channelThreshold < 0 ||
|
|
25
|
+
definition.channelThreshold > 255 ||
|
|
26
|
+
!Number.isFinite(definition.minimumChangePercent) ||
|
|
27
|
+
definition.minimumChangePercent <= 0 ||
|
|
28
|
+
definition.minimumChangePercent >= 100
|
|
29
|
+
) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
'motion change definition needs a channel threshold from 0 to 255 and a minimum greater than 0% and less than 100%',
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const label =
|
|
36
|
+
typeof input.label === 'string' && input.label.length > 0
|
|
37
|
+
? input.label
|
|
38
|
+
: 'motion change'
|
|
39
|
+
let changePixels = input.changePixels
|
|
40
|
+
let totalPixels = input.totalPixels
|
|
41
|
+
if (input.baseline !== undefined || input.candidate !== undefined) {
|
|
42
|
+
const baseline = input.baseline
|
|
43
|
+
const candidate = input.candidate
|
|
44
|
+
const width = input.width
|
|
45
|
+
const height = input.height
|
|
46
|
+
if (
|
|
47
|
+
!baseline ||
|
|
48
|
+
!candidate ||
|
|
49
|
+
typeof baseline.length !== 'number' ||
|
|
50
|
+
typeof candidate.length !== 'number' ||
|
|
51
|
+
!Number.isInteger(width) ||
|
|
52
|
+
!Number.isInteger(height) ||
|
|
53
|
+
width <= 0 ||
|
|
54
|
+
height <= 0
|
|
55
|
+
) {
|
|
56
|
+
throw new Error(`${label} needs two RGBA pixel arrays and positive dimensions`)
|
|
57
|
+
}
|
|
58
|
+
totalPixels = width * height
|
|
59
|
+
const expectedBytes = totalPixels * 4
|
|
60
|
+
if (baseline.length !== expectedBytes || candidate.length !== expectedBytes) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`${label} pixel data has ${baseline.length}/${candidate.length} bytes; expected ${expectedBytes}`,
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
changePixels = 0
|
|
66
|
+
for (let offset = 0; offset < expectedBytes; offset += 4) {
|
|
67
|
+
if (
|
|
68
|
+
Math.abs(baseline[offset] - candidate[offset]) > definition.channelThreshold ||
|
|
69
|
+
Math.abs(baseline[offset + 1] - candidate[offset + 1]) >
|
|
70
|
+
definition.channelThreshold ||
|
|
71
|
+
Math.abs(baseline[offset + 2] - candidate[offset + 2]) >
|
|
72
|
+
definition.channelThreshold ||
|
|
73
|
+
Math.abs(baseline[offset + 3] - candidate[offset + 3]) >
|
|
74
|
+
definition.channelThreshold
|
|
75
|
+
) {
|
|
76
|
+
changePixels++
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!Number.isInteger(totalPixels) || totalPixels <= 0) {
|
|
82
|
+
throw new Error(`${label} has no positive measured region area`)
|
|
83
|
+
}
|
|
84
|
+
if (!Number.isInteger(changePixels) || changePixels < 0 || changePixels > totalPixels) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`${label} changed pixel count ${changePixels} is outside its ${totalPixels}px measured region`,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
const changePercent = (changePixels / totalPixels) * 100
|
|
90
|
+
return {
|
|
91
|
+
changePercent,
|
|
92
|
+
changePixels,
|
|
93
|
+
passedMinimum: changePercent >= definition.minimumChangePercent,
|
|
94
|
+
totalPixels,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function measureProofMotionChange(input) {
|
|
99
|
+
return measureMotionChange({
|
|
100
|
+
...input,
|
|
101
|
+
definition: MOTION_CHANGE_DEFINITION,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = {
|
|
106
|
+
MOTION_CHANGE_DEFINITION,
|
|
107
|
+
measureMotionChange,
|
|
108
|
+
measureProofMotionChange,
|
|
109
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type MotionChangeDefinition = {
|
|
2
|
+
channelThreshold: number
|
|
3
|
+
minimumChangePercent: number
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
type MotionChangePixels = ArrayLike<number>
|
|
7
|
+
|
|
8
|
+
export type MotionChangeInput = {
|
|
9
|
+
baseline?: MotionChangePixels
|
|
10
|
+
candidate?: MotionChangePixels
|
|
11
|
+
changePixels?: number
|
|
12
|
+
definition: MotionChangeDefinition
|
|
13
|
+
height?: number
|
|
14
|
+
installGlobal?: boolean
|
|
15
|
+
label?: string
|
|
16
|
+
totalPixels?: number
|
|
17
|
+
width?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type MotionChangeMeasurement = {
|
|
21
|
+
changePercent: number
|
|
22
|
+
changePixels: number
|
|
23
|
+
passedMinimum: boolean
|
|
24
|
+
totalPixels: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const MOTION_CHANGE_DEFINITION: Readonly<MotionChangeDefinition>
|
|
28
|
+
|
|
29
|
+
export function measureMotionChange(
|
|
30
|
+
input: MotionChangeInput,
|
|
31
|
+
): MotionChangeMeasurement | null
|
|
32
|
+
|
|
33
|
+
export function measureProofMotionChange(
|
|
34
|
+
input: Omit<MotionChangeInput, 'definition' | 'installGlobal'>,
|
|
35
|
+
): MotionChangeMeasurement
|
package/dist-cli/bin.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! sootsim v0.1.
|
|
2
|
+
/*! sootsim v0.1.298 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
3
3
|
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
4
4
|
var require = __sootsimCreateRequire(import.meta.url)
|
|
5
|
-
import{a as w,b as g}from"./chunks/chunk-
|
|
5
|
+
import{a as w,b as g}from"./chunks/chunk-MHCSRMK4.js";import"./chunks/chunk-4H3Y3TGT.js";function b(t){let a=t instanceof Error?t.message:String(t);(/^command timed out after \d+s$/.test(a)||a.startsWith("sim disconnected:")||a.startsWith("bridge never reconnected")||a.startsWith("multiple sims are connected:")||a.startsWith("saved sim ")||a.startsWith("no sim connected with id ")||a.startsWith("could not connect to ws://")||a.startsWith("sootsim bridge daemon is not running")||a.startsWith("sootsim bridge lockfile is fresh")||a.startsWith("sootsim bridge exited before becoming ready")||a.startsWith("background daemon setup is unavailable")||a.startsWith("sootsim bridge did not start within"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${a}
|
|
6
6
|
`),process.exit(1)),t instanceof Error&&t.stack?process.stderr.write(`${t.stack}
|
|
7
7
|
`):process.stderr.write(`${a}
|
|
8
8
|
`),process.exit(1)}process.on("unhandledRejection",b);process.on("uncaughtException",b);var A=typeof __SOOTSIM_STANDALONE__<"u"&&__SOOTSIM_STANDALONE__,k=new Set(["desktop"]);function S(t){!A||!k.has(t)||(process.stderr.write(` sootsim ${t} isn't available in the standalone binary \u2014
|
|
9
9
|
it needs vite / electron / playwright from a project's node_modules.
|
|
10
10
|
run the npm package instead:
|
|
11
11
|
sootsim ${t}
|
|
12
|
-
`),process.exit(1))}var e=g(process.argv);if(!e.command&&e.commandArgs.length>0){let{resolveHiddenRuntimeAlias:t}=await import("./chunks/hidden-runtime-alias-
|
|
13
|
-
`)}var f=!1;async function v(){if(!f&&(f=!0,!(e.command==="cleanup"||e.command==="serve"||e.command==="agent-wrapper"||e.command==="daemon"&&e.commandArgs[0]==="uninstall")))try{let{isAutomaticSootsimCleanupComplete:t}=await import("./chunks/disk-cleanup-
|
|
12
|
+
`),process.exit(1))}var e=g(process.argv);if(!e.command&&e.commandArgs.length>0){let{resolveHiddenRuntimeAlias:t}=await import("./chunks/hidden-runtime-alias-4ZUCLCVJ.js");e=t(e)}var _=e.command==="cleanup"&&e.commandArgs.length===1&&e.commandArgs[0]==="--automatic-worker";if(!_){let{trackCliEvent:t}=await import("./chunks/telemetry-2UMKQXU7.js");t({event:"cli_command_invoked",properties:{command:e.command||(e.version?"version":e.help?"help":"none"),arg_count:e.commandArgs.length,platform:process.platform,arch:process.arch,node_version:process.versions.node}})}if(e.command!=="serve"&&e.command!=="daemon"){let{consumeRuntimeUpgradeNotice:t}=await import("./chunks/home-paths-PVZPREN7.js"),a=t();a&&process.stderr.write(` SootSim engine upgraded to v${a.to}`+(a.from?` (from v${a.from})`:"")+` \xB7 what's new: https://sootsim.com/changelog
|
|
13
|
+
`)}var f=!1;async function v(){if(!f&&(f=!0,!(e.command==="cleanup"||e.command==="serve"||e.command==="agent-wrapper"||e.command==="daemon"&&e.commandArgs[0]==="uninstall")))try{let{isAutomaticSootsimCleanupComplete:t}=await import("./chunks/disk-cleanup-3E7GYLGW.js");if(t())return;let{spawn:a}=await import("node:child_process"),s=["cleanup","--automatic-worker"],m=process.argv[1],d=A?a(process.execPath,s,{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env}):m?a(process.execPath,[m,...s],{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env}):a("sootsim",s,{detached:!0,stdio:["ignore","ignore","inherit"],env:process.env});d.once("error",p=>{process.stderr.write(` SootSim automatic cleanup will retry on the next run: ${p.message}
|
|
14
14
|
`)}),d.unref()}catch(t){process.stderr.write(` SootSim automatic cleanup will retry on the next run: ${t instanceof Error?t.message:String(t)}
|
|
15
|
-
`)}}async function n(t){let{flushCliTelemetry:a}=await import("./chunks/telemetry-
|
|
15
|
+
`)}}async function n(t){let{flushCliTelemetry:a}=await import("./chunks/telemetry-2UMKQXU7.js");await a(),await v(),process.exit(t)}if(e.version){let{getCliVersion:t}=await import("./chunks/cli-version-ECA6G6JQ.js"),{IS_BETA:a,BETA_LABEL:s}=await import("./chunks/beta-SCLWNOHI.js"),m=a?` \xB7 ${s}`:"";console.log(`sootsim v${t()}${m}`);let{readActiveRuntime:d}=await import("./chunks/home-paths-PVZPREN7.js"),p=d();console.log(p?`runtime v${p}`:"runtime not installed"),await n(0)}if(e.help&&!e.command){let{printHelp:t}=await import("./chunks/help-UE32FDEY.js");t(),await n(0)}var o=e.globalFlags.port,c=e.verbose,u=e.globalFlags.device,l=e.globalFlags.theme,C=e.globalFlags.driver,O=e.globalFlags.headless===!0,h=e.globalFlags.sim??e.globalFlags.session??e.globalFlags.tab,i=h?["--sim",h,...e.commandArgs]:e.commandArgs;if(u||l){let{settingsStore:t}=await import("./chunks/store-3Z2GSFGY.js"),a={};u&&(a.deviceModel=u),l&&(a.colorScheme=l),t.apply(a)}if(!e.command&&e.commandArgs.length===0){let{runSetup:t,shouldRunSetupForBareCommand:a}=await import("./chunks/setup-2CUXSDQH.js");a()&&(await t(["--from-welcome"]),await n(0));let{printHelp:s}=await import("./chunks/help-UE32FDEY.js");s(),await n(0)}var r=e.command??"";if(!r){if(e.commandArgs.length>0){let a=e.commandArgs.find(s=>!s.startsWith("-"))??e.commandArgs[0];console.error(` unknown command: ${a}`),console.error(" run `sootsim --help` to see the full surface."),await n(1)}let{printHelp:t}=await import("./chunks/help-UE32FDEY.js");t(),await n(0)}if(e.help||e.commandArgs.includes("--help")||e.commandArgs.includes("-h")){if(r==="skill"){let{runSkill:s}=await import("./chunks/skills-IILJVUMS.js");await s(e.commandArgs),await n(0)}let t=r==="do"||r==="get"||r==="debug"||r==="shell"||r==="perf"||r==="wait",a=e.commandArgs.find(s=>!s.startsWith("-"));if(!(r==="shell"||r==="perf"))if(t&&!a){let{printGroupHelp:s}=await import("./chunks/help-UE32FDEY.js");s(r)&&await n(0)}else{let{printCommandHelp:s}=await import("./chunks/help-UE32FDEY.js");s(t&&a?a:r,{prefer:t&&a?"verb":"command",group:t&&a?r:void 0}),await n(0)}}S(r);if(w.has(r)){let{runInspect:t}=await import("./chunks/inspect-QWR2U3DK.js");await t([r,...i],{port:o,verbose:c})}else switch(r){case"assert":{let{runAssert:t}=await import("./chunks/assert-NNKBTEO7.js");await t(e.commandArgs);break}case"detox":{let{runDetox:t}=await import("./chunks/detox-4RZ5UER3.js");await t(e.commandArgs,{port:o,verbose:c});break}case"maestro":{let{runMaestro:t}=await import("./chunks/maestro-33RGEXP4.js"),a=await t(i,{port:o,verbose:c});await n(typeof a=="number"?a:0)}case"record":{let{runRecord:t}=await import("./chunks/record-A4MRKJ2E.js");await t(i,{port:o,verbose:c});break}case"film":{let{runFilm:t}=await import("./chunks/film-Q7KZIGBH.js"),a=await t(i,{port:o,verbose:c});await n(typeof a=="number"?a:0)}case"storage":{let{runStorage:t}=await import("./chunks/storage-BUQQJAMD.js"),a=await t(i,{port:o,verbose:c});await n(typeof a=="number"?a:0)}case"perf":{let{runPerf:t}=await import("./chunks/perf-VW3UTYCK.js"),a=await t(i,{port:o,verbose:c});await n(typeof a=="number"?a:0)}case"screenshot":{let{runScreenshotCommand:t}=await import("./chunks/screenshot-command-6LROQJPJ.js"),a=await t(i,{port:o,verbose:c});await n(typeof a=="number"?a:0)}case"mode":{let{runMode:t}=await import("./chunks/mode-LZORZYWP.js");await t(i,{port:o,verbose:c});break}case"permissions":{let{runPermissions:t}=await import("./chunks/permissions-7JIR6QPO.js"),a=await t(i,{port:o,verbose:c});await n(a)}case"inspect":{let{runInspect:t}=await import("./chunks/inspect-QWR2U3DK.js");await t(i,{port:o,verbose:c});break}case"debug":{let{runDebug:t}=await import("./chunks/debug-MVPFRIQA.js");await t(i,{port:o,verbose:c});break}case"timeline":{let{runTimeline:t}=await import("./chunks/timeline-62RWUHSF.js");await t(i,{port:o,verbose:c});break}case"what-happened":{let{runWhatHappened:t}=await import("./chunks/what-happened-UKBSKIGA.js");await t(i,{port:o,verbose:c});break}case"open":{let{runOpenCommand:t}=await import("./chunks/control-BY6UAMKN.js");await t(i,{port:o});break}case"use":{let{runUseCommand:t}=await import("./chunks/control-BY6UAMKN.js");await t(i,{port:o});break}case"claim":{let{runClaimCommand:t}=await import("./chunks/control-BY6UAMKN.js");await t(i,{port:o});break}case"close":{let{runCloseCommand:t}=await import("./chunks/control-BY6UAMKN.js");await t(i,{port:o});break}case"device":{let{runDeviceCommand:t}=await import("./chunks/device-7R4BSFMS.js");await t(i,{port:o});break}case"compat":{let{runCompat:t}=await import("./chunks/compat-OM7ODBMO.js");await t(e.commandArgs);break}case"report-issue":{let{runReportIssue:t}=await import("./chunks/report-issue-JUWTB66Z.js"),a=await t(e.commandArgs);await n(a)}case"desktop":{let{runDesktop:t}=await import("./chunks/desktop-ZXH7NRHW.js");await t(e.commandArgs,{port:o,device:u,driver:C});break}case"login":{let{runLogin:t}=await import("./chunks/login-UMP65DJG.js");await t(e.commandArgs);break}case"logout":{let{runLogout:t}=await import("./chunks/logout-F45WBVJV.js");await t();break}case"auth":{let{runAuth:t}=await import("./chunks/auth-SF2WR2K2.js");await t(e.commandArgs);break}case"setup":{let{runSetup:t}=await import("./chunks/setup-2CUXSDQH.js");await t(e.commandArgs);break}case"serve":{let{runServe:t}=await import("./chunks/serve-64SRDCU5.js");await t(e.commandArgs,{port:o});break}case"daemon":{let{runDaemon:t}=await import("./chunks/daemon-BQ2T53ZA.js");await t(e.commandArgs,{port:o});break}case"runtime":{let{runRuntime:t}=await import("./chunks/runtime-7LSOODXL.js");await t(e.commandArgs);break}case"upgrade":case"update":{let{runUpgrade:t}=await import("./chunks/upgrade-GHXDTO2A.js");await t(e.commandArgs);break}case"version":{let{runVersion:t}=await import("./chunks/version-F4CKBMB6.js");await t(e.commandArgs);break}case"agent":{let{runAgentCommand:t}=await import("./chunks/agent-U3HXOT6B.js"),a=await t(e.commandArgs);await n(a)}case"agent-wrapper":{let{runAgentWrapper:t}=await import("./chunks/agent-wrapper-LJOPJUAN.js"),a=await t(e.commandArgs);await n(a)}case"skill":{let{runSkill:t}=await import("./chunks/skills-IILJVUMS.js");await t(e.commandArgs);break}case"app-fonts":{let{runAppFonts:t}=await import("./chunks/app-fonts-4EVLV2BX.js");await t(e.commandArgs);break}case"config":{let{runConfig:t}=await import("./chunks/config-ZIX6LPFG.js");await t(e.commandArgs);break}case"cleanup":{let{runCleanup:t}=await import("./chunks/cleanup-YZPCNPWE.js"),a=await t(e.commandArgs);await n(a)}default:console.error(` unknown command: ${r}`),console.error(" run `sootsim --help` to see the full surface."),await n(1)}await v();
|