sootsim 0.1.201 → 0.1.203
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/dist-cli/bin.js +5 -4
- package/dist-cli/chunks/{agent-SHGFOKZN.js → agent-PWM4XT5H.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-3O4ZW24C.js → agent-wrapper-V2BSTANB.js} +2 -2
- package/dist-cli/chunks/{app-fonts-KZOBGGZQ.js → app-fonts-7M4PTF6W.js} +2 -2
- package/dist-cli/chunks/{assert-RZCBKONA.js → assert-SEW5OJL3.js} +2 -2
- package/dist-cli/chunks/{auth-PBD7GLXL.js → auth-3YY55NBL.js} +2 -2
- package/dist-cli/chunks/beta-Y4ZJUKAM.js +2 -0
- package/dist-cli/chunks/{camera-5OOLGGLN.js → camera-CYXCASB3.js} +2 -2
- package/dist-cli/chunks/{chunk-HVSDT56M.js → chunk-4NHPXU3X.js} +1 -1
- package/dist-cli/chunks/{chunk-423YVBZK.js → chunk-62AKBZTO.js} +1 -1
- package/dist-cli/chunks/{chunk-WJ7YQAKC.js → chunk-6DN4BD7O.js} +38 -38
- package/dist-cli/chunks/{chunk-FITJFYBO.js → chunk-6M56UHEY.js} +2 -2
- package/dist-cli/chunks/{chunk-T6WXCSWP.js → chunk-A3FIOG7U.js} +2 -2
- package/dist-cli/chunks/{chunk-7672K2JN.js → chunk-B7VBDZ3U.js} +1 -1
- package/dist-cli/chunks/{chunk-63EDUOKZ.js → chunk-BBOFH45Q.js} +2 -2
- package/dist-cli/chunks/{chunk-SKGV3OSH.js → chunk-BJJXNQHO.js} +2 -2
- package/dist-cli/chunks/{chunk-7GG4TZ4L.js → chunk-BL67OSVN.js} +2 -2
- package/dist-cli/chunks/{chunk-EVCOHQPK.js → chunk-EBH65V62.js} +3 -3
- package/dist-cli/chunks/{chunk-NYNAI5PO.js → chunk-FF3C57T5.js} +2 -2
- package/dist-cli/chunks/{chunk-JEJV3UHZ.js → chunk-FUC7C5HS.js} +2 -2
- package/dist-cli/chunks/{chunk-XAFLM5JS.js → chunk-G5GRLVYA.js} +2 -2
- package/dist-cli/chunks/chunk-G5VOIXM7.js +1 -0
- package/dist-cli/chunks/{chunk-D6CTB275.js → chunk-GMVAXDHH.js} +1 -1
- package/dist-cli/chunks/{chunk-S2AQ4WGX.js → chunk-GTBF3M2H.js} +1 -1
- package/dist-cli/chunks/{chunk-RN662UDP.js → chunk-GYTATFQA.js} +2 -2
- package/dist-cli/chunks/{chunk-AHJ2WJST.js → chunk-JLOAWSX6.js} +1 -1
- package/dist-cli/chunks/{chunk-ENY46QHT.js → chunk-K3CRSIGS.js} +2 -2
- package/dist-cli/chunks/{chunk-SVM25PDV.js → chunk-LKJ5LA6K.js} +6 -6
- package/dist-cli/chunks/{chunk-SZVYOFMB.js → chunk-LTRNOX6N.js} +2 -2
- package/dist-cli/chunks/{chunk-3RAGDMGM.js → chunk-MQHSWXDS.js} +1 -1
- package/dist-cli/chunks/{chunk-SQXH3XGO.js → chunk-NHSO4473.js} +2 -2
- package/dist-cli/chunks/chunk-ODW7RHSD.js +97 -0
- package/dist-cli/chunks/{chunk-FPRZHFV2.js → chunk-OK7QEDHD.js} +2 -2
- package/dist-cli/chunks/{chunk-CHVGDYQJ.js → chunk-OXQOPTTB.js} +1 -1
- package/dist-cli/chunks/{chunk-F5TOTCUW.js → chunk-P4GJL7OH.js} +2 -2
- package/dist-cli/chunks/chunk-PELEQURX.js +4 -0
- package/dist-cli/chunks/chunk-QKDII5UB.js +1 -0
- package/dist-cli/chunks/chunk-R7GIVBTY.js +2 -0
- package/dist-cli/chunks/{chunk-GBUUYMPP.js → chunk-RVOF6RIE.js} +1 -1
- package/dist-cli/chunks/chunk-RZU5TDPJ.js +29 -0
- package/dist-cli/chunks/{chunk-HIWE4YYN.js → chunk-U6OCMLMU.js} +2 -2
- package/dist-cli/chunks/{chunk-MQEEYPOK.js → chunk-UJ64GJSZ.js} +1 -1
- package/dist-cli/chunks/{chunk-YOPEO6SA.js → chunk-UZGLGCLM.js} +1 -1
- package/dist-cli/chunks/{chunk-QTI33DV5.js → chunk-WSOZGPIX.js} +2 -2
- package/dist-cli/chunks/{chunk-CPVTNQAS.js → chunk-WVF2EDZF.js} +1 -1
- package/dist-cli/chunks/{chunk-ZSWQLDDQ.js → chunk-XYMOH3HP.js} +1 -1
- package/dist-cli/chunks/{chunk-WHNPGZX4.js → chunk-YJNJ5GVC.js} +3 -3
- package/dist-cli/chunks/{chunk-USGQNFAM.js → chunk-YP3NIRTK.js} +1 -1
- package/dist-cli/chunks/chunk-ZGM7N2AV.js +7 -0
- package/dist-cli/chunks/{chunk-NIDV74L2.js → chunk-ZRWVLKFL.js} +1 -1
- package/dist-cli/chunks/cli-version-N2RKZ2CU.js +2 -0
- package/dist-cli/chunks/{compat-NNM7DTTO.js → compat-FK5MP535.js} +3 -3
- package/dist-cli/chunks/{config-LBGQRC2P.js → config-T3W2HI67.js} +2 -2
- package/dist-cli/chunks/{control-5DHHQZ26.js → control-ONFLGDEC.js} +2 -2
- package/dist-cli/chunks/daemon-FDI5YYZN.js +2 -0
- package/dist-cli/chunks/{debug-PILWFGN3.js → debug-Z6NG4KY2.js} +3 -3
- package/dist-cli/chunks/{desktop-CZ43IZFV.js → desktop-BIWB2UYC.js} +3 -3
- package/dist-cli/chunks/detox-6K7WFLQO.js +50 -0
- package/dist-cli/chunks/{device-M26JCUHY.js → device-TPD5WAP2.js} +2 -2
- package/dist-cli/chunks/{diagnose-HZZMVPQZ.js → diagnose-NACPOTYX.js} +2 -2
- package/dist-cli/chunks/drivers-G2YZR6G6.js +2 -0
- package/dist-cli/chunks/flow-OAW2MZCD.js +2 -0
- package/dist-cli/chunks/help-MY2CBECX.js +2 -0
- package/dist-cli/chunks/home-paths-WWFDX6YU.js +2 -0
- package/dist-cli/chunks/inspect-ZPVPUSAK.js +2 -0
- package/dist-cli/chunks/install-desktop-QZWSSGJV.js +2 -0
- package/dist-cli/chunks/{login-VBCYZKJ6.js → login-WGCO2V4S.js} +4 -4
- package/dist-cli/chunks/{logout-J22GLKHX.js → logout-VWSSNYF5.js} +2 -2
- package/dist-cli/chunks/maestro-YED424Y6.js +96 -0
- package/dist-cli/chunks/{mode-XN46XIMK.js → mode-QCOM4UM4.js} +2 -2
- package/dist-cli/chunks/optional-demo-registry-3KZFUZZD.js +2 -0
- package/dist-cli/chunks/{perf-ZIYESQRA.js → perf-FZHCFAID.js} +2 -2
- package/dist-cli/chunks/{record-L74E3HCL.js → record-NF3PKBSW.js} +3 -3
- package/dist-cli/chunks/runtime-ETELTKFT.js +2 -0
- package/dist-cli/chunks/screenshot-command-KLPOA5TW.js +151 -0
- package/dist-cli/chunks/screenshots-capture-MCTZSXLE.js +41 -0
- package/dist-cli/chunks/{serve-CNDRR7XN.js → serve-XBWYWHMC.js} +18 -18
- package/dist-cli/chunks/{setup-XZDN7XMI.js → setup-W7EROACZ.js} +3 -3
- package/dist-cli/chunks/{skills-KJK67CVB.js → skills-RBGNF7GS.js} +2 -2
- package/dist-cli/chunks/{storage-UOLMRHLQ.js → storage-3SFE2DUE.js} +2 -2
- package/dist-cli/chunks/store-Z3CHVRR3.js +2 -0
- package/dist-cli/chunks/telemetry-SWFOAVWP.js +2 -0
- package/dist-cli/chunks/test-UP6VTFKE.js +69 -0
- package/dist-cli/chunks/{timeline-UJFIJ2GT.js → timeline-A54JB74A.js} +2 -2
- package/dist-cli/chunks/upgrade-H7XE3N76.js +4 -0
- package/dist-cli/chunks/upload-X2BRKPBJ.js +2 -0
- package/dist-cli/chunks/version-4JV7HX23.js +4 -0
- package/dist-cli/chunks/{web-QP4KDHR6.js → web-5OLZXKVF.js} +2 -2
- package/dist-cli/chunks/{what-happened-VW274SXG.js → what-happened-Q4L6SI4I.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 +1 -1
- package/dist-lib/cli-constants.cjs +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 +32 -1
- package/dist-lib/host/bridge-host.cjs +51 -2
- 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 +104 -19
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/src/home-paths.ts +48 -1
- package/src/host/bridge-host.ts +12 -0
- package/src/runtime-delivery.ts +59 -9
- package/dist-cli/chunks/beta-EJBRLRKG.js +0 -2
- package/dist-cli/chunks/chunk-3W273A5J.js +0 -4
- package/dist-cli/chunks/chunk-5GICTRK5.js +0 -1
- package/dist-cli/chunks/chunk-AGSCFR4P.js +0 -27
- package/dist-cli/chunks/chunk-O56GZV3H.js +0 -1
- package/dist-cli/chunks/chunk-ORIISY22.js +0 -6
- package/dist-cli/chunks/chunk-PMPQ57UY.js +0 -92
- package/dist-cli/chunks/cli-version-WTA2LUDV.js +0 -2
- package/dist-cli/chunks/daemon-3KGXTPXJ.js +0 -2
- package/dist-cli/chunks/detox-D6XW7Y4V.js +0 -49
- package/dist-cli/chunks/drivers-7ZM3KW22.js +0 -2
- package/dist-cli/chunks/flow-DDDWJ4KD.js +0 -2
- package/dist-cli/chunks/help-23GXSWP3.js +0 -2
- package/dist-cli/chunks/home-paths-746ZDH2I.js +0 -2
- package/dist-cli/chunks/inspect-T37ALZRL.js +0 -2
- package/dist-cli/chunks/install-desktop-FSY2ND5S.js +0 -2
- package/dist-cli/chunks/maestro-5LWJ2S6K.js +0 -96
- package/dist-cli/chunks/optional-demo-registry-YF4W5LPU.js +0 -2
- package/dist-cli/chunks/runtime-7WSR27U6.js +0 -2
- package/dist-cli/chunks/screenshot-command-ZCI5QE4T.js +0 -190
- package/dist-cli/chunks/store-FGCNPJBA.js +0 -2
- package/dist-cli/chunks/telemetry-QEO7INJC.js +0 -2
- package/dist-cli/chunks/test-5DK2XCRU.js +0 -69
- package/dist-cli/chunks/upgrade-CXZFJIH7.js +0 -4
- package/dist-cli/chunks/upload-ZFINR6K3.js +0 -2
- package/dist-cli/chunks/version-XMSYOSAX.js +0 -4
package/src/home-paths.ts
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
// │ └── sootsim-runtime-<version>.tar.gz
|
|
16
16
|
// ├── daemon.json lockfile: pid, ports, active runtime, heartbeat
|
|
17
17
|
// ├── dev-bridge.json live Vite dev bridge pointer for local CLI commands
|
|
18
|
-
//
|
|
18
|
+
// ├── runtime-upgraded.json one-shot notice written by the daemon's
|
|
19
|
+
// │ background updater, consumed (printed + deleted)
|
|
20
|
+
// │ by the next interactive CLI run
|
|
21
|
+
// └── config.json user prefs: runtime pin, update channel, cdn
|
|
22
|
+
// origin override, telemetry, engine settings
|
|
19
23
|
|
|
20
24
|
import { randomUUID } from 'node:crypto'
|
|
21
25
|
import fs from 'node:fs'
|
|
@@ -232,6 +236,49 @@ export function writeTelemetryEnabled(enabled: boolean): void {
|
|
|
232
236
|
writeSharedConfig({ telemetry: enabled })
|
|
233
237
|
}
|
|
234
238
|
|
|
239
|
+
// --- background-upgrade notice --------------------------------------------
|
|
240
|
+
//
|
|
241
|
+
// the daemon auto-updates the engine runtime in the background (hourly), so
|
|
242
|
+
// the only place its "updated to vX" line lands is the daemon's own stderr,
|
|
243
|
+
// which nobody reads. it also drops a one-shot notice file here; the next
|
|
244
|
+
// interactive CLI invocation prints it once and deletes it, so users learn
|
|
245
|
+
// their engine moved without having to run `sootsim version`.
|
|
246
|
+
|
|
247
|
+
export interface RuntimeUpgradeNotice {
|
|
248
|
+
from: string | null
|
|
249
|
+
to: string
|
|
250
|
+
at: number
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function runtimeUpgradeNoticePath(): string {
|
|
254
|
+
return path.join(sootsimHomeDir(), 'runtime-upgraded.json')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function writeRuntimeUpgradeNotice(notice: RuntimeUpgradeNotice): void {
|
|
258
|
+
ensureSootsimHome()
|
|
259
|
+
const tmp = `${runtimeUpgradeNoticePath()}.tmp`
|
|
260
|
+
fs.writeFileSync(tmp, `${JSON.stringify(notice)}\n`, 'utf8')
|
|
261
|
+
fs.renameSync(tmp, runtimeUpgradeNoticePath())
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** read + delete the pending upgrade notice, or null when none is pending.
|
|
265
|
+
* deleting first-read keeps the banner one-shot even across racing CLIs. */
|
|
266
|
+
export function consumeRuntimeUpgradeNotice(): RuntimeUpgradeNotice | null {
|
|
267
|
+
try {
|
|
268
|
+
const raw = fs.readFileSync(runtimeUpgradeNoticePath(), 'utf8')
|
|
269
|
+
fs.unlinkSync(runtimeUpgradeNoticePath())
|
|
270
|
+
const parsed = JSON.parse(raw) as Partial<RuntimeUpgradeNotice>
|
|
271
|
+
if (typeof parsed?.to !== 'string' || !parsed.to) return null
|
|
272
|
+
return {
|
|
273
|
+
from: typeof parsed.from === 'string' ? parsed.from : null,
|
|
274
|
+
to: parsed.to,
|
|
275
|
+
at: typeof parsed.at === 'number' ? parsed.at : 0,
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
return null
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
235
282
|
export function ensureSootsimHome(): void {
|
|
236
283
|
fs.mkdirSync(sootsimHomeDir(), { recursive: true })
|
|
237
284
|
fs.mkdirSync(runtimesDir(), { recursive: true })
|
package/src/host/bridge-host.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
writeActiveRuntime,
|
|
26
26
|
writeDaemonLockfile,
|
|
27
27
|
writeDevBridgeLockfile,
|
|
28
|
+
writeRuntimeUpgradeNotice,
|
|
28
29
|
writeSharedConfig,
|
|
29
30
|
type DaemonLockfile,
|
|
30
31
|
type DevBridgeLockfile,
|
|
@@ -1283,8 +1284,19 @@ export class SootSimBridgeHost {
|
|
|
1283
1284
|
}
|
|
1284
1285
|
return
|
|
1285
1286
|
}
|
|
1287
|
+
const previousVersion = this.activeRuntimeVersion
|
|
1286
1288
|
const active = this.setActiveRuntime(result.latestVersion)
|
|
1287
1289
|
process.stderr.write(`sootsim runtime updated to ${active.version} (${reason})\n`)
|
|
1290
|
+
// the daemon's stderr goes to a log nobody watches — drop a one-shot
|
|
1291
|
+
// notice so the next interactive CLI run tells the user their engine
|
|
1292
|
+
// moved (consumed + deleted by bin.ts).
|
|
1293
|
+
try {
|
|
1294
|
+
writeRuntimeUpgradeNotice({
|
|
1295
|
+
from: previousVersion,
|
|
1296
|
+
to: active.version,
|
|
1297
|
+
at: Date.now(),
|
|
1298
|
+
})
|
|
1299
|
+
} catch {}
|
|
1288
1300
|
} catch (err) {
|
|
1289
1301
|
process.stderr.write(
|
|
1290
1302
|
`sootsim runtime update failed (${reason}): ${
|
package/src/runtime-delivery.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
export const DEFAULT_RUNTIME_CDN_ORIGIN = 'https://sootbean.com'
|
|
18
18
|
export const RUNTIME_CDN_ORIGIN_ENV = 'SOOTSIM_CDN_ORIGIN'
|
|
19
19
|
export const RUNTIME_CHANNEL_ENV = 'SOOTSIM_RUNTIME_CHANNEL'
|
|
20
|
+
export const RUNTIME_PIN_ENV = 'SOOTSIM_RUNTIME_VERSION'
|
|
20
21
|
|
|
21
22
|
export interface RuntimeManifestChannel {
|
|
22
23
|
latest: string
|
|
@@ -70,6 +71,7 @@ export interface RuntimeUpdateResult {
|
|
|
70
71
|
|
|
71
72
|
interface SootsimConfig {
|
|
72
73
|
runtimeChannel?: string
|
|
74
|
+
runtimeVersion?: string
|
|
73
75
|
cdnOrigin?: string
|
|
74
76
|
}
|
|
75
77
|
|
|
@@ -97,6 +99,18 @@ export function resolveRuntimeChannel(input?: string): string {
|
|
|
97
99
|
return input || process.env[RUNTIME_CHANNEL_ENV] || config.runtimeChannel || 'stable'
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
/** pinned runtime version, or null when following the channel's latest.
|
|
103
|
+
* env wins over config so CI can pin per-invocation; the persistent pin
|
|
104
|
+
* lives in ~/.sootsim/config.json (`sootsim runtime pin <version>`). */
|
|
105
|
+
export function resolveRuntimePin(): string | null {
|
|
106
|
+
const env = process.env[RUNTIME_PIN_ENV]
|
|
107
|
+
if (env && env.trim()) return env.trim()
|
|
108
|
+
const config = readConfig()
|
|
109
|
+
return typeof config.runtimeVersion === 'string' && config.runtimeVersion.trim()
|
|
110
|
+
? config.runtimeVersion.trim()
|
|
111
|
+
: null
|
|
112
|
+
}
|
|
113
|
+
|
|
100
114
|
export function runtimeManifestUrl(cdnOrigin?: string): string {
|
|
101
115
|
const url = new URL(`${resolveRuntimeCdnOrigin(cdnOrigin)}/runtimes/manifest.json`)
|
|
102
116
|
// cdn edges can keep the no-query manifest around longer than its
|
|
@@ -123,7 +137,7 @@ export function resolveRuntimeVersion(
|
|
|
123
137
|
opts: Pick<RuntimeInstallOptions, 'version' | 'channel'> = {},
|
|
124
138
|
): { version: string; channel: string; entry: RuntimeManifestVersion } {
|
|
125
139
|
const channel = resolveRuntimeChannel(opts.channel)
|
|
126
|
-
const version = opts.version || manifest.channels[channel]?.latest
|
|
140
|
+
const version = opts.version || resolveRuntimePin() || manifest.channels[channel]?.latest
|
|
127
141
|
if (!version) {
|
|
128
142
|
throw new Error(
|
|
129
143
|
`no version specified and channel '${channel}' has no latest entry in the manifest`,
|
|
@@ -211,6 +225,34 @@ export async function updateRuntimeToLatest(
|
|
|
211
225
|
ensureSootsimHome()
|
|
212
226
|
const cdnOrigin = resolveRuntimeCdnOrigin(opts.cdnOrigin)
|
|
213
227
|
const channel = resolveRuntimeChannel(opts.channel)
|
|
228
|
+
|
|
229
|
+
// a pinned runtime never follows the channel. when the pin is already
|
|
230
|
+
// active + on disk this is a no-network no-op; otherwise install exactly
|
|
231
|
+
// the pinned version (it must still exist in the hosted manifest).
|
|
232
|
+
const pin = resolveRuntimePin()
|
|
233
|
+
if (pin) {
|
|
234
|
+
const activeVersion = readActiveRuntime()
|
|
235
|
+
const pinInstalled = fs.existsSync(path.join(runtimeDir(pin), 'index.html'))
|
|
236
|
+
if (activeVersion === pin && pinInstalled) {
|
|
237
|
+
return {
|
|
238
|
+
checked: true,
|
|
239
|
+
updated: false,
|
|
240
|
+
reason: `runtime is pinned to ${pin}`,
|
|
241
|
+
activeVersion,
|
|
242
|
+
latestVersion: pin,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const install = await installRuntime({ version: pin, channel, cdnOrigin, setActive: false })
|
|
246
|
+
return {
|
|
247
|
+
checked: true,
|
|
248
|
+
updated: true,
|
|
249
|
+
reason: `runtime is pinned to ${pin}`,
|
|
250
|
+
activeVersion: pin,
|
|
251
|
+
latestVersion: pin,
|
|
252
|
+
install,
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
214
256
|
const manifest = await fetchRuntimeManifest(cdnOrigin)
|
|
215
257
|
const latestVersion = manifest.channels[channel]?.latest
|
|
216
258
|
if (!latestVersion) {
|
|
@@ -272,27 +314,35 @@ export async function updateRuntimeToLatest(
|
|
|
272
314
|
export interface RuntimeUpToDateCheck {
|
|
273
315
|
active: string | null
|
|
274
316
|
latest: string | null
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
|
|
317
|
+
// pinned version when one is set — a pinned runtime is only "outdated"
|
|
318
|
+
// when the pin itself isn't active yet, never against channel latest.
|
|
319
|
+
pinned: string | null
|
|
320
|
+
// true only when a target version is known and it is newer than / not
|
|
321
|
+
// the active one (or nothing is active yet). network failures resolve
|
|
322
|
+
// to false so callers can treat this as a best-effort hint.
|
|
278
323
|
outdated: boolean
|
|
279
324
|
}
|
|
280
325
|
|
|
281
|
-
/** read-only check: is the active runtime the latest on its channel
|
|
282
|
-
* unlike updateRuntimeToLatest this never downloads
|
|
283
|
-
* for surfacing a "newer runtime available" hint at
|
|
326
|
+
/** read-only check: is the active runtime the latest on its channel (or,
|
|
327
|
+
* when pinned, the pin)? unlike updateRuntimeToLatest this never downloads
|
|
328
|
+
* or activates — it's for surfacing a "newer runtime available" hint at
|
|
329
|
+
* startup / in help. */
|
|
284
330
|
export async function checkRuntimeUpToDate(
|
|
285
331
|
opts: Pick<RuntimeInstallOptions, 'channel' | 'cdnOrigin'> = {},
|
|
286
332
|
): Promise<RuntimeUpToDateCheck> {
|
|
287
333
|
const active = readActiveRuntime()
|
|
334
|
+
const pinned = resolveRuntimePin()
|
|
335
|
+
if (pinned) {
|
|
336
|
+
return { active, latest: pinned, pinned, outdated: active !== pinned }
|
|
337
|
+
}
|
|
288
338
|
try {
|
|
289
339
|
const manifest = await fetchRuntimeManifest(resolveRuntimeCdnOrigin(opts.cdnOrigin))
|
|
290
340
|
const channel = resolveRuntimeChannel(opts.channel)
|
|
291
341
|
const latest = manifest.channels[channel]?.latest ?? null
|
|
292
342
|
const outdated = !!(latest && (!active || compareSemver(latest, active) > 0))
|
|
293
|
-
return { active, latest, outdated }
|
|
343
|
+
return { active, latest, pinned: null, outdated }
|
|
294
344
|
} catch {
|
|
295
|
-
return { active, latest: null, outdated: false }
|
|
345
|
+
return { active, latest: null, pinned: null, outdated: false }
|
|
296
346
|
}
|
|
297
347
|
}
|
|
298
348
|
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{D as v,E as f,F as R,H as w,l as p,u as S,x as O}from"./chunk-ORIISY22.js";import{spawn as I}from"child_process";import C from"crypto";import s from"fs";import l from"path";import{Readable as M}from"stream";import{pipeline as k}from"stream/promises";var N="https://sootbean.com",E="SOOTSIM_CDN_ORIGIN",T="SOOTSIM_RUNTIME_CHANNEL",q="https://sootsim.com/changelog";function $(){try{let t=JSON.parse(s.readFileSync(O(),"utf8"));return t&&typeof t=="object"?t:{}}catch{return{}}}function d(t){let e=$();return(t||process.env[E]||e.cdnOrigin||N).replace(/\/+$/,"")}function x(t){let e=$();return t||process.env[T]||e.runtimeChannel||"stable"}function D(t){let e=new URL(`${d(t)}/runtimes/manifest.json`);return e.searchParams.set("t",String(Date.now())),e.toString()}function U(t,e){return`${d(e)}/runtimes/sootsim-runtime-${t}.tar.gz`}async function y(t){let e=D(t),r=await fetch(e,{headers:{Accept:"application/json"}});if(!r.ok)throw new Error(`manifest fetch failed: ${r.status} ${r.statusText} (${e})`);return await r.json()}function P(t,e={}){let r=x(e.channel),n=e.version||t.channels[r]?.latest;if(!n)throw new Error(`no version specified and channel '${r}' has no latest entry in the manifest`);let i=t.versions[n];if(!i)throw new Error(`version ${n} not found in manifest; available: ${Object.keys(t.versions).slice(-10).join(", ")||"(none)"}`);return{version:n,channel:r,entry:i}}async function _(t={}){v();let e=d(t.cdnOrigin),r=await y(e),{version:n,channel:i,entry:a}=P(r,t),o=p(n),c=t.setActive!==!1;if(!t.force&&s.existsSync(l.join(o,"index.html")))return c&&R(n),{version:n,channel:i,cdnOrigin:e,runtimeDir:o,installed:!1,activated:c,manifest:r};let g=a.tarball||U(n,e),m=l.join(S(),`sootsim-runtime-${n}.tar.gz`);process.stderr.write(`sootsim: downloading runtime ${n}\u2026
|
|
3
|
-
`),await V(g,m),process.stderr.write(`sootsim: extracting runtime ${n}\u2026
|
|
4
|
-
`);let h=await A(m);if(h!==a.sha256)throw s.rmSync(m,{force:!0}),new Error(`sha256 mismatch for runtime ${n}: expected ${a.sha256}, actual ${h}`);let u=l.join(l.dirname(o),`.installing-${n}-${process.pid}`);s.rmSync(u,{recursive:!0,force:!0}),s.mkdirSync(u,{recursive:!0});try{if(await j(m,u),!s.existsSync(l.join(u,"index.html")))throw new Error(`extracted tarball for runtime ${n} is missing index.html`);s.rmSync(o,{recursive:!0,force:!0}),s.renameSync(u,o)}catch(b){throw s.rmSync(u,{recursive:!0,force:!0}),b}return c&&R(n),{version:n,channel:i,cdnOrigin:e,runtimeDir:o,installed:!0,activated:c,manifest:r}}async function B(t={}){v();let e=d(t.cdnOrigin),r=x(t.channel),n=await y(e),i=n.channels[r]?.latest;if(!i)return{checked:!0,updated:!1,reason:`channel '${r}' has no latest runtime`,activeVersion:f()};if(!n.versions[i])return{checked:!0,updated:!1,reason:`manifest is missing version ${i}`,activeVersion:f(),latestVersion:i};let o=f(),c=o?p(o):null,g=c?s.existsSync(l.join(c,"index.html")):!1;if(!(!o||!g||w(i,o)>0))return{checked:!0,updated:!1,reason:"active runtime is current",activeVersion:o,latestVersion:i};let h=await _({version:i,channel:r,cdnOrigin:e,setActive:!1});return{checked:!0,updated:!0,activeVersion:i,latestVersion:i,install:h}}async function K(t={}){let e=f();try{let r=await y(d(t.cdnOrigin)),n=x(t.channel),i=r.channels[n]?.latest??null,a=!!(i&&(!e||w(i,e)>0));return{active:e,latest:i,outdated:a}}catch{return{active:e,latest:null,outdated:!1}}}async function V(t,e){let r=await fetch(t);if(!r.ok||!r.body)throw new Error(`download failed: ${r.status} ${r.statusText} (${t})`);s.mkdirSync(l.dirname(e),{recursive:!0});let n=`${e}.partial`;try{await k(M.fromWeb(r.body),s.createWriteStream(n)),s.renameSync(n,e)}catch(i){try{s.unlinkSync(n)}catch{}throw i}}function A(t){return new Promise((e,r)=>{let n=C.createHash("sha256"),i=s.createReadStream(t);i.on("data",a=>n.update(a)),i.on("error",r),i.on("end",()=>e(n.digest("hex")))})}function j(t,e){return new Promise((r,n)=>{let i=I("tar",["-xzf",t,"-C",e],{stdio:["ignore","inherit","inherit"]});i.on("error",n),i.on("exit",a=>{a===0?r():n(new Error(`tar exited with code ${a}`))})})}export{N as a,q as b,d as c,x as d,y as e,_ as f,B as g,K as h};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{a as p,b as $,c as w,e as y,f as R}from"./chunk-3W273A5J.js";import{d}from"./chunk-HIWE4YYN.js";import{D as m,E as a,F as h,G as f,l as v}from"./chunk-ORIISY22.js";import b from"fs";import{WebSocket as A}from"ws";async function P(n,e={}){let[o,...t]=n;if(!o||o==="--help"||o==="-h"){O();return}switch(o){case"install":return D(t,e);case"list":case"ls":return N(t);case"use":return T(t);case"remove":case"rm":return L(t);case"which":case"active":return _();case"notes":case"changelog":return C(t);default:console.error(` unknown runtime subcommand: ${o}`),O(),process.exit(1)}}function O(){console.log(`
|
|
3
|
-
sootsim runtime \u2014 manage engine runtimes under ~/.sootsim/runtimes/
|
|
4
|
-
|
|
5
|
-
usage:
|
|
6
|
-
sootsim runtime install [version] install a version (default: channel latest)
|
|
7
|
-
sootsim runtime list show installed + available versions
|
|
8
|
-
sootsim runtime use <version> switch active runtime
|
|
9
|
-
sootsim runtime remove <version> delete an installed runtime
|
|
10
|
-
sootsim runtime which print active runtime version
|
|
11
|
-
sootsim runtime notes print hosted release notes URL
|
|
12
|
-
|
|
13
|
-
flags:
|
|
14
|
-
--channel <name> channel to resolve 'latest' from (default: stable)
|
|
15
|
-
--force reinstall even if the version is already on disk
|
|
16
|
-
--set-active=false do not switch active runtime after install
|
|
17
|
-
|
|
18
|
-
environment:
|
|
19
|
-
SOOTSIM_CDN_ORIGIN override the CDN base URL (default: ${p})
|
|
20
|
-
|
|
21
|
-
examples:
|
|
22
|
-
sootsim runtime install
|
|
23
|
-
sootsim runtime install 1.2.3
|
|
24
|
-
sootsim runtime install --channel beta
|
|
25
|
-
sootsim runtime use 1.2.3
|
|
26
|
-
sootsim runtime notes
|
|
27
|
-
`)}async function D(n,e){let{version:o,flags:t}=E(n),s=t.channel??e.channel??"stable",l=t.force===!0,c=t.setActive!==!1;m(),console.log("sootsim runtime install"),console.log(` cdn: ${w()}`);try{let i=await R({version:o,channel:s,force:l,setActive:c});console.log(` version: ${i.version} (channel: ${i.channel})`),i.installed?console.log(` installed ${i.version}`):console.log(` already installed at ${i.runtimeDir}`),c&&await I(i.version)}catch(i){console.error(` ${k(i)}`),process.exit(1)}}async function I(n){h(n),console.log(` active: ${n}`),await S(n)||console.log(` (no daemon running \u2014 next sootsim/electron launch will pick up ${n})`)}async function S(n){let{isDaemonLockfileFresh:e,readDaemonLockfile:o}=await import("./home-paths-746ZDH2I.js"),t=o();if(!e(t))return!1;let s=d();return new Promise(l=>{let c=!1,i=!1,u=x=>{c||(c=!0,l(x))},r=new A(`ws://127.0.0.1:${s}`,{handshakeTimeout:800}),g=setTimeout(()=>{try{r.close()}catch{}u(i)},1500);r.on("open",()=>{try{r.send(JSON.stringify({type:"runtime:use",version:n,id:0})),i=!0}catch{}setTimeout(()=>{try{r.close()}catch{}},100)}),r.on("close",()=>{clearTimeout(g),u(i)}),r.on("error",()=>{clearTimeout(g),u(!1)})})}async function N(n){m();let e=f(),o=a();if(console.log("installed:"),e.length===0)console.log(" (none)");else for(let t of e)console.log(` ${t===o?"*":" "} ${t}`);try{let t=await y();console.log("available (latest per channel):");for(let[s,l]of Object.entries(t.channels))console.log(` ${s.padEnd(8)} ${l.latest}`)}catch(t){console.log(`available: (could not fetch manifest: ${k(t)})`)}}async function T(n){let e=n[0];e||(console.error(" usage: sootsim runtime use <version>"),process.exit(1));let o=f();o.includes(e)||(console.error(` version ${e} is not installed`),console.error(` installed: ${o.join(", ")||"(none)"}`),console.error(` run \`sootsim runtime install ${e}\` first`),process.exit(1)),await I(e)}async function L(n){let e=n[0];e||(console.error(" usage: sootsim runtime remove <version>"),process.exit(1)),a()===e&&(console.error(` cannot remove active runtime ${e}`),console.error(" switch with `sootsim runtime use <other>` first, or install another version"),process.exit(1));let t=v(e);if(!b.existsSync(t)){console.error(` ${e} is not installed`);return}b.rmSync(t,{recursive:!0,force:!0}),console.log(` removed ${e}`)}async function _(){let n=a();if(!n){console.log(" no active runtime");return}console.log(n)}async function C(n){n.length>0&&(console.error(" usage: sootsim runtime notes"),process.exit(1));let e=a();e&&console.log(` active runtime: v${e}`),console.log(` release notes: ${$}`)}function E(n){let e={},o=[];for(let t=0;t<n.length;t++){let s=n[t];if(s==="--channel"&&t+1<n.length){e.channel=n[t+1],t++;continue}if(s.startsWith("--channel=")){e.channel=s.slice(10);continue}if(s==="--force"){e.force=!0;continue}if(s==="--set-active=false"||s==="--no-set-active"){e.setActive=!1;continue}o.push(s)}return{version:o[0]??null,flags:e}}function k(n){return n instanceof Error?n.message:String(n)}export{P as a};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{randomUUID as I}from"node:crypto";import t from"node:fs";import{homedir as E}from"node:os";import o from"node:path";var _="SOOTSIM_HOME",N="active",P="daemon.json",j="dev-bridge.json",F="config.json",k=3e4;function u(){let e=process.env[_];return e&&e.length>0?o.resolve(e):o.join(E(),".sootsim")}function w(){if(process.env.SOOTSIM_FORCE_DAEMON_INSTALL==="1")return!1;let e=process.env.SOOTSIM_DEV;if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;let r=process.argv[1];if(!r)return!1;try{return t.realpathSync(r).includes(`${o.sep}packages${o.sep}sootsim${o.sep}`)}catch{return!1}}function T(){return process.env.SOOTSIM_FORCE_DAEMON_INSTALL==="1"?!1:process.env.IS_TAMAGUI_DEV==="1"}function q(){return w()||T()}function f(){return o.join(u(),"runtimes")}function B(e){return o.join(f(),e)}function L(){return o.join(f(),N)}function D(){return o.join(u(),"electron")}function z(){return o.join(D(),"userData")}function Q(e){return o.join(D(),e)}function C(){return o.join(u(),"profiles")}function M(){return o.join(u(),"daemon-app")}function R(){return o.join(M(),"SootSim Daemon.app")}function Z(){return o.join(R(),"Contents","MacOS","sootsim-daemon")}function $(){return o.join(u(),"cache")}function p(){return o.join(u(),P)}function m(){return o.join(u(),j)}function b(){return o.join(u(),F)}function h(){try{let e=t.readFileSync(b(),"utf8"),r=JSON.parse(e);return r&&typeof r=="object"?r:{}}catch{return{}}}function O(e){g();let r=h(),i={...r,...e};e.settings&&typeof e.settings=="object"&&(i.settings={...r.settings&&typeof r.settings=="object"?r.settings:{},...e.settings});let c=`${b()}.tmp`;return t.writeFileSync(c,`${JSON.stringify(i,null,2)}
|
|
3
|
-
`,"utf8"),t.renameSync(c,b()),i}function ee(){return h().telemetry!==!1}function re(){let e=h().anonymousId;if(typeof e=="string"&&e.trim())return e;let r=`anon-cli-${I()}`;return O({anonymousId:r}),r}function te(e){O({telemetry:e})}function g(){t.mkdirSync(u(),{recursive:!0}),t.mkdirSync(f(),{recursive:!0}),t.mkdirSync(D(),{recursive:!0}),t.mkdirSync(C(),{recursive:!0}),t.mkdirSync($(),{recursive:!0})}function J(){try{let e=t.readFileSync(L(),"utf8").trim();return e.length>0?e:null}catch{return null}}function ne(e){t.mkdirSync(f(),{recursive:!0}),t.writeFileSync(L(),`${e}
|
|
4
|
-
`,"utf8")}function oe(){try{return t.readdirSync(f(),{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort(V)}catch{return[]}}function V(e,r){let i=s=>{let a=s.indexOf("-"),l=a>=0?s.slice(0,a):s,A=a>=0?s.slice(a+1):"",x=l.split(".").map(S=>Number.parseInt(S,10));return x.some(S=>!Number.isFinite(S))?[[Number.POSITIVE_INFINITY],s]:[x,A]},[c,n]=i(e),[v,y]=i(r);for(let s=0;s<Math.max(c.length,v.length);s++){let a=c[s]??0,l=v[s]??0;if(a!==l)return a-l}return n===y?0:n?y?n<y?-1:1:-1:1}function ie(){let e=J();if(!e)return null;let r=B(e);try{if(t.statSync(r).isDirectory())return r}catch{}return null}var d=16*1024;function H(){try{let e=t.openSync(p(),"r");try{let r=Buffer.alloc(d),i=t.readSync(e,r,0,d,0),c=r.subarray(0,i).toString("utf8"),n=JSON.parse(c);return n&&n.schema===1&&typeof n.pid=="number"&&typeof n.bridgePort=="number"&&typeof n.runtimePort=="number"&&typeof n.startedAt=="number"&&typeof n.heartbeatAt=="number"?n:null}finally{t.closeSync(e)}}catch{return null}}function se(){try{let e=t.openSync(m(),"r");try{let r=Buffer.alloc(d),i=t.readSync(e,r,0,d,0),c=r.subarray(0,i).toString("utf8"),n=JSON.parse(c);return n&&n.schema===1&&n.source==="vite-dev"&&typeof n.pid=="number"&&typeof n.bridgePort=="number"&&typeof n.runtimePort=="number"&&typeof n.cwd=="string"&&typeof n.startedAt=="number"&&typeof n.heartbeatAt=="number"&&(n.shellPort===void 0||typeof n.shellPort=="number")?n:null}finally{t.closeSync(e)}}catch{return null}}function U(e,r=Date.now()){if(!e||r-e.heartbeatAt>k)return!1;try{return process.kill(e.pid,0),!0}catch{return!1}}function ce(e,r=Date.now()){if(!e||r-e.heartbeatAt>k)return!1;try{return process.kill(e.pid,0),!0}catch{return!1}}function G(e){g();let r=`${p()}.tmp`;t.writeFileSync(r,`${JSON.stringify(e,null,2)}
|
|
5
|
-
`,"utf8"),t.renameSync(r,p())}function ue(e){g();let r=`${m()}.tmp`;t.writeFileSync(r,`${JSON.stringify(e,null,2)}
|
|
6
|
-
`,"utf8"),t.renameSync(r,m())}function ae(e){g();let r=H();return r&&U(r)&&r.pid!==e.pid?!1:(G(e),!0)}function fe(){try{t.unlinkSync(p())}catch{}}function le(){try{t.unlinkSync(m())}catch{}}export{_ as a,N as b,P as c,j as d,F as e,k as f,u as g,w as h,T as i,q as j,f as k,B as l,L as m,D as n,z as o,Q as p,C as q,M as r,R as s,Z as t,$ as u,p as v,m as w,b as x,h as y,O as z,ee as A,re as B,te as C,g as D,J as E,ne as F,oe as G,V as H,ie as I,H as J,se as K,U as L,ce as M,G as N,ue as O,ae as P,fe as Q,le as R};
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{a as ke,b as Ee,c as Re}from"./chunk-FPRZHFV2.js";import{c as Te}from"./chunk-D6CTB275.js";import{d as we}from"./chunk-S2AQ4WGX.js";import{g as Se,i as ve,k as x}from"./chunk-HIWE4YYN.js";import{d as xe}from"./chunk-YOPEO6SA.js";import{a as Me,b as v}from"./chunk-QTI33DV5.js";import{a as be}from"./chunk-FITJFYBO.js";import{createHash as ut}from"crypto";import{readFileSync as L}from"fs";import{gzipSync as Pe,gunzipSync as ct}from"zlib";function Ie(e){let t=(e||"").toLowerCase().split(";")[0].trim();return t?!(t.startsWith("image/")||t.startsWith("video/")||t.startsWith("audio/")||t==="font/woff2"||t==="application/font-woff2"||t==="application/zip"||t==="application/gzip"):!0}import{brotliCompressSync as Xe,constants as Ze}from"node:zlib";var et=9;function Be(e){return Xe(e,{params:{[Ze.BROTLI_PARAM_QUALITY]:et}})}function te(e,t){let r=new AbortController,o=setTimeout(()=>r.abort(new Error(`${t} timed out after ${e/1e3}s`)),e);return{signal:r.signal,cancel:()=>clearTimeout(o)}}async function tt(e){let t=te(12e4,`PUT ${e.url}`),r;try{r=await fetch(e.url,{method:"PUT",headers:{"content-type":e.contentType},body:e.bytes,signal:t.signal})}finally{t.cancel()}if(!r.ok){let o=await r.text().catch(()=>"");throw new Error(`PUT ${e.url} failed ${r.status}: ${o.slice(0,200)}`)}}async function nt(e,t=8,r){let o=0,i=0,a=e.length;await Promise.all(Array.from({length:Math.min(t,a)},async()=>{for(;o<a;){let u=e[o++];await tt(u),i++,r?.(i,a)}}))}function rt(e,t,r,o){let i=[];i.push({url:e.bundle.putUrl,contentType:e.bundle.contentType,bytes:t});for(let a of e.files){let u=r.get(a.urlhash);u&&i.push({url:a.putUrl,contentType:a.contentType,bytes:u})}e.events&&o?.eventsBytes&&i.push({url:e.events.putUrl,contentType:e.events.contentType,bytes:o.eventsBytes}),e.timeline&&o?.timelineBytes&&i.push({url:e.timeline.putUrl,contentType:e.timeline.contentType,bytes:o.timelineBytes}),e.flowVideo&&o?.videoBytes&&i.push({url:e.flowVideo.putUrl,contentType:e.flowVideo.contentType,bytes:o.videoBytes}),e.storage&&o?.storageBytes&&i.push({url:e.storage.putUrl,contentType:e.storage.contentType,bytes:o.storageBytes});for(let a of e.snapshots??[]){let u=o?.snapshotBytes?.get(a.id);u&&i.push({url:a.putUrl,contentType:a.contentType,bytes:u})}return i}function st(e){return e.replace(/\/+$/,"")}var z=class extends Error{constructor(r,o,i,a=""){super(r);this.status=o;this.phase=i;this.responseText=a;this.name="PresignedUploadError"}};async function Ae(e){let t=st(e.originBase),r={"content-type":"application/json"};e.authHeader&&(r.authorization=e.authHeader),e.adminBypassToken&&(r["x-soot-admin-upload-bypass"]=e.adminBypassToken);let o=te(3e4,"init"),i;try{i=await fetch(`${t}/api/preview/upload/init`,{method:"POST",headers:r,body:JSON.stringify(e.initBody),signal:o.signal})}finally{o.cancel()}if(!i.ok){let f=await i.text().catch(()=>"");throw new z(`init failed: ${i.status} ${f.slice(0,200)}`,i.status,"init",f)}let a=await i.json(),u=rt(a,e.bundleBytes,e.filesByHash,e.extras);await nt(u,e.concurrency??8,e.onPutProgress);let y=te(3e4,"finalize"),m;try{m=await fetch(`${t}/api/preview/upload/finalize`,{method:"POST",headers:r,body:JSON.stringify({token:a.token}),signal:y.signal})}finally{y.cancel()}if(!m.ok){let f=await m.text().catch(()=>"");throw new z(`finalize failed: ${m.status} ${f.slice(0,200)}`,m.status,"finalize",f)}let F=await m.json();return{init:a,finalize:F}}function ot(e){return e!==null&&typeof e=="object"?e:null}function Ue(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function ne(e){let t=ot(e);return t?t.schemaVersion===1&&Ue(t.t)!==void 0&&Ue(t.seq)!==void 0&&typeof t.context=="string"&&typeof t.kind=="string"&&"data"in t:!1}function it(e,t){let r=[];for(let o of e.split(`
|
|
3
|
-
`)){let i=o.trim();if(i)try{let a=JSON.parse(i);t(a)&&r.push(a)}catch{}}return r}function _e(e){return it(e,ne)}function dt(e){if(!e)return null;try{let t=JSON.parse(e);return typeof t.message=="string"&&t.message?t.message:null}catch{return null}}var Ce=process.env.SOOTSIM_UPLOAD_ORIGIN||"https://sootbean.com",Ne=process.env.SOOTSIM_PREVIEW_ORIGIN||"https://sootsim.com",se="http://localhost:3000",pt=2e3;async function mt(e){if(e)return e;if(process.env.SOOTSIM_UPLOAD_ORIGIN)return process.env.SOOTSIM_UPLOAD_ORIGIN;try{let t=await fetch(`${se}/api/preview/upload/init`,{method:"OPTIONS",signal:AbortSignal.timeout(pt)});if(t.ok||t.status===204||t.status===405)return se}catch{}return Ce}function U(e){return e.replace(/\/$/,"")}function ft(e){try{let r=new URL(e).hostname.replace(/^\[|\]$/g,"").toLowerCase();return r==="localhost"||r.endsWith(".localhost")||r==="0.0.0.0"||r==="::1"||/^127(?:\.\d{1,3}){3}$/.test(r)}catch{return!1}}function gt(e){try{let t=new URL(e).hostname.toLowerCase();return t==="sootbean.com"||t.endsWith(".sootbean.com")}catch{return!1}}function ht(e,t){return t?U(t):process.env.SOOTSIM_PREVIEW_ORIGIN?U(process.env.SOOTSIM_PREVIEW_ORIGIN):ft(e)?U(e):gt(e)?U(Ne):U(e)}function yt(){console.log(`
|
|
4
|
-
sootsim record upload \u2014 publish the current recorded bundle as a /preview/<id> link
|
|
5
|
-
|
|
6
|
-
usage:
|
|
7
|
-
sootsim record upload [--origin <url>] [--public-origin <url>] (--events <path> | --video <path>) [--screenshot <path>] [--owner <org> --repo <repo>] [--sim <sim>] [--open] [--assets-only]
|
|
8
|
-
|
|
9
|
-
options:
|
|
10
|
-
--origin <url> upload target (default: auto)
|
|
11
|
-
prefers ${se} when available, otherwise
|
|
12
|
-
falls back to ${Ce}
|
|
13
|
-
override with SOOTSIM_UPLOAD_ORIGIN env var
|
|
14
|
-
--public-origin <url>
|
|
15
|
-
public /preview link origin (default: ${Ne}
|
|
16
|
-
for prod uploads, upload origin for localhost/custom origins).
|
|
17
|
-
override with SOOTSIM_PREVIEW_ORIGIN env var
|
|
18
|
-
--events <path> path to a gzipped events .jsonl.gz file to attach
|
|
19
|
-
required unless --video is present; previews must
|
|
20
|
-
have replay or recording playback data
|
|
21
|
-
--screenshot <path>
|
|
22
|
-
path to a PNG thumbnail to attach to the share
|
|
23
|
-
--video <path> path to a mp4/gif flow recording. embedded inline
|
|
24
|
-
in the pr sticky comment served at
|
|
25
|
-
/api/preview/flow-video?id=<share-id>
|
|
26
|
-
--video-duration-ms <ms>
|
|
27
|
-
duration hint for attached flow video
|
|
28
|
-
--timeline-events <path>
|
|
29
|
-
extra trace timeline rows, JSONL or JSONL gz
|
|
30
|
-
--trace-frame-stats <path>
|
|
31
|
-
frame stats JSON returned by the recorder bridge
|
|
32
|
-
--owner <org> associate a session upload with a linked org repo
|
|
33
|
-
--repo <repo> repo name for --owner; the signed-in user must belong
|
|
34
|
-
to the local/org team that owns the link
|
|
35
|
-
--billing-kind test_run
|
|
36
|
-
meter this upload as a natural-language test run
|
|
37
|
-
--sim <sim> target a specific sim (see: sootsim list)
|
|
38
|
-
--open open the resulting /preview/<id> url in the browser
|
|
39
|
-
--assets-only drop API/JSON/HTML records before upload; keep images,
|
|
40
|
-
fonts, css, js, and binary blobs. live-data demos hit
|
|
41
|
-
the real network at replay time instead of serving
|
|
42
|
-
recorded API snapshots
|
|
43
|
-
-h, --help
|
|
44
|
-
|
|
45
|
-
examples:
|
|
46
|
-
sootsim record upload --events ./my-session.jsonl.gz
|
|
47
|
-
sootsim record upload --origin http://localhost:3000 --events ./my-session.jsonl.gz --open
|
|
48
|
-
sootsim record upload --video /tmp/soot-flow.mp4 --video-duration-ms 12000
|
|
49
|
-
`)}function d(e,t){let r=e.findIndex(i=>i===t);if(r<0)return;let o=e[r+1];return e.splice(r,2),o}function Le(e,t){let r=e.findIndex(o=>o===t);return r<0?!1:(e.splice(r,1),!0)}function Fe(e,t){if(!e)return;let r=Number(e);return(!Number.isFinite(r)||r<=0)&&(console.error(` invalid ${t}: ${e}`),process.exit(1)),Math.round(r)}function $e(e){let t=(e||"").toLowerCase().split(";")[0].trim();return t?!!(t.startsWith("image/")||t.startsWith("font/")||t.startsWith("video/")||t.startsWith("audio/")||t.startsWith("model/")||t==="text/css"||t==="application/javascript"||t==="text/javascript"||t==="application/wasm"||t==="application/font-woff"||t==="application/font-woff2"||t==="application/octet-stream"):!1}async function Oe(e){let t=await fetch(e);if(!t.ok)throw new Error(`fetch ${e} -> ${t.status} ${t.statusText}`);return new Uint8Array(await t.arrayBuffer())}function bt(e){return/\.bundle($|\?)/.test(e)||/\.js($|\?)/.test(e)?"application/javascript":/\.zip($|\?)/.test(e)?"application/zip":"application/javascript"}function Mt(e){try{return new URL(e).searchParams.get("appFonts")||""}catch{return""}}function wt(e){try{return new URL(e).searchParams.get("appSplash")||""}catch{return""}}function Tt(e){try{let t=new URL(e);return t.searchParams.get("lazy")==="true"?(t.searchParams.set("lazy","false"),t.toString()):e}catch{return e}}async function St(e,t){let o=[];for(let i=0;i<t;i+=15e5){let a=Math.min(i+15e5,t),u=await x(e,`(window.__sootsimLastTransformedBundle?.text || "").slice(${i}, ${a})`);if(typeof u!="string")return null;o.push(u)}return o.join("")}function vt(e){return e.recordedDurationMs&&e.recordingStartedAtMs?{since:e.recordingStartedAtMs-1e4,until:e.recordingStartedAtMs+e.recordedDurationMs+5e3,limit:2e3}:{limit:2e3}}function xt(e){let t=L(e),o=(t[0]===31&&t[1]===139?ct(t):t).toString("utf8");return _e(o)}function kt(e){return JSON.parse(L(e,"utf8"))}function Et(e){if(e===null||typeof e!="object")return[];let t=e,r=typeof t.startedAtEpochMs=="number"&&t.startedAtEpochMs>0?t.startedAtEpochMs:null;if(r===null||!Array.isArray(t.samples))return[];let o=[];for(let i of t.samples){if(!Array.isArray(i)||typeof i[0]!="number")continue;let a=u=>typeof u=="number"&&Number.isFinite(u)?Math.round(u*100)/100:0;o.push({schemaVersion:1,t:r+Math.round(i[0]),seq:o.length+1,context:"tenant",kind:"frame",data:{totalMs:a(i[1]),layoutMs:a(i[2]),renderMs:a(i[3]),copyMs:a(i[4])}})}return o}function Rt(e){if(e===null||typeof e!="object")return e;let{samples:t,...r}=e;return r}function re(e){let t=new Map;for(let r of e)t.set(r.urlhash||r.url,r);return Array.from(t.values())}async function Wt(e,t){(e.includes("--help")||e.includes("-h"))&&(yt(),process.exit(0));let r=[...e],o=await mt(d(r,"--origin")),i=ht(o,d(r,"--public-origin")),a=d(r,"--events"),u=d(r,"--screenshot"),y=d(r,"--video"),m=d(r,"--timeline-events"),F=d(r,"--trace-frame-stats"),f=d(r,"--video-duration-ms"),oe=Fe(d(r,"--recorded-duration-ms"),"--recorded-duration-ms"),ie=Fe(d(r,"--recording-started-at-ms"),"--recording-started-at-ms"),H=d(r,"--owner"),j=d(r,"--repo"),$=d(r,"--billing-kind"),De=Le(r,"--open");(H&&!j||!H&&j)&&(console.error(" --owner and --repo must be provided together"),process.exit(1)),$&&$!=="test_run"&&(console.error(` invalid --billing-kind: ${$}`),process.exit(1)),!a&&!y&&(console.error(" preview uploads require playback data: pass --events or --video.\n for a normal local preview, use `sootsim record --mode combined --open`\n or `sootsim flow --preview` instead of uploading a bundle-only snapshot."),process.exit(1));let ae=Le(r,"--assets-only"),ze=Se(r,{stripBooleanFlags:[],stripValueFlags:[]}),b=ve(ze),c,le=[],M=[],w=m?xt(m):[],ue=F?kt(F):null;w.push(...Et(ue));let ce=w.length,J=null;try{if(c=await x(b,'(typeof window.__sootsimCaptureBundle === "function") ? window.__sootsimCaptureBundle() : null'),c?.bundleUrl){let l=new URL(c.bundleUrl).origin,[B,X]=await Promise.all([x(b,`(window.__sootsimPreviewRecorder?.list?.(${JSON.stringify(l)}) || [])`),x(b,`(async () => {
|
|
50
|
-
const list = window.__sootsimListWorkerFetchRecorder
|
|
51
|
-
return typeof list === 'function'
|
|
52
|
-
? await list(${JSON.stringify(l)})
|
|
53
|
-
: []
|
|
54
|
-
})()`)]);le=re([...B,...X]).filter(p=>{try{return!new URL(p.url).pathname.startsWith("/api/")}catch{return!0}});let A=await x(b,`(async () => {
|
|
55
|
-
const rec = window.__sootsimPreviewRecorder
|
|
56
|
-
const workerList = window.__sootsimListWorkerFetchRecorder
|
|
57
|
-
return {
|
|
58
|
-
main: rec?.list ? rec.list() : [],
|
|
59
|
-
worker: typeof workerList === 'function' ? await workerList() : [],
|
|
60
|
-
}
|
|
61
|
-
})()`),P=(A?re([...A.main,...A.worker]):[]).filter(p=>{try{return new URL(p.url).origin!==l}catch{return!1}}),S=P.length>0?{count:P.length,totalBytes:P.reduce((p,Z)=>p+(Z.size||0),0)}:null;if(S&&S.count>0){console.log(` ${S.count} recorded cross-origin responses (${(S.totalBytes/1024).toFixed(1)} KiB)`);let p=await x(b,`(async () => {
|
|
62
|
-
const rec = window.__sootsimPreviewRecorder
|
|
63
|
-
const workerDump = window.__sootsimDumpWorkerFetchRecorder
|
|
64
|
-
const bundleUrl = ${JSON.stringify(c.bundleUrl)}
|
|
65
|
-
const keep = (r) => {
|
|
66
|
-
try {
|
|
67
|
-
const u = new URL(r.url)
|
|
68
|
-
if (u.origin !== ${JSON.stringify(l)}) return true
|
|
69
|
-
// bundle-origin api responses came from the rewritten
|
|
70
|
-
// /__app-api proxy at record time (re-keyed by the
|
|
71
|
-
// recorder's unwrap step). they need bodies attached the
|
|
72
|
-
// same way as cross-origin records \u2014 direct re-fetch from
|
|
73
|
-
// the dev server lacks the bundle's session cookies.
|
|
74
|
-
if (r.url !== bundleUrl && u.pathname.startsWith('/api/')) return true
|
|
75
|
-
return false
|
|
76
|
-
} catch { return false }
|
|
77
|
-
}
|
|
78
|
-
return {
|
|
79
|
-
main: rec?.dump ? rec.dump().filter(keep) : [],
|
|
80
|
-
worker: typeof workerDump === 'function'
|
|
81
|
-
? (await workerDump()).filter(keep)
|
|
82
|
-
: [],
|
|
83
|
-
}
|
|
84
|
-
})()`);if(M=re([...p.main,...p.worker]),ae){let Z=M.length;M=M.filter(ee=>$e(ee.contentType));let Ye=S.totalBytes-M.reduce((ee,Qe)=>ee+(Qe.size||0),0);console.log(` --assets-only: kept ${M.length}/${Z} records (dropped ${(Ye/1024).toFixed(1)} KiB of API responses)`)}}}c?.transformedBundle&&(J=await St(b,c.transformedBundle.byteLength));let n=await x(b,`(() => {
|
|
85
|
-
const bridge = window.SootSim?.bridges?.timeline ?? window.__sootsimTimeline
|
|
86
|
-
if (!bridge || typeof bridge.recent !== 'function') return null
|
|
87
|
-
return bridge.recent(${JSON.stringify(vt({recordedDurationMs:oe,recordingStartedAtMs:ie}))})
|
|
88
|
-
})()`).catch(l=>(console.warn(` warning: failed to capture trace timeline: ${l instanceof Error?l.message:String(l)}`),null)),s=Array.isArray(n?.events)?n.events:[];w=[...w,...s.filter(ne)].sort((l,B)=>l.t-B.t||l.seq-B.seq)}finally{b.close()}c||(console.error(" could not read bundle snapshot \u2014 is sootsim running and is the bundle loaded?"),process.exit(2)),c.bundleUrl||(console.error(` no ?bundle= URL on the current sootsim tab.
|
|
89
|
-
open the app you want to share first (e.g. sootsim open 8082), then run upload.`),process.exit(2));let g,K=!1;if(J!==null)g=new TextEncoder().encode(J),K=!0,console.log(` using post-transform bundle: ${(g.byteLength/1024).toFixed(1)} KiB`);else{let n=Tt(c.bundleUrl);n!==c.bundleUrl&&console.log(" forcing lazy=false for self-contained bundle"),console.log(` capturing: ${n}`),g=await Oe(n),console.log(` main bundle: ${(g.byteLength/1024).toFixed(1)} KiB`)}let O=le.filter(n=>n.url!==c.bundleUrl);ae&&(O=O.filter(n=>$e(n.contentType))),console.log(` fetching ${O.length} extra files\u2026`);let C=(await Promise.all(O.map(async n=>{try{let s=await Oe(n.url);return{...n,bytes:s}}catch(s){return console.error(` warning: failed to re-fetch ${n.url}: ${s instanceof Error?s.message:s}`),null}}))).filter(n=>!!n),He=C.reduce((n,s)=>n+s.bytes.byteLength,0);console.log(` ${C.length} extra files: ${(He/1024).toFixed(1)} KiB`);let k;a&&(k=L(a)),w.sort((n,s)=>n.t-s.t||n.seq-s.seq);let de=w.map(n=>JSON.stringify(n)).join(`
|
|
90
|
-
`),V=Pe(de?`${de}
|
|
91
|
-
`:"");console.log(` trace timeline: ${w.length} events${ce?` (${ce} from runner sidecars)`:""}`);let h;u&&(h=L(u),console.log(` attaching screenshot: ${(h.byteLength/1024).toFixed(1)} KiB`));let T,_,pe;if(y){if(T=L(y),_=y.endsWith(".mp4")?"video/mp4":y.endsWith(".gif")?"image/gif":void 0,_||(console.error(" preview flow videos must be mp4 or gif"),await v(),process.exit(1)),f){let n=Number(f);(!Number.isFinite(n)||n<=0)&&(console.error(` invalid --video-duration-ms: ${f}`),await v(),process.exit(1)),pe=Math.round(n)}console.log(` attaching flow video: ${(T.byteLength/1024).toFixed(1)} KiB (${_})`)}let E=ke(),R=Re(E),je=be();Me({event:"preview_upload_started",identity:{userId:E?.kind==="session"?je?.user?.id??null:null,repoId:R?.repoId??null,installationId:R?.installationId??null},properties:{origin:o,hasAuth:!!E,authMode:E?.kind??"none",bundleBytes:g.byteLength,isTransformed:K,extraFiles:C.length,recordedFetches:M.length,hasEvents:!!k,traceTimelineEvents:w.length,hasScreenshot:!!h,hasVideo:!!T}}),E||(console.error(" preview uploads need auth."),console.error(" set SOOTSIM_API_KEY=sk_sootsim_..., run `sootsim login`, or use the soot github runner."),await v(),process.exit(1));let Je=Ee(E),Ke=(()=>{try{return new URL(c.bundleUrl).origin}catch{return null}})(),N=(n,s,l,B,X,A,ye,P)=>{let S=Ie(A),p=S?Pe(n):n;return{url:s,urlhash:l,method:B,bodyHash:X,requestVaryHash:P,contentType:A,responseHeaders:ye,bytes:p,encoding:S?"gzip":void 0}},me=[],q=Mt(c.pageHref)||"";if(q){let n=await we(q,{onStaged:({url:s,byteLength:l})=>console.log(` staged app font: ${s} (${(l/1024).toFixed(1)} KiB)`),onError:(s,l)=>console.error(` warning: failed to fetch app font ${s}: ${l instanceof Error?l.message:l}`)});for(let s of n)me.push(N(Buffer.from(s.bytes),s.url,s.urlhash,"GET","-",s.contentType))}let fe=[],W=wt(c.pageHref)||"";if(W){let n=await Te(W,{onStaged:({url:s,byteLength:l})=>console.log(` staged splash image: ${s} (${(l/1024).toFixed(1)} KiB)`),onError:(s,l)=>console.error(` warning: failed to fetch splash image ${s}: ${l instanceof Error?l.message:l}`)});for(let s of n)fe.push(N(Buffer.from(s.bytes),s.url,s.urlhash,"GET","-",s.contentType))}let G=[...C.map(n=>N(n.bytes,n.url,n.urlhash,"GET","-",n.contentType,n.responseHeaders,n.requestVaryHash)),...M.map(n=>N(Buffer.from(n.bodyBase64,"base64"),n.url,n.urlhash,n.method||"GET",n.bodyHash||"-",n.contentType,n.responseHeaders,n.requestVaryHash)),...me,...fe],Y=Be(g),Ve=ut("sha256").update(g).digest("hex");console.log(` bundle raw=${(g.byteLength/1024).toFixed(1)} KiB br=${(Y.byteLength/1024).toFixed(1)} KiB`);let qe={contentHash:Ve,bundleSizeBytes:g.byteLength,bundleContentType:bt(c.bundleUrl),bundleEncoding:"br",bundleOrigin:Ke,runtimeVersion:c.runtimeVersion??void 0,appFonts:q||void 0,appSplash:W||void 0,entry:c.entry,isTransformed:K,deviceSpec:c.deviceSpec,installationId:R?.installationId??void 0,repoId:R?.repoId,owner:H??R?.owner,repo:j??R?.repo,billingKind:$==="test_run"?"test_run":void 0,files:G.map(n=>({url:n.url,urlhash:n.urlhash,method:n.method,bodyHash:n.bodyHash,contentType:n.contentType,responseHeaders:n.responseHeaders,requestVaryHash:n.requestVaryHash,encoding:n.encoding,sizeBytes:n.bytes.byteLength})),events:k?{sizeBytes:k.byteLength}:void 0,timeline:{sizeBytes:V.byteLength,encoding:"gzip"},traceFrameStats:Rt(ue),recordedDurationMs:oe,recordingStartedAtMs:ie,snapshots:h?[{id:"landing",label:"Landing",kind:"landing",t:0,contentType:"image/png",sizeBytes:h.byteLength}]:void 0,flowVideo:T&&_?{sizeBytes:T.byteLength,contentType:_,durationMs:pe}:void 0},ge=new Map;for(let n of G)ge.set(n.urlhash,n.bytes);let he=new Map;h&&he.set("landing",h);let We=Date.now(),I=Y.byteLength;for(let n of G)I+=n.bytes.byteLength;k&&(I+=k.byteLength),I+=V.byteLength,h&&(I+=h.byteLength),T&&(I+=T.byteLength);let Ge=`${U(o)}/api/preview/upload/init`;console.log(` init: ${Ge}`);let D;try{let n=await Ae({originBase:o,initBody:qe,bundleBytes:Y,filesByHash:ge,extras:{eventsBytes:k,timelineBytes:V,videoBytes:T,snapshotBytes:he},authHeader:Je,concurrency:16});D=n.finalize;let s=1+n.init.files.length+(n.init.events?1:0)+(n.init.timeline?1:0)+n.init.snapshots.length+(n.init.flowVideo?1:0);console.log(` PUT ${s} objects in ${Date.now()-We}ms (${(I/1024).toFixed(1)} KiB)`)}catch(n){let s=n;s.phase==="init"&&s.status===401&&(console.error(" preview upload requires a valid login."),console.error(" run `sootsim login` and retry."),await v(),process.exit(1)),s.status===402&&(console.error(` ${dt(s.responseText)??"preview uploads require Personal, Team, or an active trial \u2014 upgrade from the billing dialog."}`),await v(),process.exit(1)),console.error(` ${s.message??`${s.phase??"upload"} failed: ${String(n)}`}`),await v(),process.exit(1)}let Q=`${i}${D.url}`;return console.log(`
|
|
92
|
-
stored ${D.filesStored??0} extra files`),console.log(` preview: ${Q}`),De&&await xe(Q),await v(),{previewUrl:Q,shareId:D.id}}export{mt as a,ht as b,Wt as c};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{a,b,c,d,e}from"./chunk-SQXH3XGO.js";import"./chunk-QTI33DV5.js";import"./chunk-FITJFYBO.js";import"./chunk-SKGV3OSH.js";import"./chunk-RN662UDP.js";import"./chunk-SVM25PDV.js";import"./chunk-T6WXCSWP.js";import"./chunk-AHJ2WJST.js";import"./chunk-CHVGDYQJ.js";import"./chunk-ORIISY22.js";import"./chunk-3RAGDMGM.js";import"./chunk-MQEEYPOK.js";import"./chunk-423YVBZK.js";import"./chunk-NIDV74L2.js";export{c as daemonInstall,b as getDaemonServiceStatus,a as runDaemon,d as teardownDaemonService,e as teardownSootsimHome};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{d as S}from"./chunk-WJ7YQAKC.js";import"./chunk-WHNPGZX4.js";import"./chunk-XAFLM5JS.js";import"./chunk-SZVYOFMB.js";import"./chunk-PMPQ57UY.js";import"./chunk-FPRZHFV2.js";import"./chunk-ENY46QHT.js";import"./chunk-63EDUOKZ.js";import"./chunk-O56GZV3H.js";import"./chunk-D6CTB275.js";import"./chunk-S2AQ4WGX.js";import"./chunk-HVSDT56M.js";import"./chunk-F5TOTCUW.js";import"./chunk-ZSWQLDDQ.js";import"./chunk-HIWE4YYN.js";import"./chunk-7GG4TZ4L.js";import"./chunk-CPVTNQAS.js";import"./chunk-YOPEO6SA.js";import"./chunk-GBUUYMPP.js";import"./chunk-NYNAI5PO.js";import"./chunk-QTI33DV5.js";import"./chunk-FITJFYBO.js";import"./chunk-SVM25PDV.js";import"./chunk-T6WXCSWP.js";import"./chunk-AHJ2WJST.js";import"./chunk-CHVGDYQJ.js";import"./chunk-ORIISY22.js";import"./chunk-423YVBZK.js";import"./chunk-NIDV74L2.js";import{spawn as D}from"child_process";import{existsSync as u,mkdirSync as _,writeFileSync as f,unlinkSync as I}from"fs";import{tmpdir as P}from"os";import{dirname as $,resolve as n,join as M}from"path";function E(){try{let e=import.meta.resolve("sootsim/detox");return $(e.startsWith("file://")?e.slice(7):e)}catch{return n(import.meta.dirname,"..","..","detox")}}var F=`
|
|
3
|
-
sootsim detox \u2014 run detox-style tests against a sootsim shell
|
|
4
|
-
|
|
5
|
-
usage:
|
|
6
|
-
sootsim detox [testFiles...] run all tests under e2e/, test/e2e/, or detox/
|
|
7
|
-
sootsim detox --config <path> use a specific jest config
|
|
8
|
-
sootsim detox init scaffold sootsim-detox.config.cjs + sample test
|
|
9
|
-
sootsim detox --watch jest watch mode
|
|
10
|
-
sootsim detox --headed keep the sootsim shell window visible
|
|
11
|
-
sootsim detox -t <pattern> pass a jest --testNamePattern
|
|
12
|
-
sootsim detox --no-launch skip auto-launching a sootsim shell
|
|
13
|
-
|
|
14
|
-
sootsim detox auto-launches a sootsim shell if none is running on the
|
|
15
|
-
expected port. disable with --no-launch if you're managing the shell yourself.
|
|
16
|
-
`;async function B(e,s={}){if((e.includes("--help")||e.includes("-h"))&&(console.log(F),process.exit(0)),e[0]==="init")return U();let c=t=>{let o=e.indexOf(t);return o>=0?e[o+1]:void 0},r=t=>e.includes(t),w=r("--watch"),h=r("--headed"),O=r("--no-launch"),y=c("--config"),d=s.port||Number(process.env.SOOTSIM_PORT)||5173,j=e.filter((t,o)=>{if(t.startsWith("--")||t==="-t"||t==="-h")return!1;let m=e[o-1];return!(m==="--config"||m==="-t"||m==="--testNamePattern"||m==="--grep")}),T=e.filter(t=>t==="--runInBand"||t.startsWith("--maxWorkers=")),x=["e2e","test/e2e","detox"],l=process.env.SOOTSIM_TEST_DIR?n(process.cwd(),process.env.SOOTSIM_TEST_DIR):null;if(!l)for(let t of x){let o=n(process.cwd(),t);if(u(o)){l=o;break}}l||(console.error(` error: no detox tests found. expected one of: ${x.join(", ")}
|
|
17
|
-
run 'sootsim detox init' to scaffold a starter suite.`),process.exit(1)),O||await k(d);let i=["jest"],p=y||R(["sootsim-detox.config.cjs","jest.config.cjs","jest.config.js"]),a=null;if(p)i.push("--config",n(process.cwd(),p));else{let t=n(E(),"jest-preset.cjs");a=M(P(),`sootsim-detox-${process.pid}.config.cjs`);let o={rootDir:l,roots:["<rootDir>"],testMatch:["<rootDir>/**/*.test.ts","<rootDir>/**/*.test.js"]};f(a,`module.exports = { ...require(${JSON.stringify(t)}), ...${JSON.stringify(o)} }
|
|
18
|
-
`),i.push("--config",a)}w&&i.push("--watch");let g=c("-t")||c("--testNamePattern")||c("--grep");g&&i.push("-t",g),process.env.SOOTSIM_JEST_JSON==="1"&&i.push("--json"),process.env.SOOTSIM_JEST_OUTPUT_FILE&&i.push("--outputFile",process.env.SOOTSIM_JEST_OUTPUT_FILE),i.push(...T),i.push(...j),console.log(" sootsim detox"),console.log(` test dir: ${l}`),console.log(` port: ${d}${h?" (headed)":""}`),p&&console.log(` config: ${p}`);let v=D("npx",i,{cwd:process.cwd(),stdio:"inherit",env:{...process.env,SOOTSIM_TEST_DIR:l,SOOTSIM_PORT:String(d),SOOTSIM_HEADED:h?"1":"",SOOTSIM_URL:process.env.SOOTSIM_URL||`http://localhost:${d}`}}),b=await new Promise(t=>v.on("exit",o=>t(o||0)));if(a)try{I(a)}catch{}process.exit(b)}function R(e){for(let s of e)if(u(n(process.cwd(),s)))return s;return null}async function k(e){await S()||console.warn(` warn: no sootsim shell reachable on :${e}. start one with \`bun run dev:sootsim:shell\`
|
|
19
|
-
or pass --no-launch if you're managing the shell yourself.`)}function U(){let e=process.cwd(),s=n(e,"sootsim-detox.config.cjs");u(s)?console.log(` skip: ${s} already exists`):(f(s,`// sootsim detox jest config.
|
|
20
|
-
// extends the sootsim preset, which rewrites \`import ... from 'detox'\`
|
|
21
|
-
// to the sootsim driver and keeps your jest transforms intact.
|
|
22
|
-
|
|
23
|
-
/** @type {import('@jest/types').Config.InitialOptions} */
|
|
24
|
-
module.exports = {
|
|
25
|
-
preset: 'sootsim/detox/jest-preset',
|
|
26
|
-
rootDir: __dirname,
|
|
27
|
-
testMatch: ['<rootDir>/e2e/**/*.test.ts', '<rootDir>/e2e/**/*.test.js'],
|
|
28
|
-
}
|
|
29
|
-
`),console.log(` created ${s}`));let c=n(e,"e2e"),r=n(c,"example.test.ts");u(r)?console.log(` skip: ${r} already exists`):(_(c,{recursive:!0}),f(r,`// sample sootsim detox test. resolve \`detox\` via the jest preset in
|
|
30
|
-
// sootsim-detox.config.cjs \u2014 no extra imports required.
|
|
31
|
-
|
|
32
|
-
import { by, device, element, expect, waitFor } from 'detox'
|
|
33
|
-
|
|
34
|
-
describe('example', () => {
|
|
35
|
-
beforeAll(async () => {
|
|
36
|
-
await device.launchApp()
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('shows the welcome text', async () => {
|
|
40
|
-
await waitFor(element(by.text('Welcome'))).toBeVisible().withTimeout(5000)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('taps a button by id', async () => {
|
|
44
|
-
await element(by.id('start-button')).tap()
|
|
45
|
-
await expect(element(by.id('home-screen'))).toBeVisible()
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
`),console.log(` created ${r}`)),console.log(`
|
|
49
|
-
next:`),console.log(" sootsim detox # run the sample"),console.log(" sootsim detox --watch"),process.exit(0)}export{B as runDetox};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import"./chunk-5GICTRK5.js";import{f as a,g as b,h as c,i as d,j as e,k as f,l as g,m as h,n as i}from"./chunk-7GG4TZ4L.js";import"./chunk-CPVTNQAS.js";import"./chunk-YOPEO6SA.js";import"./chunk-CHVGDYQJ.js";import"./chunk-ORIISY22.js";import"./chunk-NIDV74L2.js";export{e as ALL_DRIVERS,i as buildDriverListRows,a as chromiumDriver,b as electronDriver,f as getAllDrivers,g as getDriver,c as playwrightDriver,h as resolveDriver,d as systemDriver};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{c,d,e,f,g}from"./chunk-WJ7YQAKC.js";import{c as a,d as b}from"./chunk-WHNPGZX4.js";import"./chunk-XAFLM5JS.js";import"./chunk-SZVYOFMB.js";import"./chunk-PMPQ57UY.js";import"./chunk-FPRZHFV2.js";import"./chunk-ENY46QHT.js";import"./chunk-63EDUOKZ.js";import"./chunk-O56GZV3H.js";import"./chunk-D6CTB275.js";import"./chunk-S2AQ4WGX.js";import"./chunk-HVSDT56M.js";import"./chunk-F5TOTCUW.js";import"./chunk-ZSWQLDDQ.js";import"./chunk-HIWE4YYN.js";import"./chunk-7GG4TZ4L.js";import"./chunk-CPVTNQAS.js";import"./chunk-YOPEO6SA.js";import"./chunk-GBUUYMPP.js";import"./chunk-NYNAI5PO.js";import"./chunk-QTI33DV5.js";import"./chunk-FITJFYBO.js";import"./chunk-SVM25PDV.js";import"./chunk-T6WXCSWP.js";import"./chunk-AHJ2WJST.js";import"./chunk-CHVGDYQJ.js";import"./chunk-ORIISY22.js";import"./chunk-423YVBZK.js";import"./chunk-NIDV74L2.js";export{d as discoverSootsimUrl,c as getLastFlowPreviewUploadResult,f as hoistLeadingSimFlag,a as parseFlowFile,g as runFlow,e as runFlowPlayback,b as validateFlowFile};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{a,b,c}from"./chunk-SKGV3OSH.js";import"./chunk-RN662UDP.js";import"./chunk-SVM25PDV.js";import"./chunk-T6WXCSWP.js";import"./chunk-AHJ2WJST.js";import"./chunk-CHVGDYQJ.js";import"./chunk-ORIISY22.js";import"./chunk-3RAGDMGM.js";import"./chunk-MQEEYPOK.js";import"./chunk-423YVBZK.js";import"./chunk-NIDV74L2.js";export{c as printCommandHelp,b as printGroupHelp,a as printHelp};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}from"./chunk-ORIISY22.js";import"./chunk-NIDV74L2.js";export{b as ACTIVE_RUNTIME_FILE,e as CONFIG_FILE,f as DAEMON_HEARTBEAT_STALE_MS,c as DAEMON_LOCKFILE,d as DEV_BRIDGE_LOCKFILE,a as SOOTSIM_HOME_ENV,I as activeRuntimeDir,m as activeRuntimeFile,u as cacheDir,P as claimDaemonLockfile,H as compareSemver,x as configFilePath,s as daemonAppBundlePath,r as daemonAppDir,t as daemonAppLauncherPath,v as daemonLockfilePath,w as devBridgeLockfilePath,n as electronDir,o as electronUserDataDir,p as electronVersionDir,D as ensureSootsimHome,L as isDaemonLockfileFresh,M as isDevBridgeLockfileFresh,i as isDevWorkstation,h as isSootsimDevCheckout,G as listInstalledRuntimes,q as profilesDir,E as readActiveRuntime,J as readDaemonLockfile,K as readDevBridgeLockfile,B as readOrCreateAnonymousId,y as readSharedConfig,A as readTelemetryEnabled,Q as removeDaemonLockfile,R as removeDevBridgeLockfile,l as runtimeDir,k as runtimesDir,j as shouldSkipPersistentDaemon,g as sootsimHomeDir,F as writeActiveRuntime,N as writeDaemonLockfile,O as writeDevBridgeLockfile,z as writeSharedConfig,C as writeTelemetryEnabled};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{j as a,k as b}from"./chunk-WHNPGZX4.js";import"./chunk-XAFLM5JS.js";import"./chunk-ENY46QHT.js";import"./chunk-63EDUOKZ.js";import"./chunk-O56GZV3H.js";import"./chunk-D6CTB275.js";import"./chunk-S2AQ4WGX.js";import"./chunk-HVSDT56M.js";import"./chunk-F5TOTCUW.js";import"./chunk-ZSWQLDDQ.js";import"./chunk-HIWE4YYN.js";import"./chunk-7GG4TZ4L.js";import"./chunk-CPVTNQAS.js";import"./chunk-YOPEO6SA.js";import"./chunk-GBUUYMPP.js";import"./chunk-NYNAI5PO.js";import"./chunk-SVM25PDV.js";import"./chunk-T6WXCSWP.js";import"./chunk-AHJ2WJST.js";import"./chunk-CHVGDYQJ.js";import"./chunk-ORIISY22.js";import"./chunk-423YVBZK.js";import"./chunk-NIDV74L2.js";export{a as printShellPerfReport,b as runInspect};
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.201 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{e as w,f as v}from"./chunk-WJ7YQAKC.js";import{c as y}from"./chunk-WHNPGZX4.js";import{a as h}from"./chunk-XAFLM5JS.js";import"./chunk-SZVYOFMB.js";import"./chunk-PMPQ57UY.js";import"./chunk-FPRZHFV2.js";import"./chunk-ENY46QHT.js";import"./chunk-63EDUOKZ.js";import"./chunk-O56GZV3H.js";import"./chunk-D6CTB275.js";import"./chunk-S2AQ4WGX.js";import"./chunk-HVSDT56M.js";import"./chunk-F5TOTCUW.js";import"./chunk-ZSWQLDDQ.js";import"./chunk-HIWE4YYN.js";import"./chunk-7GG4TZ4L.js";import"./chunk-CPVTNQAS.js";import"./chunk-YOPEO6SA.js";import"./chunk-GBUUYMPP.js";import"./chunk-NYNAI5PO.js";import"./chunk-QTI33DV5.js";import"./chunk-FITJFYBO.js";import"./chunk-SVM25PDV.js";import"./chunk-T6WXCSWP.js";import"./chunk-AHJ2WJST.js";import"./chunk-CHVGDYQJ.js";import"./chunk-ORIISY22.js";import"./chunk-423YVBZK.js";import"./chunk-NIDV74L2.js";import*as n from"fs";import*as a from"path";var S=[".maestro","maestro"];function k(){console.log(`
|
|
3
|
-
sootsim maestro \u2014 run maestro YAML flows against sootsim (drop-in)
|
|
4
|
-
|
|
5
|
-
usage:
|
|
6
|
-
sootsim maestro # discover .maestro/ or maestro/ in cwd, run all
|
|
7
|
-
sootsim maestro test <flow-or-dir> # mirrors "maestro test"
|
|
8
|
-
sootsim maestro test .maestro/ # every *.yaml / *.yml in a directory
|
|
9
|
-
sootsim maestro init # scaffold .maestro/login.yaml
|
|
10
|
-
sootsim maestro --list-compat # print supported/unsupported verbs
|
|
11
|
-
|
|
12
|
-
options:
|
|
13
|
-
--env KEY=VALUE set env vars for \${KEY} interpolation (repeatable)
|
|
14
|
-
--continuous re-run the flow on file changes (alias for --watch)
|
|
15
|
-
--format <fmt> accepted for maestro cli compat (not used)
|
|
16
|
-
--new force a fresh browser window for this run
|
|
17
|
-
--headed force a fresh, VISIBLE window (implies --new,
|
|
18
|
-
overrides --headless) \u2014 watch the flow run live
|
|
19
|
-
--record record a webm while the flow runs
|
|
20
|
-
--preview record events+video, upload, and print /preview/<id>
|
|
21
|
-
--preview-open open the uploaded preview link after a successful run
|
|
22
|
-
--preview-origin <url> upload target for --preview
|
|
23
|
-
--preview-public-origin <url>
|
|
24
|
-
public link origin for --preview
|
|
25
|
-
--slow <ms> delay between steps for natural pacing
|
|
26
|
-
|
|
27
|
-
examples:
|
|
28
|
-
sootsim maestro test .maestro/login.yaml
|
|
29
|
-
sootsim maestro test .maestro/
|
|
30
|
-
sootsim maestro --env USERNAME=alice test .maestro/login.yaml
|
|
31
|
-
sootsim maestro init
|
|
32
|
-
`)}function A(){console.log(`
|
|
33
|
-
sootsim maestro \u2014 compatibility matrix
|
|
34
|
-
|
|
35
|
-
supported verbs:
|
|
36
|
-
launchApp, stopApp, clearState, clearKeychain (warn-only),
|
|
37
|
-
tapOn, tapAtCoords, longPressOn,
|
|
38
|
-
inputText, pressKey, dispatchKey, hideKeyboard, eraseText,
|
|
39
|
-
assertVisible, assertNotVisible, assertTreeContains,
|
|
40
|
-
waitFor, extendedWaitUntil, waitForAnimationToEnd,
|
|
41
|
-
scroll, scrollUntilVisible, scrollTo, swipe, pinch,
|
|
42
|
-
takeScreenshot, dumpTree, back,
|
|
43
|
-
repeat (times), runFlow (file / inline commands / env / when),
|
|
44
|
-
runScript (maestro JS: http, json(), output, env vars, console.log),
|
|
45
|
-
evalScript, copyTextFrom (\${maestro.copiedText}), openLink,
|
|
46
|
-
when: (visible / notVisible / platform / true),
|
|
47
|
-
optional:, label:, onFlowStart, onFlowComplete,
|
|
48
|
-
\${...} JS-expression interpolation at step execution time
|
|
49
|
-
(env vars as globals, output.*, maestro.*, \${EXPR || 'default'}).
|
|
50
|
-
|
|
51
|
-
workspace mode (directory runs):
|
|
52
|
-
config.yaml / config.yml discovery, flows: inclusion globs,
|
|
53
|
-
includeTags / excludeTags against flow frontmatter tags.
|
|
54
|
-
|
|
55
|
-
partial:
|
|
56
|
-
clearKeychain \u2014 warn-only, sootsim has no keychain surface.
|
|
57
|
-
when.platform \u2014 sootsim reports iOS.
|
|
58
|
-
openLink \u2014 dispatched into the app via the Linking emulation
|
|
59
|
-
(react-navigation deep links work); no OS-level Safari fallback.
|
|
60
|
-
launchApp.arguments \u2014 ignored; sootsim has no native process.
|
|
61
|
-
launchApp.resetRuntime \u2014 sootsim-only extension for recorded benchmarks:
|
|
62
|
-
keep tenant storage/auth, but soft-reload the guest app inside launchApp.
|
|
63
|
-
takeScreenshot \u2014 maestro string form works unchanged; sootsim also accepts
|
|
64
|
-
{ path, withFrame } for framed export.
|
|
65
|
-
env \u2014 sootsim seeds the JS env from the full shell environment, not just
|
|
66
|
-
MAESTRO_* (so \${SOOTSIM_*} credentials resolve); a bare \${NAME} that is
|
|
67
|
-
undefined fails the step loudly instead of typing "undefined".
|
|
68
|
-
|
|
69
|
-
not yet implemented (will throw "unsupported maestro verb"):
|
|
70
|
-
travel, setLocation, setAirplaneMode, killApp, faker,
|
|
71
|
-
addMedia, startRecording, stopRecording (use --record flag),
|
|
72
|
-
repeat.while (only repeat.times is supported).
|
|
73
|
-
`)}function b(o){return o.startsWith("-")}function $(o){let i=[];for(let e=0;e<o.length;e++){if(o[e]==="--env"||o[e]==="-e"){let s=o[e+1];if(!s||!s.includes("="))throw new Error(`--env expects KEY=VALUE (got ${JSON.stringify(s??"")})`);let p=s.indexOf("="),l=s.slice(0,p),f=s.slice(p+1);process.env[l]=f,e+=1;continue}i.push(o[e])}return i}function T(o){let i=[];for(let e=0;e<o.length;e++){let s=o[e];if(s==="--format"||s==="--output"||s==="--device"||s==="-d"){e+=1;continue}if(s==="--continuous"){console.warn(" warn: --continuous is not yet implemented in sootsim \u2014 running once");continue}i.push(s)}return i}function x(o){for(let i of S){let e=a.join(o,i);if(n.existsSync(e)&&n.statSync(e).isDirectory())return e}return null}function F(o){let i=n.statSync(o);if(i.isFile())return[o];if(!i.isDirectory())throw new Error(`not a file or directory: ${o}`);let e={},s=["config.yaml","config.yml"].map(t=>a.join(o,t)).find(t=>n.existsSync(t));if(s){let t=h.parse(n.readFileSync(s,"utf8"));t&&typeof t=="object"&&(e=t)}let p=Array.isArray(e.flows)&&e.flows.length>0?e.flows:["*"],l=t=>{let r=a.basename(t);return!(r.startsWith(".")||!r.endsWith(".yaml")&&!r.endsWith(".yml")||r==="config.yaml"||r==="config.yml")},f=new Set;for(let t of p)for(let r of n.globSync(t,{cwd:o})){if(!l(r))continue;let c=a.join(o,r);n.existsSync(c)&&n.statSync(c).isFile()&&f.add(c)}let m=[...f].sort(),u=Array.isArray(e.includeTags)?e.includeTags:[],d=Array.isArray(e.excludeTags)?e.excludeTags:[];return(u.length>0||d.length>0)&&(m=m.filter(t=>{let{frontmatter:r}=y(n.readFileSync(t,"utf8")),c=Array.isArray(r.tags)?r.tags:[];return!(u.length>0&&!c.some(g=>u.includes(g))||c.some(g=>d.includes(g)))})),m}async function E(o){let i=a.join(o,".maestro");n.mkdirSync(i,{recursive:!0});let e=a.join(i,"login.yaml");return n.existsSync(e)?(console.error(` error: ${e} already exists`),1):(n.writeFileSync(e,`# sootsim maestro starter flow \u2014 drop-in compatible with the maestro cli.
|
|
74
|
-
# run: sootsim maestro test .maestro/login.yaml
|
|
75
|
-
appId: com.example.app
|
|
76
|
-
---
|
|
77
|
-
- launchApp
|
|
78
|
-
- tapOn:
|
|
79
|
-
id: "email"
|
|
80
|
-
- inputText: "user@example.com"
|
|
81
|
-
- tapOn:
|
|
82
|
-
id: "password"
|
|
83
|
-
- inputText: "secret123"
|
|
84
|
-
- tapOn: "Sign in"
|
|
85
|
-
- assertVisible: "Welcome"
|
|
86
|
-
`,"utf8"),console.log(` + wrote ${e}`),console.log(" next: sootsim maestro test .maestro/login.yaml"),0)}async function R(o,i={}){if(o.includes("--help")||o.includes("-h"))return k(),0;if(o.includes("--list-compat"))return A(),0;let e;try{e=$(o)}catch(t){return console.error(` error: ${t.message}`),1}e=T(e),e=v(e);let s=process.cwd(),p=e[0];if(p==="init")return E(s);let l=null,f;if(p==="test"){let t=e.slice(1).find(r=>!b(r));t?(l=a.resolve(s,t),f=e.slice(1).filter(r=>r!==t)):(l=x(s),f=e.slice(1))}else p&&!b(p)?(l=a.resolve(s,p),f=e.slice(1)):(l=x(s),f=e);if(!l)return console.error(`
|
|
87
|
-
error: no maestro flows found.
|
|
88
|
-
expected one of: ${S.map(t=>`./${t}/`).join(", ")}
|
|
89
|
-
or pass a flow explicitly: sootsim maestro test path/to/flow.yaml
|
|
90
|
-
or scaffold a starter flow: sootsim maestro init
|
|
91
|
-
`),1;if(!n.existsSync(l))return console.error(` error: ${l} not found`),1;let m;try{m=F(l)}catch(t){return console.error(` error: ${t.message}`),1}if(m.length===0)return console.error(` error: no *.yaml or *.yml files found in ${l}`),1;console.log(`
|
|
92
|
-
sootsim maestro \u2014 ${m.length} flow${m.length===1?"":"s"}
|
|
93
|
-
root: ${a.relative(s,l)||"."}
|
|
94
|
-
`);let u=0,d=[];for(let t of m){console.log(`
|
|
95
|
-
\u25B6 ${a.relative(s,t)}`);let r=[t,...f];i.port&&!f.includes("--url")&&r.push("--url",String(i.port));let c=0;try{c=await w(r)}catch(g){console.error(` x ${g.message}`),c=1}d.push({file:t,exit:c}),c!==0&&u===0&&(u=c)}if(m.length>1){console.log(`
|
|
96
|
-
maestro summary:`);for(let r of d){let c=r.exit===0?"pass":"fail";console.log(` ${c} ${a.relative(s,r.file)}`)}let t=d.filter(r=>r.exit===0).length;console.log(` ${t}/${d.length} passed`)}return u}export{F as collectFlowFiles,R as runMaestro};
|