sootsim 0.1.169 → 0.1.170

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.
Files changed (184) hide show
  1. package/README.md +66 -63
  2. package/detox/index.ts +3 -3
  3. package/dist-cli/bin.js +3 -3
  4. package/dist-cli/chunks/agent-3C2BKQKX.js +46 -0
  5. package/dist-cli/chunks/{agent-wrapper-Y5FQA3RL.js → agent-wrapper-OIKCYOXZ.js} +2 -2
  6. package/dist-cli/chunks/{app-fonts-FYVA4XV6.js → app-fonts-25O32NAP.js} +2 -2
  7. package/dist-cli/chunks/{assert-U64QBDM2.js → assert-ONEP4NQU.js} +2 -2
  8. package/dist-cli/chunks/beta-4E5USTEI.js +2 -0
  9. package/dist-cli/chunks/{camera-3GXBVIAM.js → camera-RORW4MAA.js} +2 -2
  10. package/dist-cli/chunks/{chunk-5C6BR24W.js → chunk-25RFZ6WP.js} +6 -6
  11. package/dist-cli/chunks/chunk-35GMFXVJ.js +2 -0
  12. package/dist-cli/chunks/{chunk-GLRFKYDV.js → chunk-3TM7DHWE.js} +2 -2
  13. package/dist-cli/chunks/{chunk-4F3BBP7B.js → chunk-4XVY5TUF.js} +2 -2
  14. package/dist-cli/chunks/{chunk-LZEL2RU3.js → chunk-5ZHUPPS5.js} +1 -1
  15. package/dist-cli/chunks/{chunk-GC4THE6K.js → chunk-6FAEORJ2.js} +2 -2
  16. package/dist-cli/chunks/{chunk-G2ZFDIPK.js → chunk-6TSXXN2W.js} +4 -4
  17. package/dist-cli/chunks/chunk-AJF3QF4K.js +1 -0
  18. package/dist-cli/chunks/{chunk-FIDPRJYH.js → chunk-AUCNBB6Q.js} +2 -2
  19. package/dist-cli/chunks/{chunk-SIB6I72P.js → chunk-BP3EGFER.js} +2 -2
  20. package/dist-cli/chunks/{chunk-OGLWXPVG.js → chunk-DAXME77O.js} +3 -3
  21. package/dist-cli/chunks/chunk-DGJTEFE7.js +13 -0
  22. package/dist-cli/chunks/chunk-DIRDEKRW.js +23 -0
  23. package/dist-cli/chunks/{chunk-3L4NRJ6C.js → chunk-DKTC53HV.js} +1 -1
  24. package/dist-cli/chunks/{chunk-KUUFY6FC.js → chunk-DWXC25XG.js} +1 -1
  25. package/dist-cli/chunks/{chunk-CR6DOLUD.js → chunk-E3FKMY5E.js} +1 -1
  26. package/dist-cli/chunks/chunk-ER4GBJRY.js +2 -0
  27. package/dist-cli/chunks/chunk-EX6V4ZV6.js +6 -0
  28. package/dist-cli/chunks/{chunk-VMYMPL6H.js → chunk-GQH3PD3W.js} +5 -5
  29. package/dist-cli/chunks/{chunk-MVOHANJG.js → chunk-GST4YSBO.js} +4 -4
  30. package/dist-cli/chunks/{chunk-BIWJE7CB.js → chunk-I4RV5I6V.js} +1 -1
  31. package/dist-cli/chunks/{chunk-Y6IDAARY.js → chunk-I5WXVKR6.js} +1 -1
  32. package/dist-cli/chunks/{chunk-HC72ILJK.js → chunk-IO7KAVFK.js} +2 -2
  33. package/dist-cli/chunks/{chunk-XEH4QGWB.js → chunk-LMX7UB5T.js} +2 -2
  34. package/dist-cli/chunks/{chunk-YADWU7OZ.js → chunk-N4YCSOCR.js} +2 -2
  35. package/dist-cli/chunks/{chunk-IODOTOOP.js → chunk-NI3L4A65.js} +2 -2
  36. package/dist-cli/chunks/{chunk-IACEJQIO.js → chunk-ODGEH6OW.js} +2 -2
  37. package/dist-cli/chunks/{chunk-DNA6VS6M.js → chunk-Q4NDYO2U.js} +1 -1
  38. package/dist-cli/chunks/{chunk-PV6MBKKE.js → chunk-QPRF3UYX.js} +2 -2
  39. package/dist-cli/chunks/{chunk-M2TMFGZH.js → chunk-QXJMXSQP.js} +1 -1
  40. package/dist-cli/chunks/chunk-RLKSLUNR.js +2 -0
  41. package/dist-cli/chunks/{chunk-WZG3YVJR.js → chunk-S244IT4W.js} +1 -1
  42. package/dist-cli/chunks/chunk-SDHOM6ZG.js +62 -0
  43. package/dist-cli/chunks/{chunk-B7BMSU7H.js → chunk-TXKOJ3P5.js} +1 -1
  44. package/dist-cli/chunks/{chunk-IBEMMV5E.js → chunk-UDMJSFSH.js} +8 -8
  45. package/dist-cli/chunks/{chunk-DQZEN256.js → chunk-ULV4PWN4.js} +1 -1
  46. package/dist-cli/chunks/{chunk-2TYEWVS4.js → chunk-V4HTQPMA.js} +1 -1
  47. package/dist-cli/chunks/chunk-VATHADSI.js +2 -0
  48. package/dist-cli/chunks/{chunk-5Q7IHJ7K.js → chunk-VUOCUCJ2.js} +2 -2
  49. package/dist-cli/chunks/{chunk-JDGLBW64.js → chunk-WN7R4GL4.js} +1 -1
  50. package/dist-cli/chunks/chunk-XOTJD6PU.js +118 -0
  51. package/dist-cli/chunks/chunk-XUNPVF5K.js +1 -0
  52. package/dist-cli/chunks/{chunk-KZIXAYN5.js → chunk-YMFSRCHL.js} +4 -4
  53. package/dist-cli/chunks/{chunk-X2HNVXNM.js → chunk-YV74D4MG.js} +2 -2
  54. package/dist-cli/chunks/{chunk-SNX3M4XD.js → chunk-YVTWSC5Y.js} +3 -3
  55. package/dist-cli/chunks/cli-version-EKKWCHRX.js +2 -0
  56. package/dist-cli/chunks/{compat-ORRWNXVT.js → compat-ZVZM5TML.js} +3 -3
  57. package/dist-cli/chunks/control-TWINYHMY.js +2 -0
  58. package/dist-cli/chunks/{cpu-profile-6PRSBZ2F.js → cpu-profile-RWGQTWLP.js} +2 -2
  59. package/dist-cli/chunks/daemon-AOSMNBD4.js +2 -0
  60. package/dist-cli/chunks/{debug-Q3J2X4WW.js → debug-R7PL3MHF.js} +5 -5
  61. package/dist-cli/chunks/{detox-DN57GMMJ.js → detox-UIJMKIGQ.js} +2 -2
  62. package/dist-cli/chunks/{device-IL5TPYGU.js → device-7SRVX2PU.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-BW6QWP4F.js → diagnose-OXAKLLZT.js} +2 -2
  64. package/dist-cli/chunks/drivers-BPPG22VP.js +2 -0
  65. package/dist-cli/chunks/{electron-DGMSXQWG.js → electron-CTCH4VOL.js} +3 -3
  66. package/dist-cli/chunks/flow-KA2WCWDL.js +2 -0
  67. package/dist-cli/chunks/help-XBQIK2XY.js +2 -0
  68. package/dist-cli/chunks/{hints-TK3EHB4U.js → hints-P3ZIAP3Z.js} +2 -2
  69. package/dist-cli/chunks/home-paths-ZSCTLOYE.js +2 -0
  70. package/dist-cli/chunks/{inspect-NFWFGU4B.js → inspect-2ABU5WET.js} +24 -24
  71. package/dist-cli/chunks/{install-desktop-GB3JKSNX.js → install-desktop-4NOQ3YY4.js} +3 -3
  72. package/dist-cli/chunks/{keys-RAAHRIFH.js → keys-RPVEUNUC.js} +2 -2
  73. package/dist-cli/chunks/{login-KQ37GHDE.js → login-6D6DXBO3.js} +4 -4
  74. package/dist-cli/chunks/{logout-63SH2Y25.js → logout-T5OSWDTJ.js} +2 -2
  75. package/dist-cli/chunks/{maestro-24BT3MML.js → maestro-MKCHEOKA.js} +2 -2
  76. package/dist-cli/chunks/optional-demo-registry-EYD2K3QH.js +2 -0
  77. package/dist-cli/chunks/{preview-KQAOVN6R.js → preview-S52ZNGN2.js} +2 -2
  78. package/dist-cli/chunks/{profile-AN2B4NJT.js → profile-IG5F3LTL.js} +2 -2
  79. package/dist-cli/chunks/{react-ATGV35B4.js → react-275HCOFC.js} +2 -2
  80. package/dist-cli/chunks/{record-36662T3R.js → record-YT63SUHO.js} +4 -4
  81. package/dist-cli/chunks/runtime-WYZMDZAK.js +2 -0
  82. package/dist-cli/chunks/{screenshot-K4N5MPAT.js → screenshot-5AZ4MY3A.js} +4 -4
  83. package/dist-cli/chunks/{screenshot-mode-4QOJDLSH.js → screenshot-mode-4S4EX2XJ.js} +2 -2
  84. package/dist-cli/chunks/{screenshots-TB35YMSR.js → screenshots-UUJNZSG7.js} +2 -2
  85. package/dist-cli/chunks/{screenshots-capture-W6X33BZ4.js → screenshots-capture-I66JD3AD.js} +2 -2
  86. package/dist-cli/chunks/{server-L2PKDJZ2.js → serve-O46Q4WAD.js} +18 -17
  87. package/dist-cli/chunks/setup-C44N54O7.js +77 -0
  88. package/dist-cli/chunks/skills-PPVS4XOQ.js +40 -0
  89. package/dist-cli/chunks/{slides-TORKO36P.js → slides-ZWSZJZUY.js} +2 -2
  90. package/dist-cli/chunks/store-3SLGP5O3.js +2 -0
  91. package/dist-cli/chunks/telemetry-2XLLWARK.js +2 -0
  92. package/dist-cli/chunks/{test-5VCENVW3.js → test-362G4M5A.js} +3 -3
  93. package/dist-cli/chunks/{three-mode-ZVS2DSX6.js → three-mode-JNVTM2UT.js} +2 -2
  94. package/dist-cli/chunks/{timeline-TNC5OWLF.js → timeline-VKC6ULRK.js} +2 -2
  95. package/dist-cli/chunks/{upgrade-E7IO6A7Z.js → upgrade-SJPX4SPA.js} +2 -2
  96. package/dist-cli/chunks/upload-MXOBV6RT.js +2 -0
  97. package/dist-cli/chunks/version-4Z7AFNH3.js +4 -0
  98. package/dist-cli/chunks/web-WB2I2AWL.js +2 -0
  99. package/dist-cli/chunks/{what-happened-RMHZ7DGX.js → what-happened-KQCWMQJT.js} +3 -3
  100. package/dist-cli/chunks/{whoami-D53ANHR5.js → whoami-K76PXAD4.js} +2 -2
  101. package/dist-lib/agent-daemon-client.cjs +4 -5
  102. package/dist-lib/agent-events.cjs +1 -1
  103. package/dist-lib/agent-sessions.cjs +1 -1
  104. package/dist-lib/attached-projects.cjs +1 -1
  105. package/dist-lib/auth/shared-session.cjs +1 -1
  106. package/dist-lib/backend-origin.cjs +1 -1
  107. package/dist-lib/beta.cjs +3 -6
  108. package/dist-lib/beta.mjs +3 -5
  109. package/dist-lib/bridge-constants.cjs +1 -1
  110. package/dist-lib/cli-constants.cjs +1 -1
  111. package/dist-lib/config.cjs +1 -1
  112. package/dist-lib/detox/index.cjs +4 -4
  113. package/dist-lib/dev-bundle-resolution.cjs +2 -9
  114. package/dist-lib/home-paths.cjs +59 -1
  115. package/dist-lib/host/bridge-host.cjs +67 -5
  116. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  117. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  118. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  119. package/dist-lib/host/websocket-proxy.cjs +1 -1
  120. package/dist-lib/index.cjs +2 -2
  121. package/dist-lib/menu.cjs +1 -1
  122. package/dist-lib/menu.mjs +1 -1
  123. package/dist-lib/metro.cjs +2 -2
  124. package/dist-lib/profiles.cjs +1 -1
  125. package/dist-lib/render-mode.cjs +1 -1
  126. package/dist-lib/scripts/dev-server-scanner.cjs +5 -4
  127. package/dist-lib/sdk.cjs +1 -1
  128. package/dist-lib/sdk.mjs +1 -1
  129. package/dist-lib/skills.cjs +124 -8
  130. package/dist-lib/vite.cjs +2 -2
  131. package/package.json +2 -1
  132. package/scripts/dev-server-scanner.ts +3 -4
  133. package/scripts/postinstall.cjs +2 -2
  134. package/scripts/preuninstall.cjs +5 -7
  135. package/skills/soot.md +178 -0
  136. package/skills/sootsim-debug.md +384 -0
  137. package/skills/sootsim-setup.md +96 -0
  138. package/skills/sootsim-test.md +292 -0
  139. package/skills/sootsim-visual.md +122 -0
  140. package/src/agent-daemon-client.ts +4 -14
  141. package/src/beta.ts +3 -5
  142. package/src/dev-bundle-resolution.ts +1 -14
  143. package/src/home-paths.ts +77 -2
  144. package/src/host/agent-host.ts +1 -1
  145. package/src/host/bridge-host.ts +65 -6
  146. package/src/metro-plugin.ts +1 -1
  147. package/src/runtime-assets.ts +1 -1
  148. package/src/vite-plugin.ts +1 -0
  149. package/dist-cli/chunks/agent-V3GVCRPL.js +0 -46
  150. package/dist-cli/chunks/auto-bootstrap-YXDP3WKH.js +0 -2
  151. package/dist-cli/chunks/beta-FGSMZWLP.js +0 -2
  152. package/dist-cli/chunks/chunk-2IFWVIKM.js +0 -2
  153. package/dist-cli/chunks/chunk-3T36XLP3.js +0 -119
  154. package/dist-cli/chunks/chunk-5KY7TQ42.js +0 -5
  155. package/dist-cli/chunks/chunk-BQOMDL4K.js +0 -1
  156. package/dist-cli/chunks/chunk-FHMPJZKM.js +0 -7
  157. package/dist-cli/chunks/chunk-GQAWFQNN.js +0 -23
  158. package/dist-cli/chunks/chunk-HJTKKF7M.js +0 -13
  159. package/dist-cli/chunks/chunk-K4JAWKOY.js +0 -1
  160. package/dist-cli/chunks/chunk-NXGQS7IR.js +0 -2
  161. package/dist-cli/chunks/chunk-R2INPDGW.js +0 -1
  162. package/dist-cli/chunks/chunk-SLL5SZN4.js +0 -2
  163. package/dist-cli/chunks/chunk-XM66K64H.js +0 -79
  164. package/dist-cli/chunks/cli-version-3L6FFDIX.js +0 -2
  165. package/dist-cli/chunks/config-5IB6ONJ2.js +0 -45
  166. package/dist-cli/chunks/control-BM4V27OB.js +0 -2
  167. package/dist-cli/chunks/daemon-62MZT4G4.js +0 -67
  168. package/dist-cli/chunks/drivers-V6QDFZQD.js +0 -2
  169. package/dist-cli/chunks/flow-VBM6TU32.js +0 -2
  170. package/dist-cli/chunks/help-XWKNL743.js +0 -2
  171. package/dist-cli/chunks/home-paths-G23H3RO6.js +0 -2
  172. package/dist-cli/chunks/install-5YL7FITA.js +0 -2
  173. package/dist-cli/chunks/launch-DYFE4CAD.js +0 -16
  174. package/dist-cli/chunks/optional-demo-registry-JJXPB3UD.js +0 -2
  175. package/dist-cli/chunks/runtime-NRMC3U3D.js +0 -2
  176. package/dist-cli/chunks/runtime-delivery-RO6CTYPQ.js +0 -2
  177. package/dist-cli/chunks/setup-repo-AK6QOUE7.js +0 -2
  178. package/dist-cli/chunks/skills-KDSOWUOE.js +0 -10
  179. package/dist-cli/chunks/start-GH2AOUX6.js +0 -23
  180. package/dist-cli/chunks/store-JHW65VGQ.js +0 -2
  181. package/dist-cli/chunks/telemetry-SZLRXJXW.js +0 -2
  182. package/dist-cli/chunks/upload-KKEWETA5.js +0 -2
  183. package/dist-cli/chunks/version-37OD33SE.js +0 -4
  184. package/dist-cli/chunks/web-4JVMIBTB.js +0 -2
@@ -21,10 +21,13 @@ import {
21
21
  readActiveRuntime,
22
22
  readSharedConfig,
23
23
  removeDaemonLockfile,
24
+ removeDevBridgeLockfile,
24
25
  writeActiveRuntime,
25
26
  writeDaemonLockfile,
27
+ writeDevBridgeLockfile,
26
28
  writeSharedConfig,
27
29
  type DaemonLockfile,
30
+ type DevBridgeLockfile,
28
31
  type SharedConfig,
29
32
  } from '../home-paths.ts'
30
33
  import { updateRuntimeToLatest } from '../runtime-delivery.ts'
@@ -210,17 +213,22 @@ export interface BridgeHostOptions {
210
213
  portFallbackCount?: number
211
214
  /** when true, write ~/.sootsim/daemon.json on successful bind + update
212
215
  * it on a heartbeat interval + remove it on close. only the standalone
213
- * `sootsim server` process should set this — tests, vite plugin, and
216
+ * `sootsim serve` process should set this — tests, vite plugin, and
214
217
  * anything embedded should leave it off to avoid clobbering a real
215
218
  * daemon's lockfile. defaults to false. */
216
219
  writeLockfile?: boolean
220
+ /** when true, write ~/.sootsim/dev-bridge.json on successful bind + update
221
+ * it on a heartbeat interval + remove it on close. the Vite dev shell sets
222
+ * this so local CLI commands prefer the live source bridge over a standalone
223
+ * daemon or stale default-port listener. */
224
+ writeDevLockfile?: boolean
217
225
  /** the sootbean origin this daemon's runtimes should talk to for auth,
218
226
  * billing, and preview uploads — inlined into served runtime html as
219
227
  * `window.__sootsimSootbeanOrigin`. without it the engine's origin.ts
220
228
  * resolves the daemon's own loopback host (localhost:<runtimePort>) to
221
229
  * the dev `localhost:3000` stack, so a *prod* CLI user's preview
222
230
  * recording would upload to a localhost stack that doesn't exist. the
223
- * `sootsim server` command resolves this with the same probe the
231
+ * `sootsim serve` command resolves this with the same probe the
224
232
  * upload itself uses (`resolveDefaultUploadOrigin`). */
225
233
  sootbeanOrigin?: string
226
234
  /** override the abandoned-sim GC TTL (ms). defaults to
@@ -386,10 +394,12 @@ export class SootSimBridgeHost {
386
394
  private maxConcurrentAutomationSims: number
387
395
  private automationSimActiveGraceMs: number
388
396
  private shouldWriteLockfile: boolean
397
+ private shouldWriteDevLockfile: boolean
389
398
  private sootbeanOrigin: string | null = null
390
399
  private effectivePort = 0
391
400
  private startedAt = 0
392
401
  private heartbeatTimer: NodeJS.Timeout | null = null
402
+ private devHeartbeatTimer: NodeJS.Timeout | null = null
393
403
  // ws-level heartbeat: sims that hang up uncleanly (page navigated, network
394
404
  // dropped, sim crashed) leave their server-side WebSocket sitting "open"
395
405
  // forever. ping every WS_HEARTBEAT_INTERVAL_MS; if the previous round's
@@ -416,10 +426,11 @@ export class SootSimBridgeHost {
416
426
  constructor(opts: BridgeHostOptions = {}) {
417
427
  this.preferredPort = opts.port || DEFAULT_SOOTSIM_BRIDGE_PORT
418
428
  this.port = this.preferredPort
419
- // default off — callers that want the lockfile (sootsim server) opt in.
429
+ // default off — callers that want the lockfile (sootsim serve) opt in.
420
430
  this.shouldWriteLockfile = opts.writeLockfile === true
431
+ this.shouldWriteDevLockfile = opts.writeDevLockfile === true
421
432
  // fallback freely on port collision regardless of who owns the lockfile.
422
- // server.ts already refuses to start if a fresh daemon.json exists, so
433
+ // serve.ts already refuses to start if a fresh daemon.json exists, so
423
434
  // two daemons can't race the lockfile. without this, an unrelated process
424
435
  // on 7668 (another worktree's vite dev fallback bridge, etc.) was enough
425
436
  // to keep the daemon from binding at all — and launchd's KeepAlive then
@@ -1052,7 +1063,7 @@ export class SootSimBridgeHost {
1052
1063
  })
1053
1064
  }
1054
1065
 
1055
- /** after a successful bind: start the cli idle sweep, write the daemon
1066
+ /** after a successful bind: start the cli idle sweep, write the daemon/dev
1056
1067
  * lockfile (if this host owns it), seed the agent host, kick off the
1057
1068
  * heartbeat loop. idempotent across rebinds because close() tears down
1058
1069
  * every timer and the lockfile. */
@@ -1077,7 +1088,7 @@ export class SootSimBridgeHost {
1077
1088
  // atomic claim: bail if another fresh daemon's lockfile already
1078
1089
  // exists (stale ones are overwritten). last line of defense
1079
1090
  // against two daemons clobbering the same file between the
1080
- // freshness check in server.ts and here.
1091
+ // freshness check in serve.ts and here.
1081
1092
  const claimed = claimDaemonLockfile(this.buildLockfileSnapshot())
1082
1093
  if (!claimed) {
1083
1094
  throw new Error(
@@ -1099,6 +1110,22 @@ export class SootSimBridgeHost {
1099
1110
  this.startRuntimeUpdater()
1100
1111
  }
1101
1112
 
1113
+ if (this.shouldWriteDevLockfile) {
1114
+ try {
1115
+ this.writeDevLockfileSnapshot()
1116
+ } catch (err) {
1117
+ process.stderr.write(
1118
+ `ws bridge failed to write dev bridge lockfile: ${String(err)}\n`,
1119
+ )
1120
+ }
1121
+ this.devHeartbeatTimer = setInterval(() => {
1122
+ try {
1123
+ this.writeDevLockfileSnapshot()
1124
+ } catch {}
1125
+ }, DAEMON_HEARTBEAT_INTERVAL_MS) as unknown as NodeJS.Timeout
1126
+ this.devHeartbeatTimer.unref()
1127
+ }
1128
+
1102
1129
  // seed the attached-projects store from the demo registry on first
1103
1130
  // daemon boot. idempotent; no-ops once the store has anything in it.
1104
1131
  void this.agentHost.seedOnBoot()
@@ -1121,10 +1148,28 @@ export class SootSimBridgeHost {
1121
1148
  }
1122
1149
  }
1123
1150
 
1151
+ private buildDevLockfileSnapshot(): DevBridgeLockfile {
1152
+ return {
1153
+ schema: 1,
1154
+ pid: process.pid,
1155
+ platform: process.platform,
1156
+ bridgePort: this.effectivePort,
1157
+ runtimePort: this.effectivePort,
1158
+ cwd: process.cwd(),
1159
+ startedAt: this.startedAt,
1160
+ heartbeatAt: Date.now(),
1161
+ source: 'vite-dev',
1162
+ }
1163
+ }
1164
+
1124
1165
  private writeLockfileSnapshot() {
1125
1166
  writeDaemonLockfile(this.buildLockfileSnapshot())
1126
1167
  }
1127
1168
 
1169
+ private writeDevLockfileSnapshot() {
1170
+ writeDevBridgeLockfile(this.buildDevLockfileSnapshot())
1171
+ }
1172
+
1128
1173
  private refreshActiveRuntime() {
1129
1174
  this.activeRuntimeVersion = readActiveRuntime()
1130
1175
  this.activeRuntimeDirPath = getActiveRuntimeDir()
@@ -1416,6 +1461,11 @@ export class SootSimBridgeHost {
1416
1461
  bridgePort: this.effectivePort,
1417
1462
  runtimePort: this.effectivePort,
1418
1463
  activeRuntime: this.activeRuntimeVersion,
1464
+ bridgeSource: this.shouldWriteDevLockfile
1465
+ ? 'vite-dev'
1466
+ : this.shouldWriteLockfile
1467
+ ? 'daemon'
1468
+ : 'embedded',
1419
1469
  startedAt: this.startedAt,
1420
1470
  uptimeMs: this.startedAt > 0 ? Date.now() - this.startedAt : 0,
1421
1471
  }),
@@ -1885,6 +1935,10 @@ export class SootSimBridgeHost {
1885
1935
  clearInterval(this.heartbeatTimer)
1886
1936
  this.heartbeatTimer = null
1887
1937
  }
1938
+ if (this.devHeartbeatTimer) {
1939
+ clearInterval(this.devHeartbeatTimer)
1940
+ this.devHeartbeatTimer = null
1941
+ }
1888
1942
  if (this.wsHeartbeatTimer) {
1889
1943
  clearInterval(this.wsHeartbeatTimer)
1890
1944
  this.wsHeartbeatTimer = null
@@ -1898,6 +1952,11 @@ export class SootSimBridgeHost {
1898
1952
  removeDaemonLockfile()
1899
1953
  } catch {}
1900
1954
  }
1955
+ if (this.shouldWriteDevLockfile) {
1956
+ try {
1957
+ removeDevBridgeLockfile()
1958
+ } catch {}
1959
+ }
1901
1960
  this.effectivePort = 0
1902
1961
  this.startedAt = 0
1903
1962
  this.agentHost.close()
@@ -1,6 +1,6 @@
1
1
  // sootsim metro plugin — serves the sootsim shell at /__soot/ on metro's server.
2
2
  // shell assets come from the active engine runtime under ~/.sootsim/runtimes/,
3
- // which is fetched by `sootsim runtime install` / `sootsim setup-repo`. resolved
3
+ // which is fetched by `sootsim runtime install` / `sootsim setup`. resolved
4
4
  // per-request so swapping runtimes (`sootsim runtime use <v>`) takes effect
5
5
  // without restarting metro.
6
6
  //
@@ -7,7 +7,7 @@ import path from 'path'
7
7
  import { readActiveRuntime, runtimeDir } from './home-paths'
8
8
 
9
9
  export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
10
- '[sootsim] no engine runtime installed — run `sootsim setup-repo` in your project, or `sootsim runtime install`'
10
+ '[sootsim] no engine runtime installed — run `sootsim setup`, or `sootsim runtime install`'
11
11
 
12
12
  const MIME_TYPES: Record<string, string> = {
13
13
  '.js': 'application/javascript',
@@ -522,6 +522,7 @@ export function wsBridgePlugin(): Plugin {
522
522
  explicitPort: process.env.SOOTSIM_BRIDGE_PORT,
523
523
  portOffset: process.env.PORT_OFFSET,
524
524
  }),
525
+ writeDevLockfile: true,
525
526
  })
526
527
  bridgeHost.start?.({ silent: true })
527
528
  })
@@ -1,46 +0,0 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as E,b as A,c as S,d as l,e as j,g as T,h as I,j as D,m as k,n as x,o as O,p as g,q as R,r as C}from"./chunk-HC72ILJK.js";import"./chunk-LZEL2RU3.js";import{c as b}from"./chunk-IBEMMV5E.js";import"./chunk-IACEJQIO.js";import"./chunk-3T36XLP3.js";import"./chunk-GLRFKYDV.js";import"./chunk-2IFWVIKM.js";import"./chunk-DQZEN256.js";import"./chunk-KUUFY6FC.js";import"./chunk-3L4NRJ6C.js";import"./chunk-NXGQS7IR.js";import"./chunk-2TYEWVS4.js";import{h as y,i as $}from"./chunk-5KY7TQ42.js";import"./chunk-JDGLBW64.js";import w from"node:fs";import d from"node:path";import{spawn as F}from"node:child_process";import B from"node:net";import{WebSocket as M}from"ws";var c=class extends Error{code;constructor(e,t){super(e),this.name="AgentDaemonError",this.code=t}},f=class{ws;port;commandTimeoutMs;ready;closed=!1;nextId=1;pending=new Map;eventListeners=new Set;statusListeners=new Set;disconnectListeners=new Set;constructor(e={}){this.port=e.port??7668,this.commandTimeoutMs=e.commandTimeoutMs??15e3,this.ws=new M(`ws://localhost:${this.port}`),this.ready=new Promise((t,r)=>{let s=()=>{this.ws.off("error",o),t()},o=i=>{this.ws.off("open",s),r(new c(`could not connect to sootsim daemon on port ${this.port}: ${i.message}`,"NO_DAEMON"))};this.ws.once("open",s),this.ws.once("error",o)}),this.ws.on("message",t=>this.handleMessage(t)),this.ws.on("close",()=>this.handleClose()),this.ws.on("error",()=>{})}async waitReady(){return this.ready}async listProjects(){return this.send("agent:list-projects")}async upsertProject(e){return this.send("agent:upsert-project",{input:e})}async deleteProject(e){return this.send("agent:delete-project",{projectId:e})}async autoAttachForUrl(e){return this.send("agent:auto-attach-for-url",{input:e})}async listSessions(e){return this.send("agent:list-sessions",{projectId:e})}async startSession(e){return this.send("agent:start-session",{input:e})}async sendPrompt(e,t){return this.send("agent:send-prompt",{sessionId:e,prompt:t})}async endSession(e){return this.send("agent:end-session",{sessionId:e})}async getTranscript(e){return this.send("agent:get-transcript",{sessionId:e})}async getPaths(){return this.send("agent:get-paths")}async subscribeEvents(e){return this.send("agent:subscribe-events",{sessionId:e})}async unsubscribeEvents(e){return this.send("agent:unsubscribe-events",{sessionId:e})}onAgentEvent(e){return this.eventListeners.add(e),()=>this.eventListeners.delete(e)}onSessionStatusChange(e){return this.statusListeners.add(e),()=>this.statusListeners.delete(e)}onDisconnect(e){return this.disconnectListeners.add(e),()=>this.disconnectListeners.delete(e)}close(){if(!this.closed){this.closed=!0;try{this.ws.close()}catch{}}}async send(e,t={}){if(await this.ready,this.closed||this.ws.readyState!==M.OPEN)throw new c("daemon connection is closed","NO_DAEMON");let r=this.nextId++;return new Promise((s,o)=>{let i=setTimeout(()=>{this.pending.delete(r),o(new c(`${e} timed out after ${Math.round(this.commandTimeoutMs/1e3)}s`,"TIMEOUT"))},this.commandTimeoutMs);this.pending.set(r,{resolve:s,reject:o,timer:i});try{this.ws.send(JSON.stringify({id:r,type:e,...t}))}catch(a){clearTimeout(i),this.pending.delete(r),o(a instanceof Error?a:new Error(String(a)))}})}handleMessage(e){let t;try{t=JSON.parse(String(e))}catch{return}if(!t||typeof t!="object")return;if(t.type==="agent:event"){for(let s of this.eventListeners)try{s({sessionId:t.sessionId,event:t.event})}catch{}return}if(t.type==="agent:session-status"){for(let s of this.statusListeners)try{s(t.session)}catch{}return}if(typeof t.id!="number")return;let r=this.pending.get(t.id);r&&(this.pending.delete(t.id),clearTimeout(r.timer),t.error?r.reject(new c(t.error,t.code)):r.resolve(t.result))}handleClose(){if(!this.closed){this.closed=!0;for(let[,e]of this.pending)clearTimeout(e.timer),e.reject(new c("daemon disconnected","DISCONNECT"));this.pending.clear();for(let e of this.disconnectListeners)try{e()}catch{}}}};function L(n=7668,e=400){return new Promise(t=>{let r=new B.Socket,s=!1,o=i=>{s||(s=!0,r.destroy(),t(i))};r.setTimeout(e),r.once("connect",()=>o(!0)),r.once("timeout",()=>o(!1)),r.once("error",()=>o(!1)),r.connect(n,"127.0.0.1")})}async function W(n={}){let e=n.port??7668;if(await L(e))return{alreadyRunning:!0};if($()){let a=y()?"this is a dev workstation (IS_TAMAGUI_DEV=1)":"this is a dev checkout";throw new c(`no sootsim bridge on port ${e} and refusing to spawn a standalone daemon \u2014 ${a}. start the live \`bun dev\` stack so the bridge is up, or set SOOTSIM_FORCE_DAEMON_INSTALL=1 to spawn one here on purpose.`,"DEV_HOST_REFUSED")}let{cmd:t,prefixArgs:r}=C(),s=[...r,"server","--quiet"];e!==7668&&s.push("--port",String(e));let o=F(t,s,{detached:!0,stdio:"ignore",env:process.env,cwd:process.cwd()});o.unref();let i=Date.now()+(n.startupTimeoutMs??5e3);for(;Date.now()<i;){if(await L(e))return{alreadyRunning:!1,pid:o.pid};await new Promise(a=>setTimeout(a,100))}throw new c(`spawned sootsim daemon on port ${e} but it did not come up in time. run \`sootsim server\` manually to diagnose.`,"SPAWN_TIMEOUT")}async function h(n={}){await W({port:n.port,startupTimeoutMs:n.startupTimeoutMs});let e=new f(n);try{await e.waitReady()}catch(t){throw e.close(),t}return e}async function fe(n){let[e,...t]=n;try{switch(e){case void 0:case"--help":case"-h":case"help":return _(),0;case"attach":return await V(t);case"projects":return await J();case"project":return await G(t);case"sessions":return await H(t);case"start":return await q(t);case"prompt":return await K(t);case"watch":return await X(t);case"transcript":return await Q(t);case"end":return await Y(t);case"paths":return await Z();default:return process.stderr.write(`unknown agent subcommand: ${e}
3
- `),_(),2}}catch(r){if(r instanceof c)return process.stderr.write(`${r.message}
4
- `),1;throw r}}async function v(n){let e=await h({clientLabel:"sootsim-agent-cli"});try{return await n(e)}finally{e.close()}}function _(){b("agent")}function P(n,e){let t={},r=[];for(let s=0;s<n.length;s++){let o=n[s],i=e[o];i==="value"?(t[o]=n[s+1]??"",s++):i==="bool"?t[o]=!0:r.push(o)}return{flags:t,positional:r}}function U(n,e="codex"){return n==="codex"||n==="claude"?n:e}async function V(n){let{flags:e,positional:t}=P(n,{"--name":"value","--provider":"value"}),r=t[0];if(!r)return process.stderr.write(`usage: sootsim agent attach <dir> [--name X] [--provider codex|claude]
5
- `),2;let s=d.resolve(r);if(!w.existsSync(s))return process.stderr.write(`directory does not exist: ${s}
6
- `),1;let o=S({cwd:s,name:e["--name"]||d.basename(s),preferredProvider:U(e["--provider"]),sourceRoots:[s]});return process.stdout.write(`\u2713 attached "${o.name}" (id: ${o.id})
7
- cwd: ${o.cwd}
8
- framework: ${o.framework}
9
- preferred provider: ${o.preferredProvider}
10
- `),0}async function J(){let n=[...j()].sort((t,r)=>r.updatedAt-t.updatedAt);if(n.length===0)return process.stdout.write("no attached projects yet. try `sootsim agent attach <dir>`.\n"),0;let e=`${"ID".padEnd(18)} ${"NAME".padEnd(20)} ${"FRAMEWORK".padEnd(10)} ${"PROVIDER".padEnd(8)} ${"7D COST".padEnd(10)} CWD
11
- `;process.stdout.write(e);for(let t of n){let r=T(t),s=r>0?`$${r.toFixed(2)}`:"\u2014";process.stdout.write(`${t.id.padEnd(18)} ${p(t.name,20).padEnd(20)} ${t.framework.padEnd(10)} ${t.preferredProvider.padEnd(8)} ${s.padEnd(10)} ${t.cwd}
12
- `)}return 0}async function G(n){let[e,t]=n;if(e==="show"){if(!t)return process.stderr.write(`usage: sootsim agent project show <id>
13
- `),2;let r=l(t);return r?(process.stdout.write(JSON.stringify(r,null,2)+`
14
- `),0):(process.stderr.write(`no project with id=${t}
15
- `),1)}return e==="remove"?t?(I(t),process.stdout.write(`\u2713 removed project ${t}
16
- `),0):(process.stderr.write(`usage: sootsim agent project remove <id>
17
- `),2):(process.stderr.write(`usage: sootsim agent project [show|remove] <id>
18
- `),2)}async function H(n){let{flags:e}=P(n,{"--project":"value","--all":"bool"}),t=e["--project"]||void 0,r=e["--all"]===!0,s=D(t);if(r||(s=s.filter(i=>i.status!=="ended")),s=s.sort((i,a)=>a.lastSeenAt-i.lastSeenAt),s.length===0)return process.stdout.write(r?`no sessions.
19
- `:`no active sessions. pass --all to include ended.
20
- `),0;let o=`${"SESSION".padEnd(26)} ${"PROJECT".padEnd(18)} ${"PROVIDER".padEnd(8)} ${"STATUS".padEnd(16)} ${"ALIVE".padEnd(5)} LAST PROMPT
21
- `;process.stdout.write(o);for(let i of s){let a=R(i.wrapperPid,i.id)?"yes":"no",m=i.lastPrompt?p(i.lastPrompt,40):"";process.stdout.write(`${N(i.id).padEnd(26)} ${N(i.projectId).padEnd(18)} ${i.provider.padEnd(8)} ${i.status.padEnd(16)} ${a.padEnd(5)} ${m}
22
- `)}return 0}async function q(n){let{flags:e,positional:t}=P(n,{"--provider":"value","--codex-bin":"value","--claude-bin":"value","--fresh-thread":"bool"}),r=t[0];if(!r)return process.stderr.write(`usage: sootsim agent start <projectId> [--provider codex|claude] [--fresh-thread]
23
- `),2;let s=await v(i=>i.startSession({projectId:r,provider:U(e["--provider"],void 0)||void 0,codexBin:e["--codex-bin"]||void 0,claudeBin:e["--claude-bin"]||void 0,freshThread:e["--fresh-thread"]===!0})),o=l(s.session.projectId);return process.stdout.write(`\u2713 session ${s.session.id} started
24
- project: ${o.name} (${o.id})
25
- provider: ${s.session.provider}
26
- pid: ${s.wrapperPid}
27
- prompt with: sootsim agent prompt ${s.session.id} "<text>"
28
- watch events: sootsim agent watch ${s.session.id}
29
- `),0}async function K(n){let[e,...t]=n,r=t.join(" ");return!e||!r?(process.stderr.write(`usage: sootsim agent prompt <sessionId> "<text>"
30
- `),2):(await v(s=>s.sendPrompt(e,{text:r})),process.stdout.write(`\u2713 prompt sent to ${e}
31
- `),0)}async function X(n){let e=n[0];if(!e)return process.stderr.write(`usage: sootsim agent watch <sessionId>
32
- `),2;process.stdout.write(`watching ${e}
33
- ---
34
- `);let t=await h({clientLabel:"sootsim-agent-watch"}),r,s=new Promise(a=>{r=a}),o=t.onAgentEvent(({sessionId:a,event:m})=>{a===e&&process.stdout.write(z(m)+`
35
- `)}),i=t.onDisconnect(()=>{process.stderr.write(`
36
- [sootsim] daemon disconnected; exiting watch.
37
- `),r(1)});try{await t.subscribeEvents(e)}catch(a){throw o(),i(),t.close(),a}return process.on("SIGINT",()=>{o(),i(),t.unsubscribeEvents(e).catch(()=>{}),t.close(),r(130)}),s}function z(n){let e=ee(n.ts);switch(n.type){case"ready":return`[${e}] ready ${n.provider} @ ${n.cwd}`;case"prompt-received":return`[${e}] prompt "${p(n.text,120)}"`;case"turn-started":return`[${e}] turn-started ${n.turnId??""}`;case"turn-message":return`[${e}] turn-message ${n.delta.replace(/\s+$/,"")}`;case"turn-reasoning":return`[${e}] turn-reasoning ${p(n.delta,200)}`;case"tool-call":return`[${e}] tool-call ${n.name}`;case"file-edited":return`[${e}] file-edited ${n.kind.padEnd(6)} ${n.path}`;case"turn-completed":return`[${e}] turn-completed ${n.durationMs}ms files=${n.filesTouched.length}`;case"approval-needed":return`[${e}] approval ${n.kind}`;case"error":return`[${e}] error ${n.message}`;case"exited":return`[${e}] exited code=${n.code}`;case"file-diff-delta":return`[${e}] file-diff-delta ${n.path}`;case"turn-plan":return`[${e}] turn-plan steps=${n.steps.length}`;default:return`[${e}] ${n.type}`}}async function Q(n){let e=n[0];if(!e)return process.stderr.write(`usage: sootsim agent transcript <sessionId>
38
- `),2;let t=g(e);return w.existsSync(t)?(process.stdout.write(w.readFileSync(t,"utf8")),0):(process.stderr.write(`transcript not found: ${t}
39
- `),1)}async function Y(n){let e=n[0];return e?(await v(t=>t.endSession(e)),process.stdout.write(`\u2713 ended session ${e}
40
- `),0):(process.stderr.write(`usage: sootsim agent end <sessionId>
41
- `),2)}async function Z(){let n=A();return process.stdout.write(`userData: ${n}
42
- state: ${d.join(n,"attached-projects.json")}
43
- sessions: ${d.join(n,"sessions")}
44
- transcripts: ${d.join(n,"transcripts")}
45
- locks: ${d.join(n,"locks")}
46
- `),0}function p(n,e){return n.length<=e?n:n.slice(0,e-1)+"\u2026"}function N(n){return n.length>14?`${n.slice(0,14)}\u2026`:n}function ee(n){return new Date(n).toISOString().replace("T"," ").slice(0,19)}var he={sessionDir:k,promptFifoPath:x,eventsFifoPath:O,transcriptPath:g,parseAgentEventLine:E};export{he as __internals,fe as runAgentCommand};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d}from"./chunk-FHMPJZKM.js";import"./chunk-GQAWFQNN.js";import"./chunk-DQZEN256.js";import"./chunk-KUUFY6FC.js";import"./chunk-5KY7TQ42.js";import"./chunk-JDGLBW64.js";export{c as ensureDaemonRunning,a as ensureRuntimeInstalled,d as ensureSootsimReady,b as resolveBootstrapPort};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d,e}from"./chunk-NXGQS7IR.js";import"./chunk-JDGLBW64.js";export{e as BETA_ASK_HEADLINE,c as BETA_LABEL,d as BETA_TAGLINE,b as BETA_VERSION_TARGET,a as IS_BETA};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.169 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- var t="http://localhost:5173/";export{t as a};