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
@@ -0,0 +1,89 @@
1
+ import { a as updateWorkspaceState, r as readWorkspaceState$1 } from "./workspace-state-DqcCbpE7.mjs";
2
+ import { HOSTED_METRO_REQUESTS_KEY, hostedMetroRequests, hostedNativeRecords, isJsonObject, parseHostedNativePlacement, unreadableHostedNative } from "@stim-cli/core/state";
3
+ //#region src/device-host/ios-state.ts
4
+ function readHostedNative(root, platform, slot) {
5
+ const state = readWorkspaceState$1(root);
6
+ if (slot !== void 0) {
7
+ const record = hostedNativeRecords(state, platform)[slot];
8
+ if (record !== void 0 && !parseHostedNativePlacement(record, platform)) throw Object.assign(new Error(unreadableHostedNative(slot, platform)), {
9
+ code: "STIM_HOSTING_REFUSED",
10
+ remedy: `Restore that slot's recorded machine and session from the host, then run stim stop${slot === "default" ? "" : ` --slot ${slot}`} to reconcile it.`
11
+ });
12
+ }
13
+ const placements = Object.fromEntries(Object.entries(hostedNativeRecords(state, platform)).flatMap(([name, record]) => {
14
+ const placement = parseHostedNativePlacement(record, platform);
15
+ return placement ? [[name, placement]] : [];
16
+ }));
17
+ return slot === void 0 ? placements : placements[slot] ? { [slot]: placements[slot] } : {};
18
+ }
19
+ function writeDevicePlacement(root, slot, platform, placement) {
20
+ if (!placement) return;
21
+ updateWorkspaceState(root, (state) => {
22
+ const slots = isJsonObject(state.deviceSlots) ? state.deviceSlots : {};
23
+ const saved = isJsonObject(slots[slot]) ? slots[slot] : {};
24
+ const current = slot === "default" ? state[platform] : saved[platform];
25
+ const record = {
26
+ ...isJsonObject(current) ? current : {},
27
+ devicePlacement: placement
28
+ };
29
+ if (slot === "default") return {
30
+ ...state,
31
+ [platform]: record
32
+ };
33
+ return {
34
+ ...state,
35
+ deviceSlots: {
36
+ ...slots,
37
+ [slot]: {
38
+ ...saved,
39
+ [platform]: record
40
+ }
41
+ }
42
+ };
43
+ });
44
+ }
45
+ function writeHostedNative(root, slot, host, platform) {
46
+ updateWorkspaceState(root, (state) => {
47
+ const slots = isJsonObject(state.deviceSlots) ? state.deviceSlots : {};
48
+ const current = slot === "default" ? state[platform] : isJsonObject(slots[slot]) ? slots[slot][platform] : void 0;
49
+ const record = isJsonObject(current) ? { ...current } : {};
50
+ const previous = parseHostedNativePlacement(record.host, platform);
51
+ if (previous && previous.session !== host?.session) {
52
+ const requests = { ...hostedMetroRequests(state) };
53
+ delete requests[previous.session];
54
+ state = {
55
+ ...state,
56
+ [HOSTED_METRO_REQUESTS_KEY]: requests
57
+ };
58
+ }
59
+ if (host) record.host = host;
60
+ else delete record.host;
61
+ return slot === "default" ? {
62
+ ...state,
63
+ [platform]: record
64
+ } : {
65
+ ...state,
66
+ deviceSlots: {
67
+ ...slots,
68
+ [slot]: {
69
+ ...isJsonObject(slots[slot]) ? slots[slot] : {},
70
+ [platform]: record
71
+ }
72
+ }
73
+ };
74
+ });
75
+ }
76
+ function readHostedIos(root, slot) {
77
+ return readHostedNative(root, "ios", slot);
78
+ }
79
+ function writeHostedIos(root, slot, host) {
80
+ writeHostedNative(root, slot, host, "ios");
81
+ }
82
+ function readHostedAndroid(root, slot) {
83
+ return readHostedNative(root, "android", slot);
84
+ }
85
+ function writeHostedAndroid(root, slot, host) {
86
+ writeHostedNative(root, slot, host, "android");
87
+ }
88
+ //#endregion
89
+ export { writeHostedAndroid as a, writeDevicePlacement as i, readHostedIos as n, writeHostedIos as o, readHostedNative as r, writeHostedNative as s, readHostedAndroid as t };
@@ -1,8 +1,8 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
- import { s as isAppLaunchError } from "./command-output-DnJMdEY-.mjs";
2
+ import { s as isAppLaunchError } from "./command-output-CDAIFD-8.mjs";
3
3
  import { o as readNdjsonGenerations } from "./ndjson-PclOIwVY.mjs";
4
4
  import { t as DEV_MENU_LAUNCH_ARGS } from "./ios-device-Ee7sDXfp.mjs";
5
- import { f as deviceShellArg, n as ANDROID_DISABLE_AUTO_LAUNCH_EXTRA, s as androidAppProcess } from "./app-install-CKWdbXIX.mjs";
5
+ import { f as deviceShellArg, n as ANDROID_DISABLE_AUTO_LAUNCH_EXTRA, s as androidAppProcess } from "./app-install-VPkI5WEg.mjs";
6
6
  import { join } from "node:path";
7
7
  //#region src/engine/app-readiness.ts
8
8
  const APP_READINESS_TIMEOUT_MS = 3e4;
@@ -1,15 +1,15 @@
1
1
  import { H as projectDeviceSlots, V as parseDeviceSlotOption, _ as loadConfig$1, g as isPathPrefix } from "./config-D0DbpZtM.mjs";
2
2
  import { t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
3
- import { f as refuseNoProject, l as phaseLine, v as launchErrorPreview } from "./command-output-DnJMdEY-.mjs";
4
- import { a as findCommandWorkspace } from "./project-Du91Ilvc.mjs";
5
- import { r as readWorkspaceState$1, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
6
- import { n as agentDeviceLiveness, u as readAgentDeviceRecords } from "./activity-BxwdD81S.mjs";
3
+ import { f as refuseNoProject, l as phaseLine, v as launchErrorPreview } from "./command-output-CDAIFD-8.mjs";
4
+ import { o as findCommandWorkspace } from "./project-D47Rh0ly.mjs";
5
+ import { r as readWorkspaceState$1, x as workspaceLogsDir$1 } from "./workspace-state-DqcCbpE7.mjs";
6
+ import { n as agentDeviceLiveness, u as readAgentDeviceRecords } from "./activity-IRvJY_ru.mjs";
7
7
  import { n as SOURCES, t as LEVELS } from "./ndjson-PclOIwVY.mjs";
8
- import { o as connectHost } from "./hosted-client-a-WmqE2L.mjs";
9
- import { n as pullHostedMacosLogs, t as pullHostedIosLogs } from "./hosted-logs-D5g041Q3.mjs";
10
- import "./hosted-macos-BSZyIb3y.mjs";
11
- import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-B9L4Ae0x.mjs";
12
- import { t as readHostedIos } from "./ios-state-DS5UpYTH.mjs";
8
+ import { o as connectHost } from "./hosted-client-wk1W_CNW.mjs";
9
+ import { n as pullHostedNativeLogs, t as pullHostedMacosLogs } from "./hosted-logs-DqYmWtXc.mjs";
10
+ import "./hosted-macos-dlHXEeF5.mjs";
11
+ import { n as readHostedIos, t as readHostedAndroid } from "./ios-state-8L3lgum6.mjs";
12
+ import { i as captureWorkspaceCrashes, t as errorDiagnostics } from "./error-diagnostics-C-NMnhrk.mjs";
13
13
  import { t as watchStdoutReader } from "./stdout-reader-C8yjre1x.mjs";
14
14
  import { realpathSync } from "node:fs";
15
15
  import { dirname, join, relative, resolve, sep } from "node:path";
@@ -17,8 +17,15 @@ import { agentDeviceStateDirs, buildCriteria, compileGrep, createAgentActionRead
17
17
  import { homedir } from "node:os";
18
18
  import chalk from "chalk";
19
19
  //#region src/devices/agent-actions.ts
20
- function workspaceAgentTargets(project) {
20
+ function workspaceAgentTargets(project, macos) {
21
21
  const targets = [];
22
+ if (macos?.app && macos.bundleId) targets.push({
23
+ platform: "macos",
24
+ id: macos.launchId,
25
+ slot: "default",
26
+ bundleId: macos.bundleId,
27
+ launchedAt: macos.app.startedAtMicros / 1e3
28
+ });
22
29
  let slots;
23
30
  try {
24
31
  slots = projectDeviceSlots(project);
@@ -49,7 +56,6 @@ function canonicalDir(path) {
49
56
  }
50
57
  }
51
58
  function createAgentActionReader$1({ targets, sinceTs, home = homedir(), now, startOf = process_identity_exports.inspectProcessStart }) {
52
- const byId = new Map(targets.map((target) => [target.id, target]));
53
59
  let claimed;
54
60
  const read = createAgentActionReader({
55
61
  sessionsDirs: () => agentDeviceStateDirs(home).map((root) => join(root, "sessions")),
@@ -57,9 +63,10 @@ function createAgentActionReader$1({ targets, sinceTs, home = homedir(), now, st
57
63
  sinceTs,
58
64
  now,
59
65
  claimedDevice: (session, sessionsDir) => {
66
+ const byId = claimed ? /* @__PURE__ */ new Map() : new Map((typeof targets === "function" ? targets() : targets).map((target) => [target.id, target]));
60
67
  claimed ??= new Map(readAgentDeviceRecords(home).filter((record) => record.kind === "claim" && record.session && record.deviceId).filter((record) => {
61
68
  const target = byId.get(record.deviceId);
62
- return target?.name === void 0 || target.name === record.deviceName;
69
+ return target?.platform !== "macos" && (target?.name === void 0 || target.name === record.deviceName);
63
70
  }).filter((record) => agentDeviceLiveness(record, startOf) === "live").map((record) => [`${record.stateDir ? canonicalDir(record.stateDir) : ""}\0${record.session}`, record.deviceId]));
64
71
  return claimed.get(`${canonicalDir(dirname(sessionsDir))}\0${session}`) ?? claimed.get(`\0${session}`) ?? null;
65
72
  }
@@ -76,11 +83,11 @@ function warning(machine, error) {
76
83
  const hint = message.includes("refused device-host.logs.query") ? ` If ${machine} runs an older stim-server, update it.` : "";
77
84
  return phaseLine("device", `Could not read the app's logs from ${machine}: ${message}. Showing the logs already copied here.${hint}`);
78
85
  }
79
- async function syncHostedMacosLogs(root, placement, slot, warnings) {
86
+ async function syncHostedMacosLogs(root, placement, slot, warnings, platform = "ios") {
80
87
  let host;
81
88
  try {
82
89
  host = await connectHost(placement.machine);
83
- if (slot !== void 0) await pullHostedIosLogs(root, slot, placement, host);
90
+ if (slot !== void 0) await pullHostedNativeLogs(root, slot, placement, host, false, platform);
84
91
  else await pullHostedMacosLogs(root, placement, host);
85
92
  return true;
86
93
  } catch (error) {
@@ -93,7 +100,7 @@ async function syncHostedMacosLogs(root, placement, slot, warnings) {
93
100
  host?.connection.close();
94
101
  }
95
102
  }
96
- function followHostedMacosLogs(root, { failing = false, intervalMs = 500, retryMs = 5e3, slot } = {}) {
103
+ function followHostedMacosLogs(root, { failing = false, intervalMs = 500, retryMs = 5e3, slot, platform = "ios" } = {}) {
97
104
  let host;
98
105
  let retryAt = 0;
99
106
  let stopped = false;
@@ -102,20 +109,20 @@ function followHostedMacosLogs(root, { failing = false, intervalMs = 500, retryM
102
109
  if (running || stopped || Date.now() < retryAt) return;
103
110
  running = true;
104
111
  try {
105
- const placement = slot !== void 0 ? readHostedIos(root)[slot] : readMacosRecord(root)?.host;
112
+ const placement = slot !== void 0 ? (platform === "ios" ? readHostedIos(root) : readHostedAndroid(root))[slot] : readMacosRecord(root)?.host;
106
113
  if (!placement) {
107
114
  host?.connection.close();
108
115
  host = void 0;
109
116
  return;
110
117
  }
111
118
  host ??= await connectHost(placement.machine);
112
- if (slot !== void 0) await pullHostedIosLogs(root, slot, placement, host);
119
+ if (slot !== void 0) await pullHostedNativeLogs(root, slot, placement, host, false, platform);
113
120
  else await pullHostedMacosLogs(root, placement, host);
114
121
  failing = false;
115
122
  } catch (error) {
116
123
  host?.connection.close();
117
124
  host = void 0;
118
- if (!failing) process.stderr.write(`${warning((slot !== void 0 ? readHostedIos(root)[slot] : readMacosRecord(root)?.host)?.machine ?? "the host", error)}\n`);
125
+ if (!failing) process.stderr.write(`${warning((slot !== void 0 ? (platform === "ios" ? readHostedIos(root) : readHostedAndroid(root))[slot] : readMacosRecord(root)?.host)?.machine ?? "the host", error)}\n`);
119
126
  failing = true;
120
127
  retryAt = Date.now() + retryMs;
121
128
  } finally {
@@ -132,17 +139,22 @@ function followHostedMacosLogs(root, { failing = false, intervalMs = 500, retryM
132
139
  async function syncHostedLogs(root) {
133
140
  const macos = readMacosRecord(root)?.host;
134
141
  const warnings = /* @__PURE__ */ new Set();
135
- return (await Promise.all([...macos ? [syncHostedMacosLogs(root, macos)] : [], ...Object.entries(readHostedIos(root)).map(([slot, placement]) => syncHostedMacosLogs(root, placement, slot, warnings))])).every(Boolean);
142
+ return (await Promise.all([
143
+ ...macos ? [syncHostedMacosLogs(root, macos)] : [],
144
+ ...Object.entries(readHostedIos(root)).map(([slot, placement]) => syncHostedMacosLogs(root, placement, slot, warnings)),
145
+ ...Object.entries(readHostedAndroid(root)).map(([slot, placement]) => syncHostedMacosLogs(root, placement, slot, warnings, "android"))
146
+ ])).every(Boolean);
136
147
  }
137
148
  function followHostedLogs(root, failing) {
138
149
  const stops = /* @__PURE__ */ new Map();
139
150
  const refresh = () => {
140
151
  if (readMacosRecord(root)?.host && !stops.has("macos")) stops.set("macos", followHostedMacosLogs(root, { failing }));
141
- for (const slot of Object.keys(readHostedIos(root))) {
142
- const key = `ios:${slot}`;
152
+ for (const platform of ["ios", "android"]) for (const slot of Object.keys(platform === "ios" ? readHostedIos(root) : readHostedAndroid(root))) {
153
+ const key = `${platform}:${slot}`;
143
154
  if (!stops.has(key)) stops.set(key, followHostedMacosLogs(root, {
144
155
  failing,
145
- slot
156
+ slot,
157
+ platform
146
158
  }));
147
159
  }
148
160
  };
@@ -301,8 +313,9 @@ function logsCommand(program) {
301
313
  const offsets = opts.follow ? fileSizes(dir) : null;
302
314
  const workspaceTimeline = opts.follow ? readLogRecords(dir) : captured;
303
315
  const agentSince = workspaceTimeline[0]?.ts ?? (opts.follow ? Date.now() : void 0);
316
+ const project = loadConfig$1()?.projects?.[root];
304
317
  const readAgent = (sources ? sources.includes("agent") : !opts.errors) && agentSince !== void 0 ? createAgentActionReader$1({
305
- targets: workspaceAgentTargets(loadConfig$1()?.projects?.[root]),
318
+ targets: () => workspaceAgentTargets(project, readMacosRecord(root)),
306
319
  sinceTs: agentSince
307
320
  }) : null;
308
321
  const timeline = readAgent ? sortByTs([...workspaceTimeline, ...readAgent()]) : workspaceTimeline;
@@ -1,12 +1,13 @@
1
1
  import { t as getExecutor } from "./exec-CaErVv7t.mjs";
2
2
  import { _ as loadConfig$1, d as getConfigDir } from "./config-D0DbpZtM.mjs";
3
- import { w as withDirLock } from "./workspace-state-B8-aJ1_5.mjs";
4
- import { T as CCACHE_UNAVAILABLE, a as toolchainMismatches, l as COMPILATION_CACHE_UNAVAILABLE, s as manifestDigest } from "./toolchain-DkUflduf.mjs";
3
+ import { w as withDirLock } from "./workspace-state-DqcCbpE7.mjs";
4
+ import { T as CCACHE_UNAVAILABLE, a as toolchainMismatches, l as COMPILATION_CACHE_UNAVAILABLE, s as manifestDigest } from "./toolchain-CNIBTIOv.mjs";
5
5
  import { createHash } from "node:crypto";
6
6
  import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync, writeSync } from "node:fs";
7
7
  import { basename, dirname, join, relative } from "node:path";
8
8
  import { InvalidArgumentError } from "commander";
9
- import { OFFLOAD_MODES, buildMachinesFile, buildMachinesLock, endpoint, findPeer as findPeer$1, isJsonObject, parseMachine, parseMachine as parseMachine$1, pinnedEndpoint, readBuildMachines, saturation } from "@stim-cli/core/state";
9
+ import { OFFLOAD_MODES, acceptsShape, buildMachinesFile, buildMachinesLock, deviceHostMachinesClaims, deviceHostMachinesFile, deviceHostMachinesLock, endpoint, findPeer as findPeer$1, isJsonObject, parseMachine, parseMachine as parseMachine$1, pinnedEndpoint, readBuildMachines, readDeviceHostMachines, saturation, settingDefinition } from "@stim-cli/core/state";
10
+ import { releaseClaim, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
10
11
  import { hostname } from "node:os";
11
12
  import { WebSocket } from "ws";
12
13
  //#region src/offload/access-ticket.ts
@@ -95,11 +96,11 @@ function pinnedEndpoint$1(credential, status = tailscaleStatus) {
95
96
  //#endregion
96
97
  //#region src/offload/build-machines.ts
97
98
  /** The build machines named in `offload.machines` that this Mac asked for build access, in that order. */
98
- function pairedMachines(entries = configuredMachines()) {
99
+ function pairedMachines(entries = configuredMachines$1()) {
99
100
  const credentials = readBuildMachines();
100
101
  return entries.flatMap((entry) => credentials.filter((each) => each.machine === entry));
101
102
  }
102
- function configuredMachines() {
103
+ function configuredMachines$1() {
103
104
  const machines = loadConfig$1()?.offload?.machines;
104
105
  return Array.isArray(machines) ? machines.filter((entry) => typeof entry === "string") : [];
105
106
  }
@@ -120,7 +121,7 @@ function updateCredentials(change) {
120
121
  }
121
122
  }, { ensureParent: () => mkdirSync(dir, { recursive: true }) });
122
123
  }
123
- function note(title, detail, fix = null) {
124
+ function note$1(title, detail, fix = null) {
124
125
  return {
125
126
  code: "build-machine",
126
127
  level: "note",
@@ -129,7 +130,7 @@ function note(title, detail, fix = null) {
129
130
  fix
130
131
  };
131
132
  }
132
- const approval = (entry, deviceId) => `A person on ${entry} approves it with \`stim-server devices grant ${deviceId} --build\`.`;
133
+ const approval$1 = (entry, deviceId) => `A person on ${entry} approves it with \`stim-server devices grant ${deviceId} --build\`.`;
133
134
  const PROBLEM_TITLES = {
134
135
  unreachable: "does not answer a build offer",
135
136
  checkout: "cannot build an app outside a git checkout",
@@ -195,7 +196,7 @@ async function requestAccess(entry, peer, port, deviceName, io) {
195
196
  state: "unreachable",
196
197
  dnsName: peer.dnsName
197
198
  },
198
- finding: note(`Could not ask ${entry} for build access`, reason)
199
+ finding: note$1(`Could not ask ${entry} for build access`, reason)
199
200
  };
200
201
  }
201
202
  const credential = {
@@ -217,7 +218,7 @@ async function requestAccess(entry, peer, port, deviceName, io) {
217
218
  deviceId: credential.deviceId,
218
219
  requestedAt: credential.requestedAt
219
220
  },
220
- finding: note(`Asked ${entry} for build access`, `The request lapses at ${reply.result.approval.expiresAt}.`, approval(entry, credential.deviceId))
221
+ finding: note$1(`Asked ${entry} for build access`, `The request lapses at ${reply.result.approval.expiresAt}.`, approval$1(entry, credential.deviceId))
221
222
  };
222
223
  }
223
224
  async function inspectMachine(entry, status, deviceName, fix, io, check) {
@@ -227,7 +228,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
227
228
  machine: entry,
228
229
  state: "invalid"
229
230
  },
230
- finding: note(`Build machine ${entry} is not a tailnet name`, "Expected `name` or `name:port`.")
231
+ finding: note$1(`Build machine ${entry} is not a tailnet name`, "Expected `name` or `name:port`.")
231
232
  };
232
233
  const peer = findPeer$1(status, parsed.name);
233
234
  if (peer === "missing" || peer === "ambiguous") {
@@ -237,7 +238,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
237
238
  machine: entry,
238
239
  state: "not-on-tailnet"
239
240
  },
240
- finding: note(`Build machine ${entry} is not on this tailnet`, detail)
241
+ finding: note$1(`Build machine ${entry} is not on this tailnet`, detail)
241
242
  };
242
243
  }
243
244
  const credential = readBuildMachines().find((each) => each.machine === entry);
@@ -252,7 +253,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
252
253
  deviceId: credential.deviceId,
253
254
  requestedAt: credential.requestedAt
254
255
  },
255
- finding: note(`Build machine ${entry} is a different tailnet node`, `This Mac paired with node ${credential.nodeId}, but ${peer.dnsName} is now node ${peer.nodeId}. Stim does not connect to it.`, `If that Mac was replaced, remove ${entry} from offload.machines, run \`stim doctor --fix\` to forget the old pairing, then add it back and run \`stim doctor --fix\` again.`)
256
+ finding: note$1(`Build machine ${entry} is a different tailnet node`, `This Mac paired with node ${credential.nodeId}, but ${peer.dnsName} is now node ${peer.nodeId}. Stim does not connect to it.`, `If that Mac was replaced, remove ${entry} from offload.machines, run \`stim doctor --fix\` to forget the old pairing, then add it back and run \`stim doctor --fix\` again.`)
256
257
  };
257
258
  if (!credential) {
258
259
  if (fix) return requestAccess(entry, peer, parsed.port, deviceName, io);
@@ -261,7 +262,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
261
262
  ...known,
262
263
  state: "not-asked"
263
264
  },
264
- finding: note(`Build machine ${entry} has not approved this Mac`, "This Mac has not asked it for build access.", "Run `stim doctor --fix` to ask.")
265
+ finding: note$1(`Build machine ${entry} has not approved this Mac`, "This Mac has not asked it for build access.", "Run `stim doctor --fix` to ask.")
265
266
  };
266
267
  }
267
268
  const paired = {
@@ -311,7 +312,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
311
312
  ...paired,
312
313
  state: "pending"
313
314
  },
314
- finding: note(`Build machine ${entry} has not approved this Mac yet`, `Requested at ${credential.requestedAt}.`, approval(entry, credential.deviceId))
315
+ finding: note$1(`Build machine ${entry} has not approved this Mac yet`, `Requested at ${credential.requestedAt}.`, approval$1(entry, credential.deviceId))
315
316
  };
316
317
  }
317
318
  if ("error" in reply && reply.error.code === "unauthorized") {
@@ -321,7 +322,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
321
322
  ...paired,
322
323
  state: "revoked"
323
324
  },
324
- finding: note(`Build machine ${entry} no longer accepts this Mac`, `${reply.error.message} It was revoked, or the request lapsed before approval.`, "Run `stim doctor --fix` to ask again.")
325
+ finding: note$1(`Build machine ${entry} no longer accepts this Mac`, `${reply.error.message} It was revoked, or the request lapsed before approval.`, "Run `stim doctor --fix` to ask again.")
325
326
  };
326
327
  }
327
328
  const reason = "failed" in reply ? reply.failed : "error" in reply ? reply.error.message : "unexpected reply";
@@ -330,7 +331,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
330
331
  ...paired,
331
332
  state: "unreachable"
332
333
  },
333
- finding: note(`Could not reach build machine ${entry}`, reason, `Check that stim-server runs on ${entry}. To keep it running there, run \`stim-server service install --serve\` on ${entry}.`)
334
+ finding: note$1(`Could not reach build machine ${entry}`, reason, `Check that stim-server runs on ${entry}. To keep it running there, run \`stim-server service install --serve\` on ${entry}.`)
334
335
  };
335
336
  }
336
337
  /**
@@ -340,7 +341,7 @@ async function inspectMachine(entry, status, deviceName, fix, io, check) {
340
341
  * pinned node forgot this Mac or, with `STIM_ACCESS_TICKET` set, when a pending request carries another ticket, and forgets pairings of machines no longer named. With `check`, each approved
341
342
  * machine is asked for one build offer and reports every reason it would not take this project's build.
342
343
  */
343
- async function inspectBuildMachines({ fix, check = null }, io = realIo, entries = configuredMachines()) {
344
+ async function inspectBuildMachines({ fix, check = null }, io = realIo, entries = configuredMachines$1()) {
344
345
  const unnamed = (each) => !entries.includes(each.machine);
345
346
  if (fix && readBuildMachines().some(unnamed)) updateCredentials((credentials) => credentials.filter((each) => !unnamed(each)));
346
347
  if (entries.length === 0) return {
@@ -349,7 +350,7 @@ async function inspectBuildMachines({ fix, check = null }, io = realIo, entries
349
350
  };
350
351
  const status = io.status();
351
352
  if (!isJsonObject(status)) return {
352
- findings: [note("Build machines are unreachable", "offload.machines names build machines, but Tailscale is not running.")],
353
+ findings: [note$1("Build machines are unreachable", "offload.machines names build machines, but Tailscale is not running.")],
353
354
  machines: entries.map((machine) => ({
354
355
  machine,
355
356
  state: "tailscale-off"
@@ -1240,7 +1241,7 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
1240
1241
  ok: true,
1241
1242
  machine,
1242
1243
  artifactPath,
1243
- ...request.platform === "macos" && typeof declared.handoff === "string" && /^[0-9a-f]{64}$/.test(declared.handoff) ? { handoff: {
1244
+ ...typeof declared.handoff === "string" && /^[0-9a-f]{64}$/.test(declared.handoff) ? { handoff: {
1244
1245
  nodeId: choice.credential.nodeId,
1245
1246
  token: declared.handoff,
1246
1247
  sha256: digest
@@ -1365,4 +1366,261 @@ function offloadCheck(projectRoot, targets) {
1365
1366
  };
1366
1367
  }
1367
1368
  //#endregion
1368
- export { readHostPermissions as S, findPeer$1 as _, offloadBuild as a, realIo as b, placementLoad as c, OffloadRefusal as d, namedBuildMachine as f, endpoint as g, inspectBuildMachines as h, closeOffload as i, remotePhaseText as l, resolveBuildPlacement as m, buildPlacementCandidates as n, offloadCheck as o, parseBuildMachineOption as p, chooseBuildMachine as r, offloadPlacement as s, BuildConnection as t, simulatorRuntime as u, parseMachine$1 as v, readAccessTicket as x, pinnedEndpoint$1 as y };
1369
+ //#region src/device-host/machines.ts
1370
+ function note(title, detail, fix = null) {
1371
+ return {
1372
+ code: "device-host-machine",
1373
+ level: "note",
1374
+ title,
1375
+ detail,
1376
+ fix
1377
+ };
1378
+ }
1379
+ const approval = (machine, id) => `A person on ${machine} approves it with \`stim-server devices grant ${id} --device-host\`.`;
1380
+ function configuredMachines() {
1381
+ const hosting = loadConfig$1()?.hosting;
1382
+ if (hosting !== void 0 && !isJsonObject(hosting)) return null;
1383
+ const machines = hosting?.machines;
1384
+ if (machines === void 0) return [];
1385
+ return acceptsShape(settingDefinition("hosting.machines"), machines) ? machines : null;
1386
+ }
1387
+ function store(machines) {
1388
+ withDirLock(deviceHostMachinesLock(), () => {
1389
+ const file = deviceHostMachinesFile();
1390
+ const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
1391
+ try {
1392
+ writeFileSync(tmp, `${JSON.stringify({
1393
+ version: 1,
1394
+ machines
1395
+ }, null, 2)}\n`, { mode: 384 });
1396
+ renameSync(tmp, file);
1397
+ } finally {
1398
+ rmSync(tmp, { force: true });
1399
+ }
1400
+ }, { ensureParent: () => mkdirSync(getConfigDir(), { recursive: true }) });
1401
+ }
1402
+ async function request(machine, peer, port, deviceName, io) {
1403
+ const ticket = readAccessTicket();
1404
+ const reply = await io.hello(endpoint(peer, port), {
1405
+ request: "device-host",
1406
+ deviceName,
1407
+ ...ticket ? { setupTicket: ticket.ticket } : {}
1408
+ });
1409
+ if (!("result" in reply) || reply.result?.approval?.state !== "pending" || typeof reply.result.deviceToken !== "string" || !reply.result.deviceToken || typeof reply.result.device?.id !== "string" || !reply.result.device.id || typeof reply.result.approval.expiresAt !== "string" || !reply.result.approval.expiresAt) return {
1410
+ report: {
1411
+ machine,
1412
+ dnsName: peer.dnsName,
1413
+ state: "unreachable"
1414
+ },
1415
+ finding: note(`Could not ask ${machine} for hosting access`, "The worker did not return a pending request.")
1416
+ };
1417
+ const credential = {
1418
+ machine,
1419
+ nodeId: peer.nodeId,
1420
+ dnsName: peer.dnsName,
1421
+ deviceId: reply.result.device.id,
1422
+ deviceToken: reply.result.deviceToken,
1423
+ state: "pending",
1424
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
1425
+ ...ticket ? { ticketHash: ticket.ticketHash } : {}
1426
+ };
1427
+ return {
1428
+ report: {
1429
+ machine,
1430
+ dnsName: peer.dnsName,
1431
+ state: "pending",
1432
+ deviceId: credential.deviceId,
1433
+ requestedAt: credential.requestedAt
1434
+ },
1435
+ finding: note(`Asked ${machine} for hosting access`, `The request lapses at ${reply.result.approval.expiresAt}.`, approval(machine, credential.deviceId)),
1436
+ credential
1437
+ };
1438
+ }
1439
+ /** Reports separately approved hosting machines. Only --fix asks or forgets credentials; every token stays pinned. */
1440
+ async function inspectDeviceHostMachines({ fix }, io = realIo, entries = configuredMachines()) {
1441
+ if (entries === null) return {
1442
+ findings: [note("Invalid hosting.machines setting", "Use a hosting object with machines as an array of tailnet names. Existing hosting credentials and pinned nodes are preserved.", "Run `stim guide settings` and correct the setting before running doctor again.")],
1443
+ machines: []
1444
+ };
1445
+ const unavailable = (state, finding) => ({
1446
+ findings: [finding],
1447
+ machines: entries.map((machine) => ({
1448
+ machine,
1449
+ state
1450
+ }))
1451
+ });
1452
+ const unreadable = () => unavailable("credentials-unavailable", note("Hosting credentials are unreadable", `Stim preserves ${deviceHostMachinesFile()} and its pinned nodes.`, "Restore the existing file from a trusted backup or restore read access before running doctor again."));
1453
+ if (!entries.length) {
1454
+ if (!fix) return {
1455
+ findings: [],
1456
+ machines: []
1457
+ };
1458
+ try {
1459
+ if (!readDeviceHostMachines().length) return {
1460
+ findings: [],
1461
+ machines: []
1462
+ };
1463
+ } catch {
1464
+ return unreadable();
1465
+ }
1466
+ }
1467
+ const claim = tryAcquireClaim({
1468
+ root: deviceHostMachinesClaims(),
1469
+ mode: "exclusive",
1470
+ label: "hosting machine approval"
1471
+ });
1472
+ if (!claim.acquired) {
1473
+ releaseClaim(claim.pending);
1474
+ return unavailable("busy", note("Hosting machine approval is in use", "Another doctor run holds the hosting credential claim. Run doctor again after it finishes."));
1475
+ }
1476
+ try {
1477
+ let credentials;
1478
+ try {
1479
+ credentials = readDeviceHostMachines();
1480
+ } catch {
1481
+ return unreadable();
1482
+ }
1483
+ if (fix) {
1484
+ const kept = credentials.filter((each) => entries.includes(each.machine));
1485
+ if (kept.length !== credentials.length) {
1486
+ store(kept);
1487
+ credentials = kept;
1488
+ }
1489
+ }
1490
+ if (!entries.length) return {
1491
+ findings: [],
1492
+ machines: []
1493
+ };
1494
+ const status = io.status();
1495
+ if (!isJsonObject(status)) return unavailable("tailscale-off", note("Hosting machines are unreachable", "hosting.machines names hosting machines, but Tailscale is not running."));
1496
+ const self = isJsonObject(status.Self) ? status.Self : {};
1497
+ const deviceName = (typeof self.HostName === "string" && self.HostName ? self.HostName : hostname()).replace(/[\p{Cc}\p{Cf}]/gu, "").trim().slice(0, 64) || "Mac";
1498
+ const inspected = {
1499
+ findings: [],
1500
+ machines: []
1501
+ };
1502
+ const namedNodes = /* @__PURE__ */ new Set();
1503
+ for (const machine of new Set(entries)) {
1504
+ const parsed = parseMachine$1(machine);
1505
+ if (!parsed) {
1506
+ inspected.machines.push({
1507
+ machine,
1508
+ state: "invalid"
1509
+ });
1510
+ inspected.findings.push(note(`Hosting machine ${machine} is not a tailnet name`, "Expected `name` or `name:port`."));
1511
+ continue;
1512
+ }
1513
+ const current = io.status();
1514
+ if (!isJsonObject(current)) {
1515
+ inspected.machines.push({
1516
+ machine,
1517
+ state: "tailscale-off"
1518
+ });
1519
+ inspected.findings.push(note(`Hosting machine ${machine} is unreachable`, "Tailscale stopped before this connection."));
1520
+ continue;
1521
+ }
1522
+ const peer = findPeer$1(current, parsed.name);
1523
+ if (peer === "missing" || peer === "ambiguous") {
1524
+ inspected.machines.push({
1525
+ machine,
1526
+ state: "not-on-tailnet"
1527
+ });
1528
+ inspected.findings.push(note(`Hosting machine ${machine} is not on this tailnet`, peer === "missing" ? "No peer has that name." : "Several peers match that name."));
1529
+ continue;
1530
+ }
1531
+ const nodeRoute = `${peer.nodeId}:${parsed.port}`;
1532
+ if (namedNodes.has(nodeRoute)) {
1533
+ inspected.machines.push({
1534
+ machine,
1535
+ dnsName: peer.dnsName,
1536
+ state: "invalid"
1537
+ });
1538
+ inspected.findings.push(note(`Hosting machine ${machine} repeats a named node`, "Name each tailnet node and serve port only once in hosting.machines."));
1539
+ continue;
1540
+ }
1541
+ namedNodes.add(nodeRoute);
1542
+ const credential = credentials.find((each) => each.machine === machine);
1543
+ const known = {
1544
+ machine,
1545
+ dnsName: peer.dnsName,
1546
+ ...credential ? {
1547
+ deviceId: credential.deviceId,
1548
+ requestedAt: credential.requestedAt
1549
+ } : {}
1550
+ };
1551
+ if (credential && credential.nodeId !== peer.nodeId) {
1552
+ inspected.machines.push({
1553
+ ...known,
1554
+ state: "node-changed"
1555
+ });
1556
+ inspected.findings.push(note(`Hosting machine ${machine} is a different tailnet node`, "Stim refuses to connect or send the saved token.", `If that Mac was replaced, remove ${machine} from hosting.machines, run \`stim doctor --fix\`, then add it back and run \`stim doctor --fix\` again to request approval on the new node.`));
1557
+ continue;
1558
+ }
1559
+ let ask = !credential && fix;
1560
+ if (credential) {
1561
+ const reply = await io.hello(endpoint(peer, parsed.port), { deviceToken: credential.deviceToken });
1562
+ if ("result" in reply && reply.result?.device?.id === credential.deviceId && Array.isArray(reply.result.capabilities) && reply.result.capabilities.includes("device-host")) {
1563
+ if (credential.state !== "approved") {
1564
+ credentials = credentials.map((each) => each.machine === machine ? {
1565
+ ...each,
1566
+ state: "approved"
1567
+ } : each);
1568
+ store(credentials);
1569
+ }
1570
+ const host = readHostPermissions(reply.result.host);
1571
+ if (host && (host.screenRecording === false || host.accessibility === false)) {
1572
+ const panes = [...host.screenRecording === false ? ["Screen & System Audio Recording (Screen Recording on macOS 14)"] : [], ...host.accessibility === false ? ["Device Control and Data Access (Accessibility on macOS 26 and earlier)"] : []];
1573
+ const detail = [...host.screenRecording === false ? ["Viewing hosted macOS apps needs Screen Recording."] : [], ...host.accessibility === false ? ["Controlling them needs the control permission."] : []];
1574
+ inspected.findings.push(note(`Hosting machine ${machine} needs ${panes.join(" and ")} for ${host.name}`, detail.join(" "), `On ${machine}, approve ${host.name} in System Settings > Privacy & Security, or run \`stim-server service install\` there again to show the requests.`));
1575
+ }
1576
+ inspected.machines.push({
1577
+ ...known,
1578
+ state: "approved",
1579
+ ...host ? { host } : {}
1580
+ });
1581
+ continue;
1582
+ }
1583
+ const pending = "error" in reply && reply.error?.code === "approval-pending";
1584
+ const revoked = "error" in reply && reply.error?.code === "unauthorized";
1585
+ const ticket = fix && pending && credential.state !== "approved" ? readAccessTicket() : void 0;
1586
+ ask = fix && (revoked || !!ticket && credential.ticketHash !== ticket.ticketHash);
1587
+ if (pending && !ask) {
1588
+ inspected.machines.push({
1589
+ ...known,
1590
+ state: "pending"
1591
+ });
1592
+ inspected.findings.push(note(`Hosting machine ${machine} has not approved this Mac yet`, `Requested at ${credential.requestedAt}.`, approval(machine, credential.deviceId)));
1593
+ continue;
1594
+ }
1595
+ if (!ask) {
1596
+ inspected.machines.push({
1597
+ ...known,
1598
+ state: revoked ? "revoked" : "unreachable"
1599
+ });
1600
+ inspected.findings.push(note(`Hosting machine ${machine} ${revoked ? "no longer accepts this Mac" : "did not confirm hosting access"}`, "The saved token and pinned node are preserved.", revoked ? "Run `stim doctor --fix` to ask again." : `Check stim-server and its tailnet serve route on ${machine}, then run doctor again.`));
1601
+ continue;
1602
+ }
1603
+ }
1604
+ if (ask) {
1605
+ const result = await request(machine, peer, parsed.port, deviceName, io);
1606
+ if (result.credential) {
1607
+ credentials = [...credentials.filter((each) => each.machine !== machine), result.credential];
1608
+ store(credentials);
1609
+ }
1610
+ inspected.machines.push(result.report);
1611
+ inspected.findings.push(result.finding);
1612
+ } else {
1613
+ inspected.machines.push({
1614
+ ...known,
1615
+ state: "not-asked"
1616
+ });
1617
+ inspected.findings.push(note(`Hosting machine ${machine} has not approved this Mac`, "This Mac has not asked for hosting access.", "Run `stim doctor --fix` to ask."));
1618
+ }
1619
+ }
1620
+ return inspected;
1621
+ } finally {
1622
+ releaseClaim(claim.acquired);
1623
+ }
1624
+ }
1625
+ //#endregion
1626
+ export { resolveBuildPlacement as _, chooseBuildMachine as a, pinnedEndpoint$1 as b, offloadCheck as c, remotePhaseText as d, simulatorRuntime as f, resolveBuildMachine as g, parseBuildMachineOption as h, buildPlacementCandidates as i, offloadPlacement as l, namedBuildMachine as m, inspectDeviceHostMachines as n, closeOffload as o, OffloadRefusal as p, BuildConnection as r, offloadBuild as s, configuredMachines as t, placementLoad as u, inspectBuildMachines as v, realIo as x, parseMachine$1 as y };