stim 1.10.1 → 1.12.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 (72) hide show
  1. package/dist/{build-progress-BXx-ZBe4.mjs → activity-CCZJs__P.mjs} +9 -182
  2. package/dist/{android-787HhP5w.mjs → android-75Sk2rIR.mjs} +81 -19
  3. package/dist/{android-Bu4mbDzc.mjs → android-B7X-8H7h.mjs} +426 -106
  4. package/dist/{android-DPov-fv2.mjs → android-BQiKJYK0.mjs} +1 -1
  5. package/dist/{android-cas-CITmIeWY.mjs → android-cas-DzLPXiEn.mjs} +7 -7
  6. package/dist/{app-install-Cfmqr3TQ.mjs → app-install-CsZi0SAT.mjs} +1 -1
  7. package/dist/{workspaces-wrH6dXfu.mjs → budget-DbzW5VTD.mjs} +651 -32
  8. package/dist/{dev-client-DExzPiOa.mjs → build-plan-CqntbR7b.mjs} +514 -38
  9. package/dist/build-progress-h60GPV27.mjs +179 -0
  10. package/dist/{cache-manifest-Pubfp8wV.mjs → cache-manifest-DWPF4RKJ.mjs} +1 -1
  11. package/dist/cache-manifest.mjs +1 -1
  12. package/dist/cli.mjs +16 -16
  13. package/dist/collector-run.mjs +4 -4
  14. package/dist/{command-output-D5z2_bcF.mjs → command-output-BWCj5Vh-.mjs} +1 -1
  15. package/dist/{config-BEq0Qw_a.mjs → config-0jCcMM0E.mjs} +10 -1
  16. package/dist/{dependency-state-r9UesA9a.mjs → dependency-state-DNN0yfx6.mjs} +1 -1
  17. package/dist/{deps-DP4eWqea.mjs → deps-CO-_7UOm.mjs} +2 -2
  18. package/dist/{device-Czh5kO8A.mjs → device-9r9xRYDa.mjs} +49 -11
  19. package/dist/{device-lease-CbEaIk7b.mjs → device-lease-B9LAXIUH.mjs} +55 -55
  20. package/dist/{device-pool-BBWLAr55.mjs → device-pool-BoezJd-c.mjs} +4 -4
  21. package/dist/{device-remote-powUHdbK.mjs → device-remote-C0_o-tJH.mjs} +254 -579
  22. package/dist/{doctor-CmqK1LcQ.mjs → doctor-LfDlbR-N.mjs} +20 -19
  23. package/dist/{error-diagnostics-xxWKNO3-.mjs → error-diagnostics-CCxjn31V.mjs} +7 -7
  24. package/dist/{gc-zCDpmdHU.mjs → gc-C4nQFcTX.mjs} +220 -282
  25. package/dist/{guide-DwmaD_Wa.mjs → guide-BPBlh9ML.mjs} +512 -73
  26. package/dist/{guide-status-CWvti2Lg.mjs → guide-status-i9ogHt4v.mjs} +2 -2
  27. package/dist/{idle--jn7pBNe.mjs → idle-S3tFZ53_.mjs} +7 -6
  28. package/dist/{in-use-CNIWU-r-.mjs → in-use-D9XYEgFD.mjs} +7 -7
  29. package/dist/{ios-CbdqTqOc.mjs → ios-CDzWL1mj.mjs} +302 -88
  30. package/dist/{ios-COGcGEcN.mjs → ios-CZWOHTTc.mjs} +24 -19
  31. package/dist/{ios-device-DNjPXBnb.mjs → ios-device-D9cLxVr5.mjs} +1 -1
  32. package/dist/{launch-verify-BhgkYAws.mjs → launch-verify-B2oR_kFN.mjs} +2 -2
  33. package/dist/logs-SzuYudOj.mjs +485 -0
  34. package/dist/merge-state-ytFQCeh4.mjs +262 -0
  35. package/dist/{metro-BWLOJ_Wr.mjs → metro-Dzo30hvJ.mjs} +1 -1
  36. package/dist/{named-ports-DiTfSz6p.mjs → named-ports-Dn1o61D9.mjs} +3 -3
  37. package/dist/{ownership-BfPNRf3-.mjs → ownership-B8d4kXBx.mjs} +49 -16
  38. package/dist/{ownership-D2xbLVoz.mjs → ownership-Dqnrjrio.mjs} +2 -2
  39. package/dist/{ports-CLSSFOuz.mjs → ports-Dp_QTwTC.mjs} +3 -3
  40. package/dist/{project-BXZzOABt.mjs → project-B8rbutVC.mjs} +4 -72
  41. package/dist/{reload-CymyF6Kk.mjs → reload-nocmx8KB.mjs} +9 -9
  42. package/dist/remote-cache-BRsRZru5.mjs +526 -0
  43. package/dist/{server-bare-DVbb7kXH.mjs → server-bare-Gn3ssSdK.mjs} +1 -1
  44. package/dist/{server-expo-BvOCeakp.mjs → server-expo-CeT1nIIk.mjs} +2 -2
  45. package/dist/server-expo-eWi9Coof.mjs +2 -0
  46. package/dist/{settings-DOugPlPz.mjs → settings-BlyDn2Oz.mjs} +14 -5
  47. package/dist/{settings-EWYNwe72.mjs → settings-bE-Gu3Ti.mjs} +23 -11
  48. package/dist/settings.schema.json +73 -1
  49. package/dist/{simslim-DZUxzsjq.mjs → simslim-COtEEP7x.mjs} +152 -96
  50. package/dist/{slot-launch-Dk4oTREM.mjs → slot-launch-DIACGEfH.mjs} +4 -4
  51. package/dist/{spawn-claims-CUleC2cM.mjs → spawn-claims-PT8aQpCa.mjs} +25 -2
  52. package/dist/{start-qWapeB4W.mjs → start-BFstVBlx.mjs} +1 -1
  53. package/dist/{start-RbmFPeZ7.mjs → start-BboE1HIS.mjs} +108 -17
  54. package/dist/{state-WGdIbi_d.mjs → state-DcDgzfu3.mjs} +15 -3
  55. package/dist/{stats-BAgJaLA4.mjs → stats-D8rUTPHd.mjs} +1 -1
  56. package/dist/{stats-DBYJ5bxL.mjs → stats-DiCtmluY.mjs} +4 -4
  57. package/dist/{status-Dr4fRkym.mjs → status-BUASYyy_.mjs} +92 -33
  58. package/dist/{status-Zp8kvexe.mjs → status-DI9Tjo7T.mjs} +255 -38
  59. package/dist/stim-desktop-D0xhpZt8.mjs +29 -0
  60. package/dist/stop-BI7QY76M.mjs +3 -0
  61. package/dist/{stop-DcEv5sk-.mjs → stop-Bn6v-GvS.mjs} +205 -37
  62. package/dist/supervisor-run.mjs +11 -11
  63. package/dist/{workspace-process-lock-BXA96myD.mjs → workspace-process-lock-BvvJTfo_.mjs} +10 -5
  64. package/dist/{worktree-DEG2o9F8.mjs → worktree-B5sTRskL.mjs} +1 -1
  65. package/dist/{worktree-Bd4dRvx7.mjs → worktree-ChDL5fO1.mjs} +17 -16
  66. package/dist/{worktree-BlizKDpZ.mjs → worktree-DeBQSVtR.mjs} +3 -1
  67. package/dist/{xcode-B2tBkf0m.mjs → xcode-BzplpQzc.mjs} +7 -7
  68. package/package.json +5 -5
  69. package/dist/budget-nDCEwtJv.mjs +0 -620
  70. package/dist/logs-MARIy6m9.mjs +0 -209
  71. package/dist/server-expo-DBVTdult.mjs +0 -2
  72. package/dist/stop-DI4BG4Os.mjs +0 -3
@@ -363,6 +363,21 @@
363
363
  ]
364
364
  }
365
365
  },
366
+ "deviceProfile": {
367
+ "description": "avdmanager hardware profile id for new owned AVDs",
368
+ "type": "string",
369
+ "default": "pixel_6",
370
+ "x-stim": {
371
+ "key": "android.deviceProfile",
372
+ "kind": "string",
373
+ "scopes": [
374
+ "machine",
375
+ "workspace",
376
+ "repo",
377
+ "committed"
378
+ ]
379
+ }
380
+ },
366
381
  "dataPartitionSizeGb": {
367
382
  "description": "Data partition size of a new owned AVD, in GiB",
368
383
  "type": "integer",
@@ -867,8 +882,29 @@
867
882
  },
868
883
  "additionalProperties": false
869
884
  },
885
+ "android": {
886
+ "type": "object",
887
+ "properties": {
888
+ "deviceProfile": {
889
+ "description": "avdmanager hardware profile id for new owned AVDs",
890
+ "type": "string",
891
+ "default": "pixel_6",
892
+ "x-stim": {
893
+ "key": "android.deviceProfile",
894
+ "kind": "string",
895
+ "scopes": [
896
+ "machine",
897
+ "workspace",
898
+ "repo",
899
+ "committed"
900
+ ]
901
+ }
902
+ }
903
+ },
904
+ "additionalProperties": false
905
+ },
870
906
  "iosSimulatorApp": {
871
- "description": "App that displays an owned iOS simulator",
907
+ "description": "App that displays an owned iOS simulator; stim-desktop by default when Stim Desktop is installed",
872
908
  "type": "string",
873
909
  "enum": [
874
910
  "xcode",
@@ -884,6 +920,22 @@
884
920
  ]
885
921
  }
886
922
  },
923
+ "androidEmulatorApp": {
924
+ "description": "App that displays an owned Android emulator Stim boots on macOS; stim-desktop by default when Stim Desktop is installed",
925
+ "type": "string",
926
+ "enum": [
927
+ "emulator",
928
+ "stim-desktop"
929
+ ],
930
+ "default": "emulator",
931
+ "x-stim": {
932
+ "key": "androidEmulatorApp",
933
+ "kind": "choice",
934
+ "scopes": [
935
+ "machine"
936
+ ]
937
+ }
938
+ },
887
939
  "concurrency": {
888
940
  "type": "object",
889
941
  "properties": {
@@ -978,6 +1030,26 @@
978
1030
  },
979
1031
  "additionalProperties": false
980
1032
  },
1033
+ "gc": {
1034
+ "type": "object",
1035
+ "properties": {
1036
+ "worktreeGraceMinutes": {
1037
+ "description": "Minutes after a linked worktree last changed, or its branch merged, before gc removes it; 0 removes it at once",
1038
+ "type": "integer",
1039
+ "minimum": 0,
1040
+ "default": 120,
1041
+ "x-stim": {
1042
+ "key": "gc.worktreeGraceMinutes",
1043
+ "kind": "number",
1044
+ "scopes": [
1045
+ "machine"
1046
+ ],
1047
+ "env": "STIM_GC_WORKTREE_GRACE_MINUTES"
1048
+ }
1049
+ }
1050
+ },
1051
+ "additionalProperties": false
1052
+ },
981
1053
  "pool": {
982
1054
  "type": "object",
983
1055
  "properties": {
@@ -1,10 +1,11 @@
1
- import { D as process_identity_exports, H as projectDeviceSlots, g as loadConfig, k as ownership_claim_exports, u as getConfigDir, z as deviceSlotPlatforms } from "./config-BEq0Qw_a.mjs";
1
+ import { A as ownership_claim_exports, B as deviceSlotPlatforms, O as process_identity_exports, U as projectDeviceSlots, _ as loadConfig, d as getConfigDir } from "./config-0jCcMM0E.mjs";
2
2
  import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
3
  import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
4
- import { g as releaseBuildLock, m as acquireBuildLock } from "./device-lease-CbEaIk7b.mjs";
5
- import { w as listAdbDevices } from "./android-787HhP5w.mjs";
6
- import { l as listAllIosSims, s as iosRuntimeMatches } from "./ios-COGcGEcN.mjs";
7
- import { _ as easCliSupport, s as readInstalledEasCliVersion, v as easCliUpgradeRemedy, z as resolveEasCliBin } from "./device-remote-powUHdbK.mjs";
4
+ import { g as releaseBuildLock, m as acquireBuildLock } from "./device-lease-B9LAXIUH.mjs";
5
+ import { E as listAdbDevices } from "./android-75Sk2rIR.mjs";
6
+ import { l as listAllIosSims, s as iosRuntimeMatches } from "./ios-CZWOHTTc.mjs";
7
+ import { f as resolveEasCliBin } from "./remote-cache-BRsRZru5.mjs";
8
+ import { O as easCliSupport, k as easCliUpgradeRemedy, s as readInstalledEasCliVersion } from "./device-remote-C0_o-tJH.mjs";
8
9
  import { existsSync, realpathSync, statSync } from "node:fs";
9
10
  import { join } from "node:path";
10
11
  import { createHash } from "node:crypto";
@@ -48,7 +49,137 @@ function selectEasBuild(builds, { platform, profile, fingerprint, projectId, phy
48
49
  }
49
50
  return null;
50
51
  }
51
- async function resolveEasDevelopmentBuild({ root, platform, profile, note, isExpo = true, physical = false, selectors = [], buildCache, readEasCliVersion = readInstalledEasCliVersion }) {
52
+ async function lookUpEasBuild({ root, platform, profile, note, physical = false, readEasCliVersion = readInstalledEasCliVersion }) {
53
+ const retry = "the same Stim command";
54
+ const buildCommand = `npx eas-cli build --platform ${platform} --profile ${shellArg(profile)}`;
55
+ const cli = resolveEasCliBin(root);
56
+ if (!cli) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS CLI is not installed.", "Install eas-cli, run eas login, then retry.");
57
+ const easCli = easCliSupport(readEasCliVersion(cli.file, root), MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION);
58
+ if (!easCli.supported) throw new EasFailure("STIM_EAS_UNAVAILABLE", easCli.version ? `eas-cli ${easCli.version} (${cli.file}) cannot download a build by ID; --eas-profile needs eas-cli ${MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION} or later.` : `Could not read an eas-cli version from \`${cli.file} --version\`; --eas-profile needs eas-cli ${MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION} or later.`, `${easCliUpgradeRemedy(MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION)}, then retry ${retry}.`);
59
+ let profileEnv = {};
60
+ const run = (args, timeoutMs = 12e4) => {
61
+ try {
62
+ return JSON.parse(getExecutor().runFile(cli.file, [
63
+ ...args,
64
+ "--json",
65
+ "--non-interactive"
66
+ ], {
67
+ cwd: root,
68
+ timeoutMs,
69
+ env: profileEnv
70
+ }));
71
+ } catch {
72
+ throw new EasFailure("STIM_EAS_UNAVAILABLE", `EAS ${args[0]} failed or returned invalid JSON.`, `Run npx eas-cli ${args.map(shellArg).join(" ")} to inspect the error, then retry ${retry}.`);
73
+ }
74
+ };
75
+ note(`eas resolving profile ${profile}`);
76
+ const config = object(run([
77
+ "config",
78
+ "--platform",
79
+ platform,
80
+ "--profile",
81
+ profile
82
+ ]));
83
+ const buildProfile = object(config.buildProfile);
84
+ profileEnv = object(buildProfile.env);
85
+ const projectId = object(object(object(config.appConfig).extra).eas).projectId;
86
+ if (buildProfile.developmentClient !== true || buildProfile.distribution !== "internal" || platform === "ios" && buildProfile.simulator === true !== !physical) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} is not an internal development build for this ${platform} target.`, `Set developmentClient: true and distribution: internal${platform === "ios" ? `, with ios.simulator: ${!physical}` : ""} in that eas.json profile.`);
87
+ if (typeof projectId !== "string" || !projectId) throw new EasFailure("STIM_BAD_ARG", "The Expo app is not linked to an EAS project.", "Link the intended EAS project with eas init, then retry.");
88
+ if (platform === "ios" ? buildProfile.buildConfiguration !== void 0 && buildProfile.buildConfiguration !== "Debug" : buildProfile.gradleCommand !== void 0 && (typeof buildProfile.gradleCommand !== "string" || !/^:app:assemble\w*Debug$/.test(buildProfile.gradleCommand))) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} overrides the development build configuration.`, "Use ios.buildConfiguration: Debug or an android.gradleCommand that assembles a single Debug APK, or remove the override.");
89
+ note(`eas fingerprinting ${platform} with profile ${profile}`);
90
+ const fingerprint = object(run([
91
+ "fingerprint:generate",
92
+ "--platform",
93
+ platform,
94
+ "--build-profile",
95
+ profile
96
+ ])).hash;
97
+ if (typeof fingerprint !== "string" || !/^[a-f0-9]{40,64}$/.test(fingerprint)) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS returned no valid native fingerprint.", `Run npx eas-cli fingerprint:generate --platform ${platform} --build-profile ${shellArg(profile)} to inspect the result.`);
98
+ note(`eas looking for ${fingerprint.slice(0, 8)}`);
99
+ const buildId = selectEasBuild(run([
100
+ "build:list",
101
+ "--platform",
102
+ platform,
103
+ "--build-profile",
104
+ profile,
105
+ "--fingerprint-hash",
106
+ fingerprint,
107
+ "--status",
108
+ "finished",
109
+ ...platform === "ios" && !physical ? ["--simulator"] : ["--distribution", "internal"],
110
+ "--limit",
111
+ "1"
112
+ ]), {
113
+ platform,
114
+ profile,
115
+ fingerprint,
116
+ projectId,
117
+ physical
118
+ });
119
+ const missing = {
120
+ code: "STIM_EAS_BUILD_MISSING",
121
+ message: `No compatible EAS ${platform} build exists for profile ${profile} and fingerprint ${fingerprint}.`,
122
+ remedy: `A cloud build may incur charges. With approval to run it, use ${buildCommand}, then retry ${retry}.`
123
+ };
124
+ return {
125
+ fingerprint,
126
+ buildId,
127
+ cacheKey: buildId ? `eas-${createHash("sha256").update(JSON.stringify({
128
+ projectId,
129
+ buildId
130
+ })).digest("hex")}-debug-${physical && platform === "ios" ? "device" : "sim"}` : null,
131
+ missing,
132
+ run
133
+ };
134
+ }
135
+ function easFailure(error, retry) {
136
+ const claim = (0, ownership_claim_exports.claimFailure)(error, null);
137
+ if (claim) return {
138
+ ok: false,
139
+ code: claim.code,
140
+ message: claim.message,
141
+ remedy: claim.remedy
142
+ };
143
+ if (error instanceof EasFailure) return {
144
+ ok: false,
145
+ code: error.code,
146
+ message: error.message,
147
+ remedy: error.remedy
148
+ };
149
+ return {
150
+ ok: false,
151
+ code: "STIM_EAS_UNAVAILABLE",
152
+ message: `Could not resolve the EAS development build: ${error.message}`,
153
+ remedy: `Retry ${retry}. No cloud build was started.`
154
+ };
155
+ }
156
+ /** Looks up the EAS build `--eas-profile` would download, without downloading it or taking a claim. */
157
+ async function planEasDevelopmentBuild(args) {
158
+ const refusal = easOptionRefusal({
159
+ profile: args.profile,
160
+ isExpo: args.isExpo ?? true,
161
+ buildSelector: (args.selectors ?? []).find((value) => value != null) ?? void 0,
162
+ buildCache: args.buildCache
163
+ });
164
+ if (refusal) return {
165
+ ok: false,
166
+ ...refusal
167
+ };
168
+ try {
169
+ const found = await lookUpEasBuild(args);
170
+ return {
171
+ ok: true,
172
+ fingerprint: found.fingerprint,
173
+ cacheKey: found.cacheKey,
174
+ buildId: found.buildId,
175
+ missing: found.buildId ? null : found.missing
176
+ };
177
+ } catch (error) {
178
+ return easFailure(error, "the same Stim command");
179
+ }
180
+ }
181
+ async function resolveEasDevelopmentBuild(args) {
182
+ const { root, platform, profile, isExpo = true, selectors = [], buildCache } = args;
52
183
  if (profile === void 0) return null;
53
184
  const refusal = easOptionRefusal({
54
185
  profile,
@@ -61,85 +192,20 @@ async function resolveEasDevelopmentBuild({ root, platform, profile, note, isExp
61
192
  ...refusal
62
193
  };
63
194
  const retry = "the same Stim command";
64
- const buildCommand = `npx eas-cli build --platform ${platform} --profile ${shellArg(profile)}`;
65
195
  let lock = null;
66
196
  try {
67
- const cli = resolveEasCliBin(root);
68
- if (!cli) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS CLI is not installed.", "Install eas-cli, run eas login, then retry.");
69
- const easCli = easCliSupport(readEasCliVersion(cli.file, root), MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION);
70
- if (!easCli.supported) throw new EasFailure("STIM_EAS_UNAVAILABLE", easCli.version ? `eas-cli ${easCli.version} (${cli.file}) cannot download a build by ID; --eas-profile needs eas-cli ${MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION} or later.` : `Could not read an eas-cli version from \`${cli.file} --version\`; --eas-profile needs eas-cli ${MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION} or later.`, `${easCliUpgradeRemedy(MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION)}, then retry ${retry}.`);
71
- let profileEnv = {};
72
- const run = (args, timeoutMs = 12e4) => {
73
- try {
74
- return JSON.parse(getExecutor().runFile(cli.file, [
75
- ...args,
76
- "--json",
77
- "--non-interactive"
78
- ], {
79
- cwd: root,
80
- timeoutMs,
81
- env: profileEnv
82
- }));
83
- } catch {
84
- throw new EasFailure("STIM_EAS_UNAVAILABLE", `EAS ${args[0]} failed or returned invalid JSON.`, `Run npx eas-cli ${args.map(shellArg).join(" ")} to inspect the error, then retry ${retry}.`);
85
- }
86
- };
87
- note(`eas resolving profile ${profile}`);
88
- const config = object(run([
89
- "config",
90
- "--platform",
91
- platform,
92
- "--profile",
93
- profile
94
- ]));
95
- const buildProfile = object(config.buildProfile);
96
- profileEnv = object(buildProfile.env);
97
- const projectId = object(object(object(config.appConfig).extra).eas).projectId;
98
- if (buildProfile.developmentClient !== true || buildProfile.distribution !== "internal" || platform === "ios" && buildProfile.simulator === true !== !physical) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} is not an internal development build for this ${platform} target.`, `Set developmentClient: true and distribution: internal${platform === "ios" ? `, with ios.simulator: ${!physical}` : ""} in that eas.json profile.`);
99
- if (typeof projectId !== "string" || !projectId) throw new EasFailure("STIM_BAD_ARG", "The Expo app is not linked to an EAS project.", "Link the intended EAS project with eas init, then retry.");
100
- if (platform === "ios" ? buildProfile.buildConfiguration !== void 0 && buildProfile.buildConfiguration !== "Debug" : buildProfile.gradleCommand !== void 0 && (typeof buildProfile.gradleCommand !== "string" || !/^:app:assemble\w*Debug$/.test(buildProfile.gradleCommand))) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} overrides the development build configuration.`, "Use ios.buildConfiguration: Debug or an android.gradleCommand that assembles a single Debug APK, or remove the override.");
101
- note(`eas fingerprinting ${platform} with profile ${profile}`);
102
- const fingerprint = object(run([
103
- "fingerprint:generate",
104
- "--platform",
105
- platform,
106
- "--build-profile",
197
+ const { fingerprint, buildId, cacheKey, missing, run } = await lookUpEasBuild({
198
+ ...args,
107
199
  profile
108
- ])).hash;
109
- if (typeof fingerprint !== "string" || !/^[a-f0-9]{40,64}$/.test(fingerprint)) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS returned no valid native fingerprint.", `Run npx eas-cli fingerprint:generate --platform ${platform} --build-profile ${shellArg(profile)} to inspect the result.`);
110
- note(`eas looking for ${fingerprint.slice(0, 8)}`);
111
- const buildId = selectEasBuild(run([
112
- "build:list",
113
- "--platform",
114
- platform,
115
- "--build-profile",
116
- profile,
117
- "--fingerprint-hash",
118
- fingerprint,
119
- "--status",
120
- "finished",
121
- ...platform === "ios" && !physical ? ["--simulator"] : ["--distribution", "internal"],
122
- "--limit",
123
- "1"
124
- ]), {
125
- platform,
126
- profile,
127
- fingerprint,
128
- projectId,
129
- physical
130
200
  });
131
- if (!buildId) throw new EasFailure("STIM_EAS_BUILD_MISSING", `No compatible EAS ${platform} build exists for profile ${profile} and fingerprint ${fingerprint}.`, `A cloud build may incur charges. With approval to run it, use ${buildCommand}, then retry ${retry}.`);
132
- const cacheKey = `eas-${createHash("sha256").update(JSON.stringify({
133
- projectId,
134
- buildId
135
- })).digest("hex")}-debug-${physical && platform === "ios" ? "device" : "sim"}`;
201
+ if (!buildId || !cacheKey) throw new EasFailure(missing.code, missing.message, missing.remedy);
136
202
  lock = acquireBuildLock({
137
203
  platform,
138
204
  key: cacheKey,
139
205
  root
140
206
  });
141
207
  if (!lock.acquired) throw new EasFailure("STIM_EAS_UNAVAILABLE", `Another run holds the EAS artifact claim at ${lock.path}.`, `Wait for that run to finish, then retry ${retry}.`);
142
- note(`eas resolving artifact for build ${buildId}`);
208
+ args.note(`eas resolving artifact for build ${buildId}`);
143
209
  const result = object(run([
144
210
  "build:download",
145
211
  "--build-id",
@@ -148,7 +214,7 @@ async function resolveEasDevelopmentBuild({ root, platform, profile, note, isExp
148
214
  if (typeof result.path !== "string" || !existsSync(result.path)) throw new Error("EAS returned no downloaded artifact.");
149
215
  const path = realpathSync(result.path);
150
216
  if (platform === "ios" ? !path.endsWith(".app") || !statSync(path).isDirectory() : !path.endsWith(".apk") || !statSync(path).isFile()) throw new Error("EAS returned an unexpected artifact path or type.");
151
- note(`eas artifact ready ${fingerprint.slice(0, 8)}`);
217
+ args.note(`eas artifact ready ${fingerprint.slice(0, 8)}`);
152
218
  return {
153
219
  ok: true,
154
220
  path,
@@ -157,25 +223,7 @@ async function resolveEasDevelopmentBuild({ root, platform, profile, note, isExp
157
223
  cacheHit: "remote"
158
224
  };
159
225
  } catch (error) {
160
- const claim = (0, ownership_claim_exports.claimFailure)(error, null);
161
- if (claim) return {
162
- ok: false,
163
- code: claim.code,
164
- message: claim.message,
165
- remedy: claim.remedy
166
- };
167
- if (error instanceof EasFailure) return {
168
- ok: false,
169
- code: error.code,
170
- message: error.message,
171
- remedy: error.remedy
172
- };
173
- return {
174
- ok: false,
175
- code: "STIM_EAS_UNAVAILABLE",
176
- message: `Could not resolve the EAS development build: ${error.message}`,
177
- remedy: `Retry ${retry}. No cloud build was started.`
178
- };
226
+ return easFailure(error, retry);
179
227
  } finally {
180
228
  releaseBuildLock(lock);
181
229
  }
@@ -289,6 +337,14 @@ function unknownAndroidSystemImageRefusal(requested, images) {
289
337
  remedy: "Pass `--system-image` (or set android.systemImage) to one of the package ids printed above. Install more with `sdkmanager \"system-images;android-36;google_apis;arm64-v8a\"`."
290
338
  };
291
339
  }
340
+ function unknownAndroidDeviceProfileRefusal(requested, profiles) {
341
+ if (!requested) return null;
342
+ if (profiles.includes(requested)) return null;
343
+ return {
344
+ message: `No Android hardware profile is named "${requested}". Profiles avdmanager offers: ${installedNames(profiles)}.`,
345
+ remedy: "Pass `--device-profile` (or set android.deviceProfile) to one of the ids printed above, exactly as `avdmanager list device -c` spells it, e.g. \"pixel_fold\" or \"pixel_tablet\"."
346
+ };
347
+ }
292
348
  //#endregion
293
349
  //#region src/engine/simslim.ts
294
350
  async function reconcileSimSlim({ udid, profile, previouslyManaged = false, out = () => {}, spawn, timeoutMs = 72e4, cleanupMs = 1e4 }) {
@@ -433,4 +489,4 @@ function simslimIsOnPath() {
433
489
  }
434
490
  }
435
491
  //#endregion
436
- export { liveOwnedDeviceCount as a, unknownIosRuntimeRefusal as c, isEasBuildFailure as d, resolveEasDevelopmentBuild as f, deviceTypeMismatch as i, MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION as l, simslimIsOnPath as n, unknownAndroidSystemImageRefusal as o, checkDeviceCapacity as r, unknownIosDeviceTypeRefusal as s, reconcileSimSlim as t, easDeviceBuildRemedy as u };
492
+ export { liveOwnedDeviceCount as a, unknownIosDeviceTypeRefusal as c, easDeviceBuildRemedy as d, isEasBuildFailure as f, deviceTypeMismatch as i, unknownIosRuntimeRefusal as l, resolveEasDevelopmentBuild as m, simslimIsOnPath as n, unknownAndroidDeviceProfileRefusal as o, planEasDevelopmentBuild as p, checkDeviceCapacity as r, unknownAndroidSystemImageRefusal as s, reconcileSimSlim as t, MIN_EAS_CLI_BUILD_DOWNLOAD_VERSION as u };
@@ -1,7 +1,7 @@
1
- import { f as getProject } from "./config-BEq0Qw_a.mjs";
2
- import { m as readWorkspaceState } from "./metro-BWLOJ_Wr.mjs";
3
- import { n as fileLeaseIo } from "./device-lease-CbEaIk7b.mjs";
4
- import { d as deviceShellArg } from "./app-install-Cfmqr3TQ.mjs";
1
+ import { p as getProject } from "./config-0jCcMM0E.mjs";
2
+ import { m as readWorkspaceState } from "./metro-Dzo30hvJ.mjs";
3
+ import { n as fileLeaseIo } from "./device-lease-B9LAXIUH.mjs";
4
+ import { d as deviceShellArg } from "./app-install-CsZi0SAT.mjs";
5
5
  //#region src/engine/slot-launch.ts
6
6
  function launchSlotScope(root, slot = "default") {
7
7
  if (slot !== "default") return slot;
@@ -1,6 +1,8 @@
1
- import { D as process_identity_exports, k as ownership_claim_exports } from "./config-BEq0Qw_a.mjs";
1
+ import { A as ownership_claim_exports, O as process_identity_exports } from "./config-0jCcMM0E.mjs";
2
2
  //#region src/engine/spawn-claims.ts
3
3
  const declaring = /* @__PURE__ */ new Set();
4
+ const running = /* @__PURE__ */ new Set();
5
+ let refusal = null;
4
6
  /** Record every child later started through `spawnDeclared` on `claim` until `stopDeclaringSpawnsOn(claim)`. */
5
7
  function declareSpawnsOn(claim) {
6
8
  if (claim) declaring.add(claim);
@@ -15,6 +17,7 @@ function stopDeclaringSpawnsOn(claim) {
15
17
  * records one child, so declared spawns must not overlap.
16
18
  */
17
19
  function spawnDeclared(spawn) {
20
+ if (refusal !== null) throw new Error(refusal);
18
21
  const claims = [...declaring];
19
22
  if (claims.length === 0) return spawn();
20
23
  const forget = () => {
@@ -35,6 +38,8 @@ function spawnDeclared(spawn) {
35
38
  forget();
36
39
  return child;
37
40
  }
41
+ running.add(child);
42
+ child.once("exit", () => running.delete(child));
38
43
  const captured = (0, process_identity_exports.captureProcessIdentity)(pid);
39
44
  if (captured.ok) for (const claim of claims) try {
40
45
  (0, ownership_claim_exports.setClaimChild)(claim, {
@@ -45,5 +50,23 @@ function spawnDeclared(spawn) {
45
50
  child.once("exit", forget);
46
51
  return child;
47
52
  }
53
+ /** Refuse every later `spawnDeclared` with `reason` until `allowDeclaredSpawns()`. */
54
+ function refuseDeclaredSpawns(reason) {
55
+ refusal = reason;
56
+ }
57
+ function allowDeclaredSpawns() {
58
+ refusal = null;
59
+ }
60
+ /** Send `signal` to every child started through `spawnDeclared` that has not exited; returns how many were signalled. */
61
+ function signalDeclaredSpawns(signal) {
62
+ let signalled = 0;
63
+ for (const child of running) {
64
+ if (child.exitCode !== null || child.signalCode !== null) continue;
65
+ try {
66
+ if (child.kill(signal)) signalled += 1;
67
+ } catch {}
68
+ }
69
+ return signalled;
70
+ }
48
71
  //#endregion
49
- export { spawnDeclared as n, stopDeclaringSpawnsOn as r, declareSpawnsOn as t };
72
+ export { spawnDeclared as a, signalDeclaredSpawns as i, declareSpawnsOn as n, stopDeclaringSpawnsOn as o, refuseDeclaredSpawns as r, allowDeclaredSpawns as t };
@@ -1,2 +1,2 @@
1
- import { a as registerStart, c as startFacts, d as wantsExpoOwnTunnel, i as readLogTail, l as supervisorEntry, n as liveSupervisor, o as startCommand, r as parseWait, s as startDevServer, t as failureEvidence, u as tailLines } from "./start-RbmFPeZ7.mjs";
1
+ import { a as registerStart, c as startFacts, d as wantsExpoOwnTunnel, i as readLogTail, l as supervisorEntry, n as liveSupervisor, o as startCommand, r as parseWait, s as startDevServer, t as failureEvidence, u as tailLines } from "./start-BboE1HIS.mjs";
2
2
  export { startCommand as default, failureEvidence, liveSupervisor, parseWait, readLogTail, registerStart, startDevServer, startFacts, supervisorEntry, tailLines, wantsExpoOwnTunnel };
@@ -1,23 +1,113 @@
1
- import { D as process_identity_exports, f as getProject, w as upsertProject } from "./config-BEq0Qw_a.mjs";
1
+ import { O as process_identity_exports, T as upsertProject, p as getProject } from "./config-0jCcMM0E.mjs";
2
2
  import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
3
3
  import { i as levelRank } from "./ndjson-CDY9IkVC.mjs";
4
- import { T as supervisorLogFile, c as signalProcessTree, d as reserveMetroPort, h as recordWorkspaceUse, i as pidExists, j as workspaceLogsDir, m as readWorkspaceState$1, p as clearWorkspaceStateKeys, s as resolveProjectMetro, x as ensureWorkspaceStorage, y as writeWorkspaceState } from "./metro-BWLOJ_Wr.mjs";
5
- import { s as gitCommonDir, y as repoRoot } from "./worktree-BlizKDpZ.mjs";
6
- import { a as detectIsExpo, i as detectBundleId, n as appProjectProblem, o as findProjectRoot, r as detectAndroidPackage, t as NO_PROJECT_REFUSAL } from "./project-BXZzOABt.mjs";
4
+ import { T as supervisorLogFile, c as signalProcessTree, d as reserveMetroPort, h as recordWorkspaceUse, i as pidExists, j as workspaceLogsDir, m as readWorkspaceState$1, p as clearWorkspaceStateKeys, s as resolveProjectMetro, x as ensureWorkspaceStorage, y as writeWorkspaceState } from "./metro-Dzo30hvJ.mjs";
5
+ import { s as gitCommonDir, y as repoRoot } from "./worktree-DeBQSVtR.mjs";
6
+ import { c as readAppConfigText, i as findProjectRoot, l as readAppJson, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-B8rbutVC.mjs";
7
7
  import { n as windowsLauncherArgs } from "./detached-entry-DJHoca49.mjs";
8
- import { i as clearManagedMetroTunnel, s as readMetroTunnel } from "./state-WGdIbi_d.mjs";
9
- import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-BXA96myD.mjs";
10
- import { _ as stepTimer, l as phaseLine } from "./command-output-D5z2_bcF.mjs";
11
- import { A as resolveCacheProviderConfig, B as planMetroReach, C as ngrokUrlSetting, E as publicUrlSetting, I as tunnelModeSetting, L as unknownSettingKeys, O as remoteAndroidSetting, P as settingShapeErrors, R as PUBLIC_METRO_ENV, a as SETTING_SHAPE_REMEDY, b as metroIdleStopMinutesSetting, d as cacheProviderSettingError, j as resolveSettings, k as remoteIosSetting, x as metroTunnelSettingError, z as detectProviders } from "./settings-DOugPlPz.mjs";
12
- import { a as terminateChild, c as withManagedTunnelLock, i as stopTunnel, o as withManagedRemoteWorktreeLock, r as startTunnelSequence, t as resolveSupervisorTarget } from "./ownership-BfPNRf3-.mjs";
13
- import { i as stopOwnedMetroForReset, t as budgetGate } from "./budget-nDCEwtJv.mjs";
8
+ import { c as readMetroTunnel, i as clearManagedMetroTunnel } from "./state-DcDgzfu3.mjs";
9
+ import { n as workspaceProcessLockError, t as withWorkspaceProcessLock } from "./workspace-process-lock-BvvJTfo_.mjs";
10
+ import { _ as stepTimer, l as phaseLine } from "./command-output-BWCj5Vh-.mjs";
11
+ import { A as resolveCacheProviderConfig, B as planMetroReach, C as ngrokUrlSetting, E as publicUrlSetting, I as tunnelModeSetting, L as unknownSettingKeys, O as remoteAndroidSetting, P as settingShapeErrors, R as PUBLIC_METRO_ENV, a as SETTING_SHAPE_REMEDY, b as metroIdleStopMinutesSetting, d as cacheProviderSettingError, j as resolveSettings, k as remoteIosSetting, x as metroTunnelSettingError, z as detectProviders } from "./settings-BlyDn2Oz.mjs";
12
+ import { a as terminateChild, c as withManagedTunnelLock, i as stopTunnel, o as withManagedRemoteWorktreeLock, r as startTunnelSequence, t as resolveSupervisorTarget } from "./ownership-B8d4kXBx.mjs";
13
+ import { d as readProjectConfig } from "./remote-cache-BRsRZru5.mjs";
14
+ import { I as stopOwnedMetroForReset, t as budgetGate } from "./budget-DbzW5VTD.mjs";
14
15
  import { appendFileSync, mkdirSync, openSync, readFileSync } from "node:fs";
16
+ import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1 } from "fs";
17
+ import { join } from "path";
15
18
  import { IDLE_STOP_KEY, queryLogs } from "@stim-cli/core/state";
16
- import { basename, dirname, join } from "node:path";
19
+ import { basename as basename$1, dirname as dirname$1, join as join$1 } from "node:path";
17
20
  import { EventEmitter } from "node:events";
18
21
  import { fileURLToPath } from "node:url";
19
22
  import chalk from "chalk";
20
23
  import { CACHE_PROVIDER_ENV, cacheProviderEnv } from "@stim-cli/cache";
24
+ //#region src/workspace/app-id.ts
25
+ function configString(config, platform, key) {
26
+ if (!config || typeof config !== "object") return null;
27
+ const expoField = config.expo;
28
+ const section = (expoField && typeof expoField === "object" ? expoField : config)[platform];
29
+ if (!section || typeof section !== "object") return null;
30
+ const value = section[key];
31
+ return typeof value === "string" && value ? value : null;
32
+ }
33
+ function literalString(text, key) {
34
+ return text?.match(new RegExp(`${key}\\s*:\\s*["']([^"']+)["']`))?.[1] ?? null;
35
+ }
36
+ function idsFromConfig(projectRoot) {
37
+ const read = readProjectConfig(projectRoot);
38
+ if (!read.unavailable) return {
39
+ bundleId: configString(read.config, "ios", "bundleIdentifier"),
40
+ androidPackage: configString(read.config, "android", "package")
41
+ };
42
+ const appJson = readAppJson(projectRoot);
43
+ const text = readAppConfigText(projectRoot);
44
+ return {
45
+ bundleId: configString(appJson, "ios", "bundleIdentifier") ?? literalString(text, "bundleIdentifier"),
46
+ androidPackage: configString(appJson, "android", "package") ?? literalString(text, "package")
47
+ };
48
+ }
49
+ function detectAppIds(projectRoot) {
50
+ const ids = idsFromConfig(projectRoot);
51
+ return {
52
+ bundleId: ids.bundleId ?? detectBundleIdFromPbxproj(projectRoot),
53
+ androidPackage: ids.androidPackage ?? detectAndroidPackageFromGradle(projectRoot)
54
+ };
55
+ }
56
+ function detectBundleId(projectRoot) {
57
+ return detectAppIds(projectRoot).bundleId;
58
+ }
59
+ function detectAndroidPackage(projectRoot) {
60
+ return detectAppIds(projectRoot).androidPackage;
61
+ }
62
+ function detectBundleIdFromPbxproj(projectRoot) {
63
+ const iosDir = join(projectRoot, "ios");
64
+ if (!existsSync$1(iosDir)) return null;
65
+ let entries;
66
+ try {
67
+ entries = readdirSync$1(iosDir, { withFileTypes: true });
68
+ } catch {
69
+ return null;
70
+ }
71
+ for (const entry of entries) {
72
+ if (!entry.isDirectory() || !entry.name.endsWith(".xcodeproj")) continue;
73
+ const pbx = join(iosDir, entry.name, "project.pbxproj");
74
+ if (!existsSync$1(pbx)) continue;
75
+ let text;
76
+ try {
77
+ text = readFileSync$1(pbx, "utf-8");
78
+ } catch {
79
+ continue;
80
+ }
81
+ const concrete = [...text.matchAll(/PRODUCT_BUNDLE_IDENTIFIER\s*=\s*([^;\s"]+)\s*;/g)].map((m) => m[1]).filter((id) => !!id && !id.startsWith("$") && !id.includes("("));
82
+ if (concrete.length === 0) continue;
83
+ const counts = {};
84
+ for (const id of concrete) counts[id] = (counts[id] || 0) + 1;
85
+ let best = null, bestCount = 0, bestLen = Infinity;
86
+ for (const [id, count] of Object.entries(counts)) if (count > bestCount || count === bestCount && id.length < bestLen) {
87
+ best = id;
88
+ bestCount = count;
89
+ bestLen = id.length;
90
+ }
91
+ return best;
92
+ }
93
+ return null;
94
+ }
95
+ function detectAndroidPackageFromGradle(projectRoot) {
96
+ const gradle = join(projectRoot, "android", "app", "build.gradle");
97
+ if (!existsSync$1(gradle)) return null;
98
+ let text;
99
+ try {
100
+ text = readFileSync$1(gradle, "utf-8");
101
+ } catch {
102
+ return null;
103
+ }
104
+ const nsId = text.match(/namespace\s+["']([^"']+)["']/)?.[1];
105
+ if (nsId) return nsId;
106
+ const appId = text.match(/applicationId\s+["']([^"']+)["']/)?.[1];
107
+ if (appId) return appId;
108
+ return null;
109
+ }
110
+ //#endregion
21
111
  //#region src/spawn-entry.ts
22
112
  const DEV_ENTRIES = {
23
113
  "supervisor-run": "./supervisor/run.ts",
@@ -25,8 +115,8 @@ const DEV_ENTRIES = {
25
115
  };
26
116
  function spawnEntry(name) {
27
117
  const here = fileURLToPath(import.meta.url);
28
- if (basename(dirname(here)) === "src") return fileURLToPath(new URL(DEV_ENTRIES[name], import.meta.url));
29
- return join(dirname(here), `${name}.mjs`);
118
+ if (basename$1(dirname$1(here)) === "src") return fileURLToPath(new URL(DEV_ENTRIES[name], import.meta.url));
119
+ return join$1(dirname$1(here), `${name}.mjs`);
30
120
  }
31
121
  //#endregion
32
122
  //#region src/commands/start.ts
@@ -269,9 +359,10 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
269
359
  if (tunnel) note(chalk.dim("note requesting an Expo tunnel for this workspace's dev server"));
270
360
  const managedRemote = remote && !tunnel && !publicUrl && tunnelMode !== "off";
271
361
  const runStart = async () => {
362
+ const appIds = detectAppIds(root);
272
363
  upsertProject(root, {
273
- bundleId: detectBundleId(root) ?? void 0,
274
- androidPackage: detectAndroidPackage(root) ?? void 0,
364
+ bundleId: appIds.bundleId ?? void 0,
365
+ androidPackage: appIds.androidPackage ?? void 0,
275
366
  isExpo
276
367
  });
277
368
  const gateBudget = async () => {
@@ -819,7 +910,7 @@ async function startDevServer({ root, settings, waitSeconds = DEFAULT_WAIT_SECON
819
910
  };
820
911
  const startLocked = async () => {
821
912
  try {
822
- return await withWorkspaceProcessLock(dirname(workspaceLogsDir(root)), "metro-start", runStart, { external: true });
913
+ return await withWorkspaceProcessLock(dirname$1(workspaceLogsDir(root)), "metro-start", runStart, { external: true });
823
914
  } catch (error) {
824
915
  if (workspaceProcessLockError(error) !== "timeout") throw error;
825
916
  return fail({
@@ -935,4 +1026,4 @@ function report({ json, out, facts, waited, reclaimed }) {
935
1026
  out(chalk.dim(phaseLine("logs", facts.logsDir)));
936
1027
  }
937
1028
  //#endregion
938
- export { registerStart as a, startFacts as c, wantsExpoOwnTunnel as d, spawnEntry as f, readLogTail as i, supervisorEntry as l, liveSupervisor as n, startCommand as o, parseWait as r, startDevServer as s, failureEvidence as t, tailLines as u };
1029
+ export { registerStart as a, startFacts as c, wantsExpoOwnTunnel as d, spawnEntry as f, detectBundleId as h, readLogTail as i, supervisorEntry as l, detectAppIds as m, liveSupervisor as n, startCommand as o, detectAndroidPackage as p, parseWait as r, startDevServer as s, failureEvidence as t, tailLines as u };