stim 1.16.0 → 1.17.0

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 (107) hide show
  1. package/dist/{activity-BxwdD81S.mjs → activity-IRvJY_ru.mjs} +23 -3
  2. package/dist/{agent-device-usage-output-Dpih1Tfa.mjs → agent-device-usage-output-BK41vnzN.mjs} +3 -3
  3. package/dist/{android-Ift4kVvt.mjs → android--GorydsK.mjs} +679 -317
  4. package/dist/{android-B_NumVyQ.mjs → android-B_YNZy61.mjs} +7 -43
  5. package/dist/{android-r2rBL8kV.mjs → android-DE4v59Mb.mjs} +12 -3
  6. package/dist/{android-cas-8U4DPqeR.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
  7. package/dist/{app-install-CKWdbXIX.mjs → app-install-VPkI5WEg.mjs} +6 -5
  8. package/dist/{budget-DjrURfQn.mjs → budget-DM984mnd.mjs} +34 -18
  9. package/dist/{build-plan-DjeSb2Xl.mjs → build-plan-CmQCoyT-.mjs} +345 -86
  10. package/dist/{build-slots-rUk77xqh.mjs → build-slots-41v_d2x9.mjs} +35 -25
  11. package/dist/{cli-BkYZ_6hy.mjs → cli-Daxug5-K.mjs} +17 -17
  12. package/dist/cli.mjs +1 -1
  13. package/dist/collector-run.mjs +3 -3
  14. package/dist/{command-output-DnJMdEY-.mjs → command-output-CDAIFD-8.mjs} +3 -3
  15. package/dist/{created-devices-D5owjN4C.mjs → created-devices-CpvzBL25.mjs} +1 -1
  16. package/dist/{dependency-state-B6cMXZab.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
  17. package/dist/{deps-urbmfXig.mjs → deps-DNWaQs4V.mjs} +1 -1
  18. package/dist/{dev-client-CroAIjjj.mjs → dev-client-B-XC4uRH.mjs} +2 -2
  19. package/dist/{device-DZQjF7H5.mjs → device-C8IOoJPv.mjs} +7 -7
  20. package/dist/device-capacity-D0GWHnTM.mjs +580 -0
  21. package/dist/device-host-worker.mjs +436 -154
  22. package/dist/{device-ios-BadmeL2V.mjs → device-ios-Bc7vbGwl.mjs} +75 -44
  23. package/dist/{device-lease-D-hLqx1b.mjs → device-lease-DpoS36_u.mjs} +2 -2
  24. package/dist/{device-lease-run-CqjShqWW.mjs → device-lease-run-B8YltIH_.mjs} +3 -3
  25. package/dist/{device-pool-CUaEWMMm.mjs → device-pool-3o7s5lo4.mjs} +2 -2
  26. package/dist/{device-remote-D5WVWqOq.mjs → device-remote-hFSkpfQu.mjs} +12 -12
  27. package/dist/{doctor-Cimf-gGB.mjs → doctor-Cnk9d7Hm.mjs} +32 -51
  28. package/dist/{error-diagnostics-B9L4Ae0x.mjs → error-diagnostics-C-NMnhrk.mjs} +7 -7
  29. package/dist/{gc-Bqy4bjEu.mjs → gc-DxCL2A46.mjs} +308 -26
  30. package/dist/{gradle-BsO1MdsH.mjs → gradle-D7-L2Th3.mjs} +3 -3
  31. package/dist/{guide-B_uyKTxW.mjs → guide-DjrTQGik.mjs} +1662 -565
  32. package/dist/{guide-status-CjEZMAD4.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
  33. package/dist/hosted-android-DwLS4Mse.mjs +32 -0
  34. package/dist/{hosted-client-a-WmqE2L.mjs → hosted-client-wk1W_CNW.mjs} +2 -3
  35. package/dist/hosted-ios-aMo7Cljf.mjs +15 -0
  36. package/dist/{hosted-logs-D5g041Q3.mjs → hosted-logs-DqYmWtXc.mjs} +16 -16
  37. package/dist/{hosted-macos-BSZyIb3y.mjs → hosted-macos-dlHXEeF5.mjs} +4 -4
  38. package/dist/{hosted-ios-CIWelIqf.mjs → hosted-native-C9xJ9rpA.mjs} +77 -42
  39. package/dist/{idle-DYVymG92.mjs → idle-B2m-Ovod.mjs} +122 -58
  40. package/dist/{idle-shutdown-CTsDnAde.mjs → idle-shutdown-_3oOV1V_.mjs} +25 -17
  41. package/dist/{in-use-DvBw0Kxo.mjs → in-use-CvGOUn-x.mjs} +11 -11
  42. package/dist/{ios-aq3QoGI3.mjs → ios-DE6duQoz.mjs} +170 -88
  43. package/dist/{ios-BaInn3sW.mjs → ios-DSfnPfse.mjs} +4 -4
  44. package/dist/{ios-device-FfCN_C8p.mjs → ios-device-pAN4t54-.mjs} +1 -1
  45. package/dist/ios-state-8L3lgum6.mjs +89 -0
  46. package/dist/{launch-verify-2DBRizjo.mjs → launch-verify-lTnWZikR.mjs} +2 -2
  47. package/dist/{logs-CHEVK8zD.mjs → logs-DUoKlDoS.mjs} +36 -23
  48. package/dist/{client-BTY686R6.mjs → machines-C2XiNwh9.mjs} +278 -20
  49. package/dist/{macos-CSYEAFmF.mjs → macos-32po3kUq.mjs} +14 -14
  50. package/dist/macos-run.mjs +1 -1
  51. package/dist/maintenance-run.mjs +2 -2
  52. package/dist/{metro-BeK1ZPz1.mjs → metro-g8NqiJp-.mjs} +1 -1
  53. package/dist/{metro-gateway-BJ-rxQAB.mjs → metro-gateway-DmslUM30.mjs} +22 -3
  54. package/dist/{named-ports-Bk_YXEmE.mjs → named-ports-C8m8EQs2.mjs} +2 -2
  55. package/dist/{native-run-NaBrRFtQ.mjs → native-run-D1Q56rAD.mjs} +17 -6
  56. package/dist/{native-runtime-DBzRnEKS.mjs → native-runtime-DhJI3IVQ.mjs} +6 -6
  57. package/dist/offload-worker.mjs +8 -8
  58. package/dist/{teardown-CL6vDs2Y.mjs → ownership-BMLqyBgJ.mjs} +603 -26
  59. package/dist/{ownership-DiA16AsN.mjs → ownership-DyRQru0q.mjs} +2 -2
  60. package/dist/{ports-R2k4Knz3.mjs → ports-4nYqxPWC.mjs} +2 -2
  61. package/dist/{prebuild-D2fsca_Z.mjs → prebuild-D7RUMkwN.mjs} +3 -3
  62. package/dist/{preview-DhVbvPAZ.mjs → preview-CxPRIEht.mjs} +7 -7
  63. package/dist/{project-Du91Ilvc.mjs → project-D47Rh0ly.mjs} +7 -2
  64. package/dist/{recordings-BIXOzVnr.mjs → recordings-BNgsD1Tr.mjs} +1 -1
  65. package/dist/{reload-rM9jV1fz.mjs → reload-qZei6iWn.mjs} +62 -35
  66. package/dist/{remote-cache-C8-V13XN.mjs → remote-cache-Ci4DQYYj.mjs} +3 -3
  67. package/dist/{run-f4vI6EnK.mjs → run-CkE9y0nc.mjs} +1 -1
  68. package/dist/{server-bare-CpPGmnmO.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
  69. package/dist/{server-expo-MC0rHlOC.mjs → server-expo-CHTyXD0G.mjs} +2 -2
  70. package/dist/server-expo-CeakhI8P.mjs +2 -0
  71. package/dist/{settings-oTSkkNrM.mjs → settings-Cfd-0r2d.mjs} +10 -9
  72. package/dist/{settings-ZkDzkzvB.mjs → settings-SR9OmW9h.mjs} +3 -3
  73. package/dist/settings.schema.json +42 -13
  74. package/dist/{simslim-B8-7AoTI.mjs → simslim-BWNrc3M3.mjs} +3 -3
  75. package/dist/{slot-launch-B8VrHxn1.mjs → slot-launch-DmjBmpQV.mjs} +8 -8
  76. package/dist/{start-CX8Ig-U2.mjs → start-BopZcr4n.mjs} +1 -1
  77. package/dist/{start-ClDfwXa4.mjs → start-uee1wWjY.mjs} +11 -11
  78. package/dist/{state-DcW6vOd_.mjs → state-BKa8wNN5.mjs} +1 -1
  79. package/dist/{state-3DwSQIBK.mjs → state-DyaNTgfR.mjs} +1 -1
  80. package/dist/{state-C1jB7Q8q.mjs → state-PeQiJuIu.mjs} +2 -2
  81. package/dist/{stats-CEZ_i5je.mjs → stats-Bq4fJOdB.mjs} +7 -4
  82. package/dist/{status-BuvBFeII.mjs → status-CzS4bPO9.mjs} +4 -4
  83. package/dist/{status-Cs7VLg9D.mjs → status-dXX2JX1C.mjs} +125 -35
  84. package/dist/{stop-Dt_dM16A.mjs → stop-BCwvvs7F.mjs} +2 -2
  85. package/dist/{stop-Bffozsac.mjs → stop-BjizrQwv.mjs} +48 -27
  86. package/dist/{stop-B0Yxtmiy.mjs → stop-Cr1d8DSE.mjs} +4 -4
  87. package/dist/{stop-cause-CidxIqup.mjs → stop-cause-rhmGfwvt.mjs} +1 -1
  88. package/dist/supervisor-run.d.mts +7 -4
  89. package/dist/supervisor-run.mjs +26 -28
  90. package/dist/{support-D4aYJniO.mjs → support-BJPgYCu-.mjs} +44 -19
  91. package/dist/{support-CAP8qDUF.mjs → support-F9-sr0Oi.mjs} +6 -6
  92. package/dist/{toolchain-DkUflduf.mjs → toolchain-CNIBTIOv.mjs} +7 -7
  93. package/dist/{warm-progress-CpGv3Wb6.mjs → warm-progress-DF4JRvYI.mjs} +3 -3
  94. package/dist/watchman-APYjXSQa.mjs +48 -0
  95. package/dist/{web-DWUaMp8P.mjs → web-exQtGkoc.mjs} +12 -13
  96. package/dist/web-run.mjs +4 -4
  97. package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
  98. package/dist/{workspace-state-B8-aJ1_5.mjs → workspace-state-DqcCbpE7.mjs} +2 -2
  99. package/dist/{workspaces-BV4pdHFz.mjs → workspaces-Bcib97-6.mjs} +3 -3
  100. package/dist/{worktree-jnG8Iph5.mjs → worktree-Dc98cE6_.mjs} +1 -1
  101. package/dist/{worktree-K-xj9Db0.mjs → worktree-ZpZR3S1k.mjs} +141 -40
  102. package/package.json +5 -5
  103. package/dist/device-capacity-BHwxNBlu.mjs +0 -136
  104. package/dist/ios-state-DS5UpYTH.mjs +0 -48
  105. package/dist/machines-BIBUdhpj.mjs +0 -265
  106. package/dist/ownership-hCkPzGmr.mjs +0 -571
  107. package/dist/server-expo-CVqO2c35.mjs +0 -2
@@ -1,5 +1,5 @@
1
1
  import { E as upsertProject, d as getConfigDir, p as getProject } from "./config-D0DbpZtM.mjs";
2
- import { n as appProjectProblem } from "./project-Du91Ilvc.mjs";
2
+ import { n as appProjectProblem } from "./project-D47Rh0ly.mjs";
3
3
  import { t as compareStimVersions } from "./stim-installations-QMEI32v1.mjs";
4
4
  import { mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
@@ -0,0 +1,32 @@
1
+ import { a as call, o as connectHost } from "./hosted-client-wk1W_CNW.mjs";
2
+ import { i as requestHostedMetro } from "./metro-gateway-DmslUM30.mjs";
3
+ import { n as prepareHostedNative, r as stopHostedNative, t as placeHostedNative } from "./hosted-native-C9xJ9rpA.mjs";
4
+ //#region src/device-host/hosted-android.ts
5
+ async function prepareHostedAndroid(machine, selectors, recorded) {
6
+ return await prepareHostedNative(machine, selectors, recorded, "android");
7
+ }
8
+ async function placeHostedAndroid(target, options) {
9
+ return await placeHostedNative(target, {
10
+ ...options,
11
+ platform: "android",
12
+ reserved: (placement) => options.reserved(placement)
13
+ });
14
+ }
15
+ async function stopHostedAndroid(root, slot) {
16
+ await stopHostedNative(root, slot, "android");
17
+ }
18
+ async function reopenHostedAndroidMetro(root, placement, clientMetroPort) {
19
+ const host = await connectHost(placement.machine, void 0, true);
20
+ try {
21
+ const gateway = await requestHostedMetro(root, placement.session, host.credential);
22
+ await call(host, "device-host.metro.open", {
23
+ session: placement.session,
24
+ clientMetroPort,
25
+ ...gateway
26
+ });
27
+ } finally {
28
+ host.connection.close();
29
+ }
30
+ }
31
+ //#endregion
32
+ export { stopHostedAndroid as i, prepareHostedAndroid as n, reopenHostedAndroidMetro as r, placeHostedAndroid as t };
@@ -1,5 +1,4 @@
1
- import { t as BuildConnection, y as pinnedEndpoint$1 } from "./client-BTY686R6.mjs";
2
- import { t as configuredMachines } from "./machines-BIBUdhpj.mjs";
1
+ import { b as pinnedEndpoint$1, r as BuildConnection, t as configuredMachines } from "./machines-C2XiNwh9.mjs";
3
2
  import { createHash } from "node:crypto";
4
3
  import { lstatSync, readFileSync, readdirSync, readlinkSync } from "node:fs";
5
4
  import { join } from "node:path";
@@ -107,7 +106,7 @@ async function probeSession(placement, timeoutMs) {
107
106
  let host;
108
107
  try {
109
108
  host = await connectHost(placement.machine, timeoutMs);
110
- const session = await attach(host, placement.session, timeoutMs, placement.device === void 0 ? "macos" : "ios");
109
+ const session = await attach(host, placement.session, timeoutMs, placement.platform ?? (placement.device === void 0 ? "macos" : isJsonObject(placement.device) && "avdName" in placement.device ? "android" : "ios"));
111
110
  if (session.state === "ready" || session.state === "stopped") return { state: session.state };
112
111
  return {
113
112
  state: "unknown",
@@ -0,0 +1,15 @@
1
+ import { n as prepareHostedNative, r as stopHostedNative, t as placeHostedNative } from "./hosted-native-C9xJ9rpA.mjs";
2
+ //#region src/device-host/hosted-ios.ts
3
+ async function prepareHostedIos(machine, selectors, recorded) {
4
+ return await prepareHostedNative(machine, selectors, recorded, "ios");
5
+ }
6
+ async function placeHostedIos(target, options) {
7
+ return await placeHostedNative(target, {
8
+ ...options,
9
+ platform: "ios",
10
+ reserved: (placement) => options.reserved(placement)
11
+ });
12
+ }
13
+ const stopHostedIos = stopHostedNative;
14
+ //#endregion
15
+ export { prepareHostedIos as n, stopHostedIos as r, placeHostedIos as t };
@@ -1,11 +1,11 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { L as deviceSlotFileKey, W as validateDeviceSlot } from "./config-D0DbpZtM.mjs";
3
- import { l as phaseLine } from "./command-output-DnJMdEY-.mjs";
4
- import { v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
5
- import { t as macosDir } from "./state-3DwSQIBK.mjs";
3
+ import { l as phaseLine } from "./command-output-CDAIFD-8.mjs";
4
+ import { v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
5
+ import { t as macosDir } from "./state-DyaNTgfR.mjs";
6
6
  import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
7
- import { v as parseMachine$1 } from "./client-BTY686R6.mjs";
8
- import { a as call } from "./hosted-client-a-WmqE2L.mjs";
7
+ import { y as parseMachine$1 } from "./machines-C2XiNwh9.mjs";
8
+ import { a as call } from "./hosted-client-wk1W_CNW.mjs";
9
9
  import { mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { dirname, join } from "node:path";
11
11
  import { LOG_ROTATE_BYTES, withDirLock } from "@stim-cli/core";
@@ -14,7 +14,7 @@ import { isJsonObject, parseHostedAgentGrant, parseHostedLogsCursor } from "@sti
14
14
  function agentAccess(file, credential, grant, note, platform = "macos") {
15
15
  const parsed = grant === void 0 ? null : parseHostedAgentGrant(grant);
16
16
  if (grant !== void 0 && !parsed) note(`${credential.machine} offered agent control this Stim does not support.`);
17
- if (!parsed || parsed.driver === "none" || parsed.lease.backend === "ios-instance" !== (platform === "ios")) {
17
+ if (!parsed || parsed.driver === "none" || parsed.lease.backend !== (platform === "macos" ? void 0 : `${platform}-instance`)) {
18
18
  rmSync(file, { force: true });
19
19
  return {
20
20
  driver: "none",
@@ -29,7 +29,7 @@ function agentAccess(file, credential, grant, note, platform = "macos") {
29
29
  sessionIsolation: "tenant",
30
30
  runId: parsed.lease.runId,
31
31
  ...platform === "macos" ? { leaseId: parsed.scope } : {},
32
- leaseBackend: platform === "ios" ? "ios-instance" : "macos-app",
32
+ leaseBackend: platform === "macos" ? "macos-app" : `${platform}-instance`,
33
33
  leaseProvider: "proxy",
34
34
  clientId: parsed.lease.clientId,
35
35
  deviceKey: parsed.lease.deviceKey,
@@ -108,9 +108,9 @@ const MACOS = {
108
108
  platform: "macos",
109
109
  slot: "default"
110
110
  };
111
- const logsStateDir = (root, target) => target.platform === "macos" ? macosDir(root) : join(workspaceDir(root), "ios", validateDeviceSlot(target.slot));
111
+ const logsStateDir = (root, target) => target.platform === "macos" ? macosDir(root) : join(workspaceDir(root), target.platform, validateDeviceSlot(target.slot));
112
112
  const cursorFile = (root, target) => join(logsStateDir(root, target), "host-logs-cursor.json");
113
- const hostLogFile = (root, target) => join(workspaceLogsDir$1(root), `${target.platform === "macos" ? "macos" : deviceSlotFileKey("ios", target.slot)}-host.ndjson`);
113
+ const hostLogFile = (root, target) => join(workspaceLogsDir$1(root), `${target.platform === "macos" ? "macos" : deviceSlotFileKey(target.platform, target.slot)}-host.ndjson`);
114
114
  function readCursor(root, session, target) {
115
115
  try {
116
116
  const stored = JSON.parse(readFileSync(cursorFile(root, target), "utf8"));
@@ -126,10 +126,10 @@ function commitPage(root, session, before, records, cursor, target) {
126
126
  if (records.length) {
127
127
  const writer = createNdjsonWriter(hostLogFile(root, target), { maxBytes: LOG_ROTATE_BYTES });
128
128
  try {
129
- for (const record of records) writer.write(target.platform === "ios" ? {
129
+ for (const record of records) writer.write(target.platform !== "macos" ? {
130
130
  ...record,
131
131
  src: "device",
132
- platform: "ios",
132
+ platform: target.platform,
133
133
  slot: target.slot,
134
134
  event: "hosted_native_log"
135
135
  } : record);
@@ -174,14 +174,14 @@ async function pullHostedMacosLogs(root, placement, host, target = MACOS, maxPag
174
174
  }
175
175
  checkpoint = nextCheckpoint;
176
176
  }
177
- if (target.platform === "ios") throw new Error(final ? "Final native log copy reached its time or page bound; the host keeps collected logs after stop." : "Hosted native logs still have unread pages; run stim logs again.");
177
+ if (target.platform !== "macos") throw new Error(final ? "Final native log copy reached its time or page bound; the host keeps collected logs after stop." : "Hosted native logs still have unread pages; run stim logs again.");
178
178
  }
179
- async function pullHostedIosLogs(root, slot, placement, host, final = false) {
180
- if (!host.connection.supports("hosted-ios-data")) throw new Error(`${host.machine} needs a newer stim-server to collect hosted iOS native logs.`);
179
+ async function pullHostedNativeLogs(root, slot, placement, host, final = false, platform = "ios") {
180
+ if (!host.connection.supports(platform === "ios" ? "hosted-ios-data" : "hosted-android-data")) throw new Error(`${host.machine} needs a newer stim-server to collect hosted ${platform === "ios" ? "iOS" : "Android"} native logs.`);
181
181
  await pullHostedMacosLogs(root, placement, host, {
182
- platform: "ios",
182
+ platform,
183
183
  slot
184
184
  }, final ? 1024 : MAX_PAGES, final);
185
185
  }
186
186
  //#endregion
187
- export { agentAccess as i, pullHostedMacosLogs as n, closeAgentConnection as r, pullHostedIosLogs as t };
187
+ export { agentAccess as i, pullHostedNativeLogs as n, closeAgentConnection as r, pullHostedMacosLogs as t };
@@ -1,7 +1,7 @@
1
- import { l as phaseLine } from "./command-output-DnJMdEY-.mjs";
2
- import { t as macosDir } from "./state-3DwSQIBK.mjs";
3
- import { a as call, c as hostedSession, d as sha256, f as sleep, i as bundleManifest, m as upload, n as SESSION_TIMEOUT_MS, o as connectHost, p as unknownSession, r as attach, s as heldNoLonger, t as INSTALL_TIMEOUT_MS, u as settle } from "./hosted-client-a-WmqE2L.mjs";
4
- import { i as agentAccess, n as pullHostedMacosLogs, r as closeAgentConnection } from "./hosted-logs-D5g041Q3.mjs";
1
+ import { l as phaseLine } from "./command-output-CDAIFD-8.mjs";
2
+ import { t as macosDir } from "./state-DyaNTgfR.mjs";
3
+ import { a as call, c as hostedSession, d as sha256, f as sleep, i as bundleManifest, m as upload, n as SESSION_TIMEOUT_MS, o as connectHost, p as unknownSession, r as attach, s as heldNoLonger, t as INSTALL_TIMEOUT_MS, u as settle } from "./hosted-client-wk1W_CNW.mjs";
4
+ import { i as agentAccess, r as closeAgentConnection, t as pullHostedMacosLogs } from "./hosted-logs-DqYmWtXc.mjs";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import { rmSync } from "node:fs";
7
7
  import { join } from "node:path";
@@ -1,50 +1,57 @@
1
- import { r as readWorkspaceState$1, v as workspaceDir } from "./workspace-state-B8-aJ1_5.mjs";
2
- import { a as call, c as hostedSession, d as sha256, f as sleep, i as bundleManifest, m as upload, n as SESSION_TIMEOUT_MS, o as connectHost, p as unknownSession, r as attach, s as heldNoLonger, t as INSTALL_TIMEOUT_MS, u as settle } from "./hosted-client-a-WmqE2L.mjs";
3
- import { i as requireHostedMetro, n as closeHostedMetro, r as requestHostedMetro } from "./metro-gateway-BJ-rxQAB.mjs";
4
- import { i as agentAccess, r as closeAgentConnection, t as pullHostedIosLogs } from "./hosted-logs-D5g041Q3.mjs";
5
- import { n as writeHostedIos } from "./ios-state-DS5UpYTH.mjs";
1
+ import { r as readWorkspaceState$1, v as workspaceDir } from "./workspace-state-DqcCbpE7.mjs";
2
+ import { a as call, c as hostedSession, d as sha256, f as sleep, i as bundleManifest, m as upload, n as SESSION_TIMEOUT_MS, o as connectHost, p as unknownSession, r as attach, s as heldNoLonger, t as INSTALL_TIMEOUT_MS, u as settle } from "./hosted-client-wk1W_CNW.mjs";
3
+ import { a as requireHostedMetro, i as requestHostedMetro, n as closeHostedMetro } from "./metro-gateway-DmslUM30.mjs";
4
+ import { i as agentAccess, n as pullHostedNativeLogs, r as closeAgentConnection } from "./hosted-logs-DqYmWtXc.mjs";
5
+ import { s as writeHostedNative } from "./ios-state-8L3lgum6.mjs";
6
6
  import { randomUUID } from "node:crypto";
7
- import { rmSync } from "node:fs";
7
+ import { readFileSync, rmSync } from "node:fs";
8
8
  import { join } from "node:path";
9
- import { hostedIosRecords, isJsonObject, parseHostedChoice, parseHostedDevice, parseHostedIosPlacement, unreadableHostedIos } from "@stim-cli/core/state";
10
- //#region src/device-host/hosted-ios.ts
9
+ import { hostedNativeRecords, isJsonObject, parseHostedAndroidDevice, parseHostedChoice, parseHostedDevice, parseHostedNativeOffer, parseHostedNativePlacement, unreadableHostedNative } from "@stim-cli/core/state";
10
+ //#region src/device-host/hosted-native.ts
11
11
  function hostingRefusal(machine, error) {
12
12
  return Object.assign(/* @__PURE__ */ new Error(`${machine}: ${error instanceof Error ? error.message : String(error)}`), {
13
13
  code: "STIM_HOSTING_REFUSED",
14
14
  ...error instanceof Error && "remedy" in error ? { remedy: error.remedy } : {}
15
15
  });
16
16
  }
17
+ const androidAgentRemoteConfig = (root, slot) => join(workspaceDir(root), "hosted-android", slot, "agent-device-remote.json");
17
18
  const iosAgentRemoteConfig = (root, slot) => join(workspaceDir(root), "hosted-ios", slot, "agent-device-remote.json");
18
- async function prepareHostedIos(machine, selectors, recorded) {
19
+ async function prepareHostedNative(machine, selectors, recorded, platform = "ios", resumeOnly = false) {
19
20
  let host;
20
21
  try {
21
22
  host = await connectHost(machine, void 0, true);
22
23
  if (recorded) {
23
24
  let session = null;
24
25
  try {
25
- session = await settle(host, await attach(host, recorded.session, void 0, "ios"), ["preparing", "stopping"], SESSION_TIMEOUT_MS, "ios");
26
+ session = await settle(host, await attach(host, recorded.session, void 0, platform), ["preparing", "stopping"], SESSION_TIMEOUT_MS, platform);
26
27
  } catch (error) {
27
28
  if (!heldNoLonger(error)) throw error;
28
29
  }
29
30
  if (session && session.state !== "stopped") {
30
31
  if (session.state !== "ready") throw unknownSession(host, session);
31
- const device = parseHostedDevice(session.device);
32
+ const device = platform === "ios" ? parseHostedDevice(session.device) : parseHostedAndroidDevice(session.device);
32
33
  if (!device) throw new Error("The ready session has no simulator identity.");
33
- if (selectors.deviceType && selectors.deviceType !== device.deviceType || selectors.runtime && selectors.runtime.replace(/^iOS /, "") !== device.runtime.replace(/^iOS /, "")) throw new Error(`This session uses ${device.deviceType} (iOS ${device.runtime.replace(/^iOS /, "")}); run stim stop first to change it.`);
34
+ if ("udid" in device ? selectors.deviceType && selectors.deviceType !== device.deviceType || selectors.runtime && selectors.runtime.replace(/^iOS /, "") !== device.runtime.replace(/^iOS /, "") : selectors.systemImage && selectors.systemImage !== device.systemImage || selectors.deviceProfile && selectors.deviceProfile !== device.deviceProfile) throw new Error("udid" in device ? `This session uses ${device.deviceType} (iOS ${device.runtime.replace(/^iOS /, "")}); run stim stop first to change it.` : `This session uses ${device.deviceProfile} (${device.systemImage}); run stim stop first to change it.`);
34
35
  return {
35
36
  host,
36
37
  choice: device,
37
- session
38
+ session,
39
+ selection: {
40
+ selected: recorded.selected,
41
+ ...recorded.reason !== void 0 ? { reason: recorded.reason } : {}
42
+ }
38
43
  };
39
44
  }
40
45
  }
46
+ if (resumeOnly) return null;
41
47
  const offer = await call(host, "device-host.offer", {
42
- platform: "ios",
48
+ platform,
43
49
  ...selectors
44
50
  }, 3e3);
45
- const choice = offer.platform === "ios" ? parseHostedChoice(offer.choice) : null;
51
+ const parsedOffer = parseHostedNativeOffer(offer);
52
+ const choice = platform === "ios" ? offer.platform === "ios" ? parseHostedChoice(offer.choice) : null : parsedOffer?.platform === "android" ? parsedOffer.choice : null;
46
53
  if (typeof offer.declined === "string") throw new Error(offer.declined);
47
- if (!choice) throw new Error("No compatible installed iOS simulator runtime was offered.");
54
+ if (!choice) throw new Error(platform === "ios" ? "No compatible installed iOS simulator runtime was offered." : "No compatible installed Android emulator image was offered.");
48
55
  if (!isJsonObject(offer.capacity) || offer.capacity.available === 0) throw new Error("No hosted device capacity is available.");
49
56
  if (!isJsonObject(offer.resources) || offer.resources.memoryPressure !== "normal") throw new Error("Host memory pressure is unknown or elevated.");
50
57
  return {
@@ -59,35 +66,48 @@ async function prepareHostedIos(machine, selectors, recorded) {
59
66
  host?.connection.close();
60
67
  }
61
68
  }
62
- async function placeHostedIos(target, { root, slot, bundle, handoff, bundleId, selectors, release, devClientScheme, reserved, note, metro = requestHostedMetro }) {
69
+ async function reserveHostedNative(host, target, params, platform) {
70
+ try {
71
+ return hostedSession(host, await call(host, "device-host.reserve", params), platform);
72
+ } catch (error) {
73
+ if (target.selection?.selected !== "auto") throw error;
74
+ throw Object.assign(/* @__PURE__ */ new Error(`${error instanceof Error ? error.message : String(error)} Retry stim ${platform} --remote auto; the build already targets ${host.machine}'s architecture.`), { remedy: `Retry stim ${platform} --remote auto.` });
75
+ }
76
+ }
77
+ async function placeHostedNative(target, { root, slot, bundle, handoff, bundleId, selectors, release, devClientScheme, reserved, note, metro = requestHostedMetro, metroPort, platform = "ios" }) {
63
78
  let host = target.host;
64
79
  try {
65
80
  if (!release && metro === requestHostedMetro) requireHostedMetro(root);
66
81
  host = await connectHost(host.machine, void 0, true);
67
82
  target.host = host;
68
- let session = (target.session ? await attach(host, target.session.id, void 0, "ios") : null) ?? hostedSession(host, await call(host, "device-host.reserve", {
69
- platform: "ios",
83
+ let session = (target.session ? await attach(host, target.session.id, void 0, platform) : null) ?? await reserveHostedNative(host, target, {
84
+ platform,
70
85
  workspace: root,
71
86
  slot,
72
87
  attempt: randomUUID(),
73
- ...selectors
74
- }), "ios");
88
+ ...selectors,
89
+ ...platform === "android" && "systemImage" in target.choice ? {
90
+ systemImage: target.choice.systemImage,
91
+ deviceProfile: target.choice.deviceProfile
92
+ } : {}
93
+ }, platform);
75
94
  let placement = {
76
95
  machine: host.machine,
77
- selected: host.machine,
96
+ selected: target.selection?.selected ?? host.machine,
97
+ ...target.selection ? { reason: target.selection.reason } : {},
78
98
  session: session.id,
79
99
  appAttempt: randomUUID(),
80
- device: parseHostedDevice(session.device),
100
+ device: platform === "ios" ? parseHostedDevice(session.device) : parseHostedAndroidDevice(session.device),
81
101
  agent: {
82
102
  driver: "none",
83
103
  setting: "hosting.agentDriver"
84
104
  }
85
105
  };
86
106
  reserved(placement);
87
- session = await settle(host, session, ["preparing"], SESSION_TIMEOUT_MS, "ios");
107
+ session = await settle(host, session, ["preparing"], SESSION_TIMEOUT_MS, platform);
88
108
  if (session.state !== "ready") throw unknownSession(host, session);
89
- const device = parseHostedDevice(session.device);
90
- if (!device || device.architecture !== target.choice.architecture) throw new Error("The reserved simulator does not match the offered architecture.");
109
+ const device = platform === "ios" ? parseHostedDevice(session.device) : parseHostedAndroidDevice(session.device);
110
+ if (!device || device.architecture !== target.choice.architecture || "avdName" in device && "systemImage" in target.choice && (device.systemImage !== target.choice.systemImage || device.deviceProfile !== target.choice.deviceProfile)) throw new Error(platform === "ios" ? "The reserved simulator does not match the offered architecture." : "The reserved emulator does not match the offered architecture or selectors.");
91
111
  placement = {
92
112
  ...placement,
93
113
  device
@@ -97,13 +117,26 @@ async function placeHostedIos(target, { root, slot, bundle, handoff, bundleId, s
97
117
  const gateway = await metro(root, session.id, host.credential);
98
118
  await call(host, "device-host.metro.open", {
99
119
  session: session.id,
100
- ...gateway
120
+ ...gateway,
121
+ ...platform === "android" ? { clientMetroPort: metroPort } : {}
101
122
  });
102
123
  } else {
103
124
  await call(host, "device-host.metro.close", { session: session.id });
104
125
  await closeHostedMetro(root, session.id);
105
126
  }
106
- const { files, content } = bundleManifest(bundle);
127
+ const { files, content } = platform === "ios" ? bundleManifest(bundle) : (() => {
128
+ const apk = readFileSync(bundle);
129
+ const digest = sha256(apk);
130
+ return {
131
+ files: [{
132
+ path: "App.apk",
133
+ kind: "file",
134
+ size: apk.length,
135
+ sha256: digest
136
+ }],
137
+ content: /* @__PURE__ */ new Map([[digest, () => apk]])
138
+ };
139
+ })();
107
140
  const manifest = Buffer.from(JSON.stringify(files));
108
141
  content.set(sha256(manifest), () => manifest);
109
142
  const ids = {
@@ -120,11 +153,11 @@ async function placeHostedIos(target, { root, slot, bundle, handoff, bundleId, s
120
153
  size: manifest.length
121
154
  }
122
155
  };
123
- note(`Delivering ${files.length} files to ${host.machine} (${device.runtime}, ${device.architecture})`);
156
+ note(`Delivering ${files.length} files to ${host.machine} (${"runtime" in device ? device.runtime : device.systemImage}, ${device.architecture})`);
124
157
  await upload(host, ids, (await call(host, "device-host.app.offer", offer)).missing, content);
125
158
  let missing = (await call(host, "device-host.app.offer", offer)).missing;
126
159
  if (handoff && handoff.nodeId === host.credential.nodeId && Array.isArray(missing) && missing.length) {
127
- if (!host.connection.supports("hosted-ios-data")) note(`${host.machine} needs a newer stim-server for iOS build handoff; uploading the app instead`);
160
+ if (!host.connection.supports(platform === "android" ? "hosted-android-data" : "hosted-ios-data")) note(`${host.machine} needs a newer stim-server for ${platform === "ios" ? "iOS" : "Android"} build handoff; uploading the app instead`);
128
161
  else try {
129
162
  const taken = await call(host, "device-host.app.handoff", {
130
163
  ...ids,
@@ -160,7 +193,7 @@ async function placeHostedIos(target, { root, slot, bundle, handoff, bundleId, s
160
193
  if (typeof delivery.notice === "string") note(delivery.notice);
161
194
  placement = {
162
195
  ...placement,
163
- agent: agentAccess(iosAgentRemoteConfig(root, slot), host.credential, host.connection.supports("hosted-ios-agent") ? delivery.agent : void 0, note, "ios")
196
+ agent: agentAccess(platform === "ios" ? iosAgentRemoteConfig(root, slot) : androidAgentRemoteConfig(root, slot), host.credential, host.connection.supports(platform === "ios" ? "hosted-ios-agent" : "hosted-android-agent") ? delivery.agent : void 0, note, platform)
164
197
  };
165
198
  return {
166
199
  placement,
@@ -168,15 +201,17 @@ async function placeHostedIos(target, { root, slot, bundle, handoff, bundleId, s
168
201
  };
169
202
  } catch (error) {
170
203
  throw hostingRefusal(host.machine, error);
204
+ } finally {
205
+ if (platform === "android") host.connection.close();
171
206
  }
172
207
  }
173
- async function stopHostedIos(root, slot) {
208
+ async function stopHostedNative(root, slot, platform = "ios") {
174
209
  const failures = [];
175
- for (const [name, record] of Object.entries(hostedIosRecords(readWorkspaceState$1(root)))) {
210
+ for (const [name, record] of Object.entries(hostedNativeRecords(readWorkspaceState$1(root), platform))) {
176
211
  if (slot !== void 0 && name !== slot) continue;
177
- const placement = parseHostedIosPlacement(record);
212
+ const placement = parseHostedNativePlacement(record, platform);
178
213
  if (!placement) {
179
- failures.push(unreadableHostedIos(name));
214
+ failures.push(unreadableHostedNative(name, platform));
180
215
  continue;
181
216
  }
182
217
  if (placement.agent.driver === "agent-device") closeAgentConnection(placement.agent.remoteConfig);
@@ -184,14 +219,14 @@ async function stopHostedIos(root, slot) {
184
219
  try {
185
220
  host = await connectHost(placement.machine);
186
221
  try {
187
- await pullHostedIosLogs(root, name, placement, host, true);
222
+ await pullHostedNativeLogs(root, name, placement, host, true, platform);
188
223
  } catch (error) {
189
224
  process.stderr.write(`Could not copy final native logs from ${placement.machine}: ${error.message}\n`);
190
225
  }
191
- const stopped = await settle(host, hostedSession(host, await call(host, "device-host.stop", { session: placement.session }), "ios"), ["stopping"], SESSION_TIMEOUT_MS, "ios");
226
+ const stopped = await settle(host, hostedSession(host, await call(host, "device-host.stop", { session: placement.session }), platform), ["stopping"], SESSION_TIMEOUT_MS, platform);
192
227
  if (stopped.state !== "stopped") throw unknownSession(host, stopped);
193
228
  try {
194
- await pullHostedIosLogs(root, name, placement, host, true);
229
+ await pullHostedNativeLogs(root, name, placement, host, true, platform);
195
230
  } catch (error) {
196
231
  process.stderr.write(`Could not copy the host's final native logs from ${placement.machine}: ${error.message}\n`);
197
232
  }
@@ -200,7 +235,7 @@ async function stopHostedIos(root, slot) {
200
235
  const hostCode = error instanceof Error && "hostCode" in error ? error.hostCode : code;
201
236
  if (hostCode !== "unknown-session" && hostCode !== "forbidden") {
202
237
  const remedy = code === "closed" || code === "timeout" ? "rerun stim stop when that machine answers." : code === "STIM_BAD_ARG" || code === "STIM_HOSTING_REFUSED" ? "run stim doctor, restore hosting access, then rerun stim stop." : "run stim stop to reconcile the session.";
203
- failures.push(`Could not stop the iOS simulator on ${placement.machine}: ${(error instanceof Error ? error.message : String(error)).replace(/\.+$/, "")}. The placement is kept; ${remedy}`);
238
+ failures.push(`Could not stop the ${platform === "ios" ? "iOS simulator" : "Android emulator"} on ${placement.machine}: ${(error instanceof Error ? error.message : String(error)).replace(/\.+$/, "")}. The placement is kept; ${remedy}`);
204
239
  continue;
205
240
  }
206
241
  } finally {
@@ -208,8 +243,8 @@ async function stopHostedIos(root, slot) {
208
243
  }
209
244
  try {
210
245
  await closeHostedMetro(root, placement.session);
211
- rmSync(iosAgentRemoteConfig(root, name), { force: true });
212
- writeHostedIos(root, name, null);
246
+ rmSync(platform === "ios" ? iosAgentRemoteConfig(root, name) : androidAgentRemoteConfig(root, name), { force: true });
247
+ writeHostedNative(root, name, null, platform);
213
248
  } catch (error) {
214
249
  failures.push(`${placement.machine}: ${error.message}. The placement is kept; rerun stim stop.`);
215
250
  }
@@ -217,4 +252,4 @@ async function stopHostedIos(root, slot) {
217
252
  if (failures.length) throw new Error(failures.join("\n"));
218
253
  }
219
254
  //#endregion
220
- export { prepareHostedIos as n, stopHostedIos as r, placeHostedIos as t };
255
+ export { prepareHostedNative as n, stopHostedNative as r, placeHostedNative as t };