stim 1.15.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.
- package/dist/{activity-B2-kb7k0.mjs → activity-IRvJY_ru.mjs} +49 -16
- package/dist/agent-device-usage-output-BK41vnzN.mjs +411 -0
- package/dist/{android-Cu_L4RpL.mjs → android--GorydsK.mjs} +692 -321
- package/dist/{android-Db2eEMaf.mjs → android-B_YNZy61.mjs} +25 -60
- package/dist/{android-CYmq0jNi.mjs → android-DE4v59Mb.mjs} +12 -3
- package/dist/{android-cas-PnKfAdB2.mjs → android-cas-ZFpjJBL_.mjs} +6 -5
- package/dist/{app-install-CAAplXAt.mjs → app-install-VPkI5WEg.mjs} +52 -8
- package/dist/attempt-Bk2ouQVL.mjs +39 -0
- package/dist/{budget-CMiqghig.mjs → budget-DM984mnd.mjs} +372 -366
- package/dist/{build-plan-aeGMFX7e.mjs → build-plan-CmQCoyT-.mjs} +346 -88
- package/dist/{build-slots-Df1ApSuH.mjs → build-slots-41v_d2x9.mjs} +36 -21
- package/dist/{cli-0cX5s4NM.mjs → cli-Daxug5-K.mjs} +23 -17
- package/dist/cli.mjs +1 -1
- package/dist/collector-run.mjs +5 -192
- package/dist/{command-output-CliHHn7a.mjs → command-output-CDAIFD-8.mjs} +3 -3
- package/dist/{created-devices-BwVb3liH.mjs → created-devices-CpvzBL25.mjs} +1 -1
- package/dist/{dependency-state-BmsjKZ2H.mjs → dependency-state-CLcBHH2M.mjs} +1 -1
- package/dist/{deps-BFlKbOIp.mjs → deps-DNWaQs4V.mjs} +2 -2
- package/dist/{dev-client-CjzLoNs0.mjs → dev-client-B-XC4uRH.mjs} +3 -40
- package/dist/{device-BZ1IyS5A.mjs → device-C8IOoJPv.mjs} +7 -6
- package/dist/device-capacity-D0GWHnTM.mjs +580 -0
- package/dist/device-host-worker.mjs +587 -110
- package/dist/{device-ios-DMQBUPus.mjs → device-ios-Bc7vbGwl.mjs} +75 -44
- package/dist/{device-lease-W5Pi1q38.mjs → device-lease-DpoS36_u.mjs} +2 -2
- package/dist/{device-pool-B2r38YjS.mjs → device-lease-run-B8YltIH_.mjs} +5 -125
- package/dist/device-pool-3o7s5lo4.mjs +125 -0
- package/dist/{device-remote-9ulPsdhY.mjs → device-remote-hFSkpfQu.mjs} +13 -13
- package/dist/{doctor-BcE5xsBn.mjs → doctor-Cnk9d7Hm.mjs} +86 -59
- package/dist/{error-diagnostics-Cb0P1WLS.mjs → error-diagnostics-C-NMnhrk.mjs} +11 -15
- package/dist/{gc-BuL1Genr.mjs → gc-DxCL2A46.mjs} +478 -33
- package/dist/{gradle-CmdOBq1d.mjs → gradle-D7-L2Th3.mjs} +4 -4
- package/dist/{guide-cCSGRFd3.mjs → guide-DjrTQGik.mjs} +2198 -566
- package/dist/{guide-status-CIoWK6mG.mjs → guide-status-Du-ZQ3FD.mjs} +1 -1
- package/dist/hosted-android-DwLS4Mse.mjs +32 -0
- package/dist/hosted-client-wk1W_CNW.mjs +196 -0
- package/dist/hosted-ios-aMo7Cljf.mjs +15 -0
- package/dist/hosted-logs-DqYmWtXc.mjs +187 -0
- package/dist/hosted-macos-dlHXEeF5.mjs +135 -0
- package/dist/hosted-native-C9xJ9rpA.mjs +255 -0
- package/dist/{build-progress-C2Jv1AtA.mjs → idle-B2m-Ovod.mjs} +300 -9
- package/dist/{idle-shutdown-B5LPaNyY.mjs → idle-shutdown-_3oOV1V_.mjs} +25 -17
- package/dist/{in-use-CndPMilN.mjs → in-use-CvGOUn-x.mjs} +30 -10
- package/dist/ios-Cr1h5gnO.mjs +189 -0
- package/dist/{ios-Chob8ny7.mjs → ios-DE6duQoz.mjs} +819 -460
- package/dist/{ios-3CtCRhwr.mjs → ios-DSfnPfse.mjs} +4 -4
- package/dist/{ios-device-DERmMmk-.mjs → ios-device-pAN4t54-.mjs} +1 -1
- package/dist/ios-state-8L3lgum6.mjs +89 -0
- package/dist/{launch-verify-CLlofxRt.mjs → launch-verify-lTnWZikR.mjs} +3 -3
- package/dist/logs-DUoKlDoS.mjs +384 -0
- package/dist/{client-C2IS36Qf.mjs → machines-C2XiNwh9.mjs} +330 -37
- package/dist/{macos-BwtbUtTN.mjs → macos-32po3kUq.mjs} +23 -18
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.d.mts +4 -0
- package/dist/maintenance-run.mjs +254 -0
- package/dist/{metro-Di8EN2Tb.mjs → metro-g8NqiJp-.mjs} +1 -1
- package/dist/metro-gateway-DmslUM30.mjs +103 -0
- package/dist/{named-ports-Dj_XBNaL.mjs → named-ports-C8m8EQs2.mjs} +2 -2
- package/dist/{native-run-DFDoIRbY.mjs → native-run-D1Q56rAD.mjs} +17 -6
- package/dist/{native-runtime-i6OkVg-w.mjs → native-runtime-DhJI3IVQ.mjs} +6 -6
- package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
- package/dist/offload-worker.d.mts +1 -0
- package/dist/offload-worker.mjs +13 -8
- package/dist/{teardown-BYuQafiA.mjs → ownership-BMLqyBgJ.mjs} +639 -53
- package/dist/{ownership-BkYxUtuj.mjs → ownership-DyRQru0q.mjs} +2 -2
- package/dist/{ports-DddwttKM.mjs → ports-4nYqxPWC.mjs} +2 -2
- package/dist/{prebuild-BvxDlD6C.mjs → prebuild-D7RUMkwN.mjs} +4 -4
- package/dist/preview-CxPRIEht.mjs +318 -0
- package/dist/{project-CV3tDFHl.mjs → project-D47Rh0ly.mjs} +72 -3
- package/dist/{recordings-GEXp4iBT.mjs → recordings-BNgsD1Tr.mjs} +1 -1
- package/dist/{reload-ChPiwvot.mjs → reload-qZei6iWn.mjs} +77 -13
- package/dist/{remote-cache-CO2elm_b.mjs → remote-cache-Ci4DQYYj.mjs} +4 -4
- package/dist/{run-B-xqRrsg.mjs → run-CkE9y0nc.mjs} +2 -2
- package/dist/{server-bare-X7PUgizL.mjs → server-bare-BkrSxSsZ.mjs} +1 -1
- package/dist/{server-expo-DhtX6bom.mjs → server-expo-CHTyXD0G.mjs} +4 -4
- package/dist/server-expo-CeakhI8P.mjs +2 -0
- package/dist/settings-BBpMcDbD.mjs +25 -0
- package/dist/settings-Cfd-0r2d.mjs +1440 -0
- package/dist/{settings-DbL6uhH8.mjs → settings-SR9OmW9h.mjs} +10 -4
- package/dist/settings.schema.json +472 -50
- package/dist/{simslim-Ciluxlq4.mjs → simslim-BWNrc3M3.mjs} +3 -3
- package/dist/{slot-launch-BY0BrOLh.mjs → slot-launch-DmjBmpQV.mjs} +10 -7
- package/dist/{spawn-entry-DncpJvWL.mjs → spawn-entry-jw19m8Nl.mjs} +1 -0
- package/dist/{start-CPO4wRg7.mjs → start-BopZcr4n.mjs} +1 -1
- package/dist/{start-CUD9nasA.mjs → start-uee1wWjY.mjs} +21 -17
- package/dist/{state-CaeaHbDA.mjs → state-BKa8wNN5.mjs} +1 -1
- package/dist/{state-BxCgeTQu.mjs → state-DyaNTgfR.mjs} +2 -2
- package/dist/{state-DFm77wRn.mjs → state-PeQiJuIu.mjs} +5 -5
- package/dist/{stats-DBLLFmKd.mjs → stats-Bq4fJOdB.mjs} +19 -5
- package/dist/{status-l84Gbvp2.mjs → status-CzS4bPO9.mjs} +5 -3
- package/dist/status-DlKvVJBE.mjs +49 -0
- package/dist/{status-DYXxNOT0.mjs → status-dXX2JX1C.mjs} +244 -50
- package/dist/{stop-CceYh1yG.mjs → stop-BCwvvs7F.mjs} +2 -2
- package/dist/{stop-Bad61z_2.mjs → stop-BjizrQwv.mjs} +83 -21
- package/dist/{stop-Bvf1MflS.mjs → stop-Cr1d8DSE.mjs} +4 -4
- package/dist/stop-cause-rhmGfwvt.mjs +97 -0
- package/dist/supervisor-run.d.mts +7 -4
- package/dist/supervisor-run.mjs +156 -27
- package/dist/{support-WLOzVKUv.mjs → support-BJPgYCu-.mjs} +61 -11
- package/dist/{support-CDQbDkL5.mjs → support-F9-sr0Oi.mjs} +6 -5
- package/dist/{toolchain-DmeYjXD9.mjs → toolchain-CNIBTIOv.mjs} +11 -11
- package/dist/trigger-Bemu8ytw.mjs +65 -0
- package/dist/trigger-CXts6qt1.mjs +2 -0
- package/dist/{warm-progress-DVHLN4uC.mjs → warm-progress-DF4JRvYI.mjs} +274 -5
- package/dist/watchman-APYjXSQa.mjs +48 -0
- package/dist/{web-k6w8-HnE.mjs → web-exQtGkoc.mjs} +14 -16
- package/dist/web-run.mjs +5 -5
- package/dist/{workspace-process-lock-BL7Q0HCO.mjs → workspace-process-lock-2mNzOlaB.mjs} +3 -2
- package/dist/{workspace-state-CL2V2Ons.mjs → workspace-state-DqcCbpE7.mjs} +34 -34
- package/dist/workspaces-Bcib97-6.mjs +306 -0
- package/dist/{worktree-BT_uULFx.mjs → worktree-Dc98cE6_.mjs} +1 -1
- package/dist/{worktree-qXKzADSC.mjs → worktree-ZpZR3S1k.mjs} +601 -33
- package/package.json +5 -5
- package/dist/device-capacity-DMuKC7nx.mjs +0 -136
- package/dist/hosted-macos-DwowNU7W.mjs +0 -447
- package/dist/logs-h9h6MUvE.mjs +0 -568
- package/dist/machine-usage-D_YYMzfR.mjs +0 -283
- package/dist/machines-CYtOCqcj.mjs +0 -259
- package/dist/merge-state-BnxLNQiM.mjs +0 -272
- package/dist/server-expo-KJeD109I.mjs +0 -2
- package/dist/settings-CduVcoxj.mjs +0 -680
- package/dist/stop-cause-CZ_Mr-v9.mjs +0 -665
- package/dist/watchman-Bx0wceoF.mjs +0 -238
- package/dist/worktree-C0c5Udwa.mjs +0 -753
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
2
|
-
import { basename, dirname, join } from "node:path";
|
|
3
|
-
import { compiledHelper, configDir } from "@stim-cli/core";
|
|
4
|
-
import { MACHINE_OWNER_KINDS } from "@stim-cli/core/state";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
//#region src/footprint.ts
|
|
7
|
-
const SOURCE = "stim-footprint.swift";
|
|
8
|
-
const COMPILE_TIMEOUT_MS = 12e4;
|
|
9
|
-
const READ_TIMEOUT_MS = 5e3;
|
|
10
|
-
function helperSource() {
|
|
11
|
-
const here = fileURLToPath(import.meta.url);
|
|
12
|
-
if (basename(dirname(here)) === "src") return fileURLToPath(new URL(`../helper/${SOURCE}`, import.meta.url));
|
|
13
|
-
return join(dirname(here), SOURCE);
|
|
14
|
-
}
|
|
15
|
-
async function buildHelper() {
|
|
16
|
-
const exec = getExecutor();
|
|
17
|
-
if (!exec.runFileQuiet("xcode-select", ["-p"], { timeoutMs: READ_TIMEOUT_MS })) return null;
|
|
18
|
-
const source = helperSource();
|
|
19
|
-
try {
|
|
20
|
-
return await compiledHelper({
|
|
21
|
-
dir: join(configDir(), "helpers"),
|
|
22
|
-
name: "stim-footprint",
|
|
23
|
-
inputs: [source],
|
|
24
|
-
version: "",
|
|
25
|
-
compile: async (output) => {
|
|
26
|
-
await exec.runFileAsync("xcrun", [
|
|
27
|
-
"swiftc",
|
|
28
|
-
"-O",
|
|
29
|
-
"-swift-version",
|
|
30
|
-
"5",
|
|
31
|
-
"-o",
|
|
32
|
-
output,
|
|
33
|
-
source
|
|
34
|
-
], { timeoutMs: COMPILE_TIMEOUT_MS });
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
} catch {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
const RETRY_BACKOFF_INITIAL_MS = 1e4;
|
|
42
|
-
const RETRY_BACKOFF_MAX_MS = 6e5;
|
|
43
|
-
let built = null;
|
|
44
|
-
let failure = null;
|
|
45
|
-
/** The `stim-footprint` helper, compiled on first use. A success is kept for the process's life; a failure retries after a doubling backoff instead of being pinned forever. */
|
|
46
|
-
function footprintHelper() {
|
|
47
|
-
const home = configDir();
|
|
48
|
-
if (built?.home === home) return built.promise;
|
|
49
|
-
if (failure?.home === home && Date.now() < failure.nextRetryAt) return Promise.resolve(null);
|
|
50
|
-
const promise = buildHelper().then((helper) => {
|
|
51
|
-
if (helper === null) {
|
|
52
|
-
const backoffMs = failure?.home === home ? Math.min(failure.backoffMs * 2, RETRY_BACKOFF_MAX_MS) : RETRY_BACKOFF_INITIAL_MS;
|
|
53
|
-
failure = {
|
|
54
|
-
home,
|
|
55
|
-
nextRetryAt: Date.now() + backoffMs,
|
|
56
|
-
backoffMs
|
|
57
|
-
};
|
|
58
|
-
if (built?.promise === promise) built = null;
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
return helper;
|
|
62
|
-
});
|
|
63
|
-
built = {
|
|
64
|
-
home,
|
|
65
|
-
promise
|
|
66
|
-
};
|
|
67
|
-
return promise;
|
|
68
|
-
}
|
|
69
|
-
/** Parses the helper's `<pid> <bytes>` lines into footprint bytes by pid. */
|
|
70
|
-
function parseFootprints(output) {
|
|
71
|
-
const footprints = /* @__PURE__ */ new Map();
|
|
72
|
-
for (const line of output.split("\n")) {
|
|
73
|
-
const match = /^(\d+) (\d+)$/.exec(line.trim());
|
|
74
|
-
if (match) footprints.set(Number(match[1]), Number(match[2]));
|
|
75
|
-
}
|
|
76
|
-
return footprints;
|
|
77
|
-
}
|
|
78
|
-
/** Every readable process's physical footprint in bytes, or null when the helper is unavailable or fails. */
|
|
79
|
-
async function readFootprints() {
|
|
80
|
-
const helper = await footprintHelper();
|
|
81
|
-
const output = helper ? getExecutor().runFileQuiet(helper, [], { timeoutMs: READ_TIMEOUT_MS }) : null;
|
|
82
|
-
return output === null ? null : parseFootprints(output);
|
|
83
|
-
}
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/machine-usage.ts
|
|
86
|
-
const SHARED = [
|
|
87
|
-
{
|
|
88
|
-
name: "CoreSimulator services",
|
|
89
|
-
pattern: /\/CoreSimulator\.framework\/|\/Simulator\.app\/Contents\/MacOS\//
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: "adb server",
|
|
93
|
-
pattern: /(^|\/)adb\s.*\bfork-server\b/
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: "Android emulator services",
|
|
97
|
-
pattern: /\/emulator\/netsimd(\s|$)/
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: "Gradle daemon",
|
|
101
|
-
pattern: /\borg\.gradle\.launcher\.daemon\.bootstrap\.GradleDaemon\b/
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
name: "Kotlin daemon",
|
|
105
|
-
pattern: /\bKotlinCompileDaemon\b/
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: "Watchman",
|
|
109
|
-
pattern: /(^|\/)watchman(\s|$)/
|
|
110
|
-
}
|
|
111
|
-
];
|
|
112
|
-
const STIM_SERVER_COMMAND = /(^|[\s/])stim-server(\.mjs)?(\s|$)/;
|
|
113
|
-
const LAUNCHD_SIM = /^launchd_sim\s.*\/Devices\/([^/\s]+)\//;
|
|
114
|
-
const MAX_DEPTH = 64;
|
|
115
|
-
const EMULATOR = /(?:^|\/)(?:qemu-system-[^\s/]+|emulator)(?:\s.*)?\s(?:-avd\s+|@)(\S+)/;
|
|
116
|
-
function emulatorAvd(command) {
|
|
117
|
-
return EMULATOR.exec(command)?.[1] ?? null;
|
|
118
|
-
}
|
|
119
|
-
function workspaceOwner(kind, env, name, id) {
|
|
120
|
-
return {
|
|
121
|
-
kind,
|
|
122
|
-
name,
|
|
123
|
-
workspace: env.path,
|
|
124
|
-
id,
|
|
125
|
-
owned: true
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
function rank(owner) {
|
|
129
|
-
return MACHINE_OWNER_KINDS.indexOf(owner.kind);
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Attributes every process in `processes` to at most one owner: the owner of its nearest ancestor (itself included)
|
|
133
|
-
* that is a root. Roots are each booted simulator's `launchd_sim`, each emulator launcher and qemu process, and each
|
|
134
|
-
* workspace's verified supervisor and Metro, running build and Chrome. Only a process under none of those falls back
|
|
135
|
-
* to stim-server or a machine-wide service in `SHARED`, so a `simctl` a workspace's log collector runs stays with the
|
|
136
|
-
* workspace. An emulator counts in the workspace that records its AVD whenever its process runs, including while adb
|
|
137
|
-
* does not list it. A process with no root ancestor is left out. `footprints` holds physical footprint bytes by pid,
|
|
138
|
-
* or is null when they could not be read, and the owners' `memoryMb` then sums resident sizes. A process missing from
|
|
139
|
-
* `footprints`, another user's or one that exited between the two reads, counts its resident size.
|
|
140
|
-
*/
|
|
141
|
-
function attributeMachineUsage({ processes, footprints, environments, roots, simNames }) {
|
|
142
|
-
const owners = /* @__PURE__ */ new Map();
|
|
143
|
-
const rootOf = /* @__PURE__ */ new Map();
|
|
144
|
-
const fallbackOf = /* @__PURE__ */ new Map();
|
|
145
|
-
const claim = (pid, key, owner, into = rootOf) => {
|
|
146
|
-
if (!owners.has(key)) owners.set(key, owner);
|
|
147
|
-
into.set(pid, key);
|
|
148
|
-
};
|
|
149
|
-
const sims = /* @__PURE__ */ new Map();
|
|
150
|
-
const emulators = /* @__PURE__ */ new Map();
|
|
151
|
-
for (const env of environments) for (const device of [{
|
|
152
|
-
slot: "default",
|
|
153
|
-
ios: env.ios,
|
|
154
|
-
android: env.android
|
|
155
|
-
}, ...env.slots ?? []]) {
|
|
156
|
-
const slot = device.slot === "default" ? {} : { slot: device.slot };
|
|
157
|
-
if (device.ios?.state === "Booted") sims.set(device.ios.udid.toUpperCase(), {
|
|
158
|
-
kind: "simulator",
|
|
159
|
-
name: device.ios.name ?? device.ios.udid,
|
|
160
|
-
workspace: env.path,
|
|
161
|
-
...slot,
|
|
162
|
-
id: device.ios.udid,
|
|
163
|
-
owned: device.ios.owned
|
|
164
|
-
});
|
|
165
|
-
if (device.android?.name && !device.android.physical) emulators.set(device.android.name, {
|
|
166
|
-
kind: "emulator",
|
|
167
|
-
name: device.android.name,
|
|
168
|
-
workspace: env.path,
|
|
169
|
-
...slot,
|
|
170
|
-
id: device.android.name,
|
|
171
|
-
owned: device.android.owned
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
for (const p of processes) {
|
|
175
|
-
const shared = SHARED.find(({ pattern }) => pattern.test(p.command));
|
|
176
|
-
if (shared) {
|
|
177
|
-
const owner = {
|
|
178
|
-
kind: "shared",
|
|
179
|
-
name: shared.name,
|
|
180
|
-
workspace: null,
|
|
181
|
-
id: null,
|
|
182
|
-
owned: false
|
|
183
|
-
};
|
|
184
|
-
claim(p.pid, `shared:${shared.name}`, owner, fallbackOf);
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (STIM_SERVER_COMMAND.test(p.command)) {
|
|
188
|
-
claim(p.pid, "server", {
|
|
189
|
-
kind: "server",
|
|
190
|
-
name: "stim-server",
|
|
191
|
-
workspace: null,
|
|
192
|
-
id: null,
|
|
193
|
-
owned: false
|
|
194
|
-
}, fallbackOf);
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
const udid = LAUNCHD_SIM.exec(p.command)?.[1]?.toUpperCase();
|
|
198
|
-
if (udid) {
|
|
199
|
-
const owner = sims.get(udid) ?? {
|
|
200
|
-
kind: "simulator",
|
|
201
|
-
name: simNames[udid] ?? udid,
|
|
202
|
-
workspace: null,
|
|
203
|
-
id: udid,
|
|
204
|
-
owned: false
|
|
205
|
-
};
|
|
206
|
-
claim(p.pid, `simulator:${udid}`, owner);
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
const avd = emulatorAvd(p.command);
|
|
210
|
-
if (avd) {
|
|
211
|
-
const owner = emulators.get(avd) ?? {
|
|
212
|
-
kind: "emulator",
|
|
213
|
-
name: avd,
|
|
214
|
-
workspace: null,
|
|
215
|
-
id: avd,
|
|
216
|
-
owned: false
|
|
217
|
-
};
|
|
218
|
-
claim(p.pid, `emulator:${avd}`, owner);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const present = new Set(processes.map((p) => p.pid));
|
|
222
|
-
const byPath = new Map(environments.map((env) => [env.path, env]));
|
|
223
|
-
for (const root of roots) {
|
|
224
|
-
const env = byPath.get(root.path);
|
|
225
|
-
if (!env) continue;
|
|
226
|
-
const metroPids = [root.supervisorPid, env.metro?.running ? env.metro.pid : null];
|
|
227
|
-
for (const pid of metroPids) if (pid && present.has(pid)) {
|
|
228
|
-
const port = env.metro?.port ?? null;
|
|
229
|
-
claim(pid, `metro:${env.path}`, workspaceOwner("metro", env, port ? `Metro :${port}` : "Metro", port ? String(port) : null));
|
|
230
|
-
}
|
|
231
|
-
if (root.build && present.has(root.build.pid)) {
|
|
232
|
-
const label = root.build.platform === "ios" ? "iOS build" : "Android build";
|
|
233
|
-
claim(root.build.pid, `build:${env.path}`, workspaceOwner("build", env, label, root.build.platform));
|
|
234
|
-
}
|
|
235
|
-
for (const pid of root.macosPids ?? []) if (present.has(pid)) claim(pid, `macos:${env.path}`, workspaceOwner("macos", env, env.macos?.product ?? "macOS app", null));
|
|
236
|
-
for (const pid of root.browserPids) if (present.has(pid)) claim(pid, `browser:${env.path}`, workspaceOwner("browser", env, "Chrome", null));
|
|
237
|
-
}
|
|
238
|
-
const parentOf = new Map(processes.map((p) => [p.pid, p.ppid]));
|
|
239
|
-
const totals = /* @__PURE__ */ new Map();
|
|
240
|
-
for (const p of processes) {
|
|
241
|
-
let pid = p.pid;
|
|
242
|
-
let key;
|
|
243
|
-
let fallback;
|
|
244
|
-
for (let depth = 0; pid !== void 0 && pid > 1 && depth < MAX_DEPTH; depth++) {
|
|
245
|
-
key = rootOf.get(pid);
|
|
246
|
-
if (key) break;
|
|
247
|
-
fallback ??= fallbackOf.get(pid);
|
|
248
|
-
pid = parentOf.get(pid);
|
|
249
|
-
}
|
|
250
|
-
key ??= fallback;
|
|
251
|
-
if (!key) continue;
|
|
252
|
-
const total = totals.get(key) ?? {
|
|
253
|
-
rssKb: 0,
|
|
254
|
-
memoryKb: 0,
|
|
255
|
-
cpu: 0,
|
|
256
|
-
processes: 0
|
|
257
|
-
};
|
|
258
|
-
total.rssKb += p.rssKb;
|
|
259
|
-
const footprint = footprints?.get(p.pid);
|
|
260
|
-
total.memoryKb += footprint === void 0 ? p.rssKb : footprint / 1024;
|
|
261
|
-
total.cpu += p.cpuPercent;
|
|
262
|
-
total.processes += 1;
|
|
263
|
-
totals.set(key, total);
|
|
264
|
-
}
|
|
265
|
-
const list = [];
|
|
266
|
-
for (const [key, owner] of owners) {
|
|
267
|
-
const total = totals.get(key);
|
|
268
|
-
if (total) list.push({
|
|
269
|
-
...owner,
|
|
270
|
-
cpuPercent: Math.round(total.cpu),
|
|
271
|
-
residentMb: Math.round(total.rssKb / 1024),
|
|
272
|
-
memoryMb: Math.round(total.memoryKb / 1024),
|
|
273
|
-
processes: total.processes
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
list.sort((x, y) => rank(x) - rank(y) || (x.workspace ?? "~").localeCompare(y.workspace ?? "~") || x.name.localeCompare(y.name));
|
|
277
|
-
return {
|
|
278
|
-
memorySource: footprints ? "footprint" : "rss",
|
|
279
|
-
owners: list
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
//#endregion
|
|
283
|
-
export { attributeMachineUsage as n, readFootprints as r, STIM_SERVER_COMMAND as t };
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import { _ as loadConfig$1, d as getConfigDir } from "./config-D0DbpZtM.mjs";
|
|
2
|
-
import { E as withDirLock } from "./workspace-state-CL2V2Ons.mjs";
|
|
3
|
-
import { _ as findPeer, b as realIo, g as endpoint, v as parseMachine$1 } from "./client-C2IS36Qf.mjs";
|
|
4
|
-
import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { acceptsShape, deviceHostMachinesClaims, deviceHostMachinesFile, deviceHostMachinesLock, isJsonObject, readDeviceHostMachines, settingDefinition } from "@stim-cli/core/state";
|
|
6
|
-
import { releaseClaim, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
|
|
7
|
-
import { hostname } from "node:os";
|
|
8
|
-
//#region src/device-host/machines.ts
|
|
9
|
-
function note(title, detail, fix = null) {
|
|
10
|
-
return {
|
|
11
|
-
code: "device-host-machine",
|
|
12
|
-
level: "note",
|
|
13
|
-
title,
|
|
14
|
-
detail,
|
|
15
|
-
fix
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
const approval = (machine, id) => `A person on ${machine} approves it with \`stim-server devices grant ${id} --device-host\`.`;
|
|
19
|
-
function configuredMachines() {
|
|
20
|
-
const hosting = loadConfig$1()?.hosting;
|
|
21
|
-
if (hosting !== void 0 && !isJsonObject(hosting)) return null;
|
|
22
|
-
const machines = hosting?.machines;
|
|
23
|
-
if (machines === void 0) return [];
|
|
24
|
-
return acceptsShape(settingDefinition("hosting.machines"), machines) ? machines : null;
|
|
25
|
-
}
|
|
26
|
-
function store(machines) {
|
|
27
|
-
withDirLock(deviceHostMachinesLock(), () => {
|
|
28
|
-
const file = deviceHostMachinesFile();
|
|
29
|
-
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
30
|
-
try {
|
|
31
|
-
writeFileSync(tmp, `${JSON.stringify({
|
|
32
|
-
version: 1,
|
|
33
|
-
machines
|
|
34
|
-
}, null, 2)}\n`, { mode: 384 });
|
|
35
|
-
renameSync(tmp, file);
|
|
36
|
-
} finally {
|
|
37
|
-
rmSync(tmp, { force: true });
|
|
38
|
-
}
|
|
39
|
-
}, { ensureParent: () => mkdirSync(getConfigDir(), { recursive: true }) });
|
|
40
|
-
}
|
|
41
|
-
async function request(machine, peer, port, deviceName, io) {
|
|
42
|
-
const reply = await io.hello(endpoint(peer, port), {
|
|
43
|
-
request: "device-host",
|
|
44
|
-
deviceName
|
|
45
|
-
});
|
|
46
|
-
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 {
|
|
47
|
-
report: {
|
|
48
|
-
machine,
|
|
49
|
-
dnsName: peer.dnsName,
|
|
50
|
-
state: "unreachable"
|
|
51
|
-
},
|
|
52
|
-
finding: note(`Could not ask ${machine} for hosting access`, "The worker did not return a pending request.")
|
|
53
|
-
};
|
|
54
|
-
const credential = {
|
|
55
|
-
machine,
|
|
56
|
-
nodeId: peer.nodeId,
|
|
57
|
-
dnsName: peer.dnsName,
|
|
58
|
-
deviceId: reply.result.device.id,
|
|
59
|
-
deviceToken: reply.result.deviceToken,
|
|
60
|
-
state: "pending",
|
|
61
|
-
requestedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
62
|
-
};
|
|
63
|
-
return {
|
|
64
|
-
report: {
|
|
65
|
-
machine,
|
|
66
|
-
dnsName: peer.dnsName,
|
|
67
|
-
state: "pending",
|
|
68
|
-
deviceId: credential.deviceId,
|
|
69
|
-
requestedAt: credential.requestedAt
|
|
70
|
-
},
|
|
71
|
-
finding: note(`Asked ${machine} for hosting access`, `The request lapses at ${reply.result.approval.expiresAt}.`, approval(machine, credential.deviceId)),
|
|
72
|
-
credential
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
/** Reports separately approved hosting machines. Only --fix asks or forgets credentials; every token stays pinned. */
|
|
76
|
-
async function inspectDeviceHostMachines({ fix }, io = realIo, entries = configuredMachines()) {
|
|
77
|
-
if (entries === null) return {
|
|
78
|
-
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.")],
|
|
79
|
-
machines: []
|
|
80
|
-
};
|
|
81
|
-
const unavailable = (state, finding) => ({
|
|
82
|
-
findings: [finding],
|
|
83
|
-
machines: entries.map((machine) => ({
|
|
84
|
-
machine,
|
|
85
|
-
state
|
|
86
|
-
}))
|
|
87
|
-
});
|
|
88
|
-
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."));
|
|
89
|
-
if (!entries.length) {
|
|
90
|
-
if (!fix) return {
|
|
91
|
-
findings: [],
|
|
92
|
-
machines: []
|
|
93
|
-
};
|
|
94
|
-
try {
|
|
95
|
-
if (!readDeviceHostMachines().length) return {
|
|
96
|
-
findings: [],
|
|
97
|
-
machines: []
|
|
98
|
-
};
|
|
99
|
-
} catch {
|
|
100
|
-
return unreadable();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
const claim = tryAcquireClaim({
|
|
104
|
-
root: deviceHostMachinesClaims(),
|
|
105
|
-
mode: "exclusive",
|
|
106
|
-
label: "hosting machine approval"
|
|
107
|
-
});
|
|
108
|
-
if (!claim.acquired) {
|
|
109
|
-
releaseClaim(claim.pending);
|
|
110
|
-
return unavailable("busy", note("Hosting machine approval is in use", "Another doctor run holds the hosting credential claim. Run doctor again after it finishes."));
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
let credentials;
|
|
114
|
-
try {
|
|
115
|
-
credentials = readDeviceHostMachines();
|
|
116
|
-
} catch {
|
|
117
|
-
return unreadable();
|
|
118
|
-
}
|
|
119
|
-
if (fix) {
|
|
120
|
-
const kept = credentials.filter((each) => entries.includes(each.machine));
|
|
121
|
-
if (kept.length !== credentials.length) {
|
|
122
|
-
store(kept);
|
|
123
|
-
credentials = kept;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (!entries.length) return {
|
|
127
|
-
findings: [],
|
|
128
|
-
machines: []
|
|
129
|
-
};
|
|
130
|
-
const status = io.status();
|
|
131
|
-
if (!isJsonObject(status)) return unavailable("tailscale-off", note("Hosting machines are unreachable", "hosting.machines names hosting machines, but Tailscale is not running."));
|
|
132
|
-
const self = isJsonObject(status.Self) ? status.Self : {};
|
|
133
|
-
const deviceName = (typeof self.HostName === "string" && self.HostName ? self.HostName : hostname()).replace(/[\p{Cc}\p{Cf}]/gu, "").trim().slice(0, 64) || "Mac";
|
|
134
|
-
const inspected = {
|
|
135
|
-
findings: [],
|
|
136
|
-
machines: []
|
|
137
|
-
};
|
|
138
|
-
const namedNodes = /* @__PURE__ */ new Set();
|
|
139
|
-
for (const machine of new Set(entries)) {
|
|
140
|
-
const parsed = parseMachine$1(machine);
|
|
141
|
-
if (!parsed) {
|
|
142
|
-
inspected.machines.push({
|
|
143
|
-
machine,
|
|
144
|
-
state: "invalid"
|
|
145
|
-
});
|
|
146
|
-
inspected.findings.push(note(`Hosting machine ${machine} is not a tailnet name`, "Expected `name` or `name:port`."));
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
const current = io.status();
|
|
150
|
-
if (!isJsonObject(current)) {
|
|
151
|
-
inspected.machines.push({
|
|
152
|
-
machine,
|
|
153
|
-
state: "tailscale-off"
|
|
154
|
-
});
|
|
155
|
-
inspected.findings.push(note(`Hosting machine ${machine} is unreachable`, "Tailscale stopped before this connection."));
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
const peer = findPeer(current, parsed.name);
|
|
159
|
-
if (peer === "missing" || peer === "ambiguous") {
|
|
160
|
-
inspected.machines.push({
|
|
161
|
-
machine,
|
|
162
|
-
state: "not-on-tailnet"
|
|
163
|
-
});
|
|
164
|
-
inspected.findings.push(note(`Hosting machine ${machine} is not on this tailnet`, peer === "missing" ? "No peer has that name." : "Several peers match that name."));
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
const nodeRoute = `${peer.nodeId}:${parsed.port}`;
|
|
168
|
-
if (namedNodes.has(nodeRoute)) {
|
|
169
|
-
inspected.machines.push({
|
|
170
|
-
machine,
|
|
171
|
-
dnsName: peer.dnsName,
|
|
172
|
-
state: "invalid"
|
|
173
|
-
});
|
|
174
|
-
inspected.findings.push(note(`Hosting machine ${machine} repeats a named node`, "Name each tailnet node and serve port only once in hosting.machines."));
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
namedNodes.add(nodeRoute);
|
|
178
|
-
const credential = credentials.find((each) => each.machine === machine);
|
|
179
|
-
const known = {
|
|
180
|
-
machine,
|
|
181
|
-
dnsName: peer.dnsName,
|
|
182
|
-
...credential ? {
|
|
183
|
-
deviceId: credential.deviceId,
|
|
184
|
-
requestedAt: credential.requestedAt
|
|
185
|
-
} : {}
|
|
186
|
-
};
|
|
187
|
-
if (credential && credential.nodeId !== peer.nodeId) {
|
|
188
|
-
inspected.machines.push({
|
|
189
|
-
...known,
|
|
190
|
-
state: "node-changed"
|
|
191
|
-
});
|
|
192
|
-
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.`));
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
let ask = !credential && fix;
|
|
196
|
-
if (credential) {
|
|
197
|
-
const reply = await io.hello(endpoint(peer, parsed.port), { deviceToken: credential.deviceToken });
|
|
198
|
-
if ("result" in reply && reply.result?.device?.id === credential.deviceId && Array.isArray(reply.result.capabilities) && reply.result.capabilities.includes("device-host")) {
|
|
199
|
-
if (credential.state !== "approved") {
|
|
200
|
-
credentials = credentials.map((each) => each.machine === machine ? {
|
|
201
|
-
...each,
|
|
202
|
-
state: "approved"
|
|
203
|
-
} : each);
|
|
204
|
-
store(credentials);
|
|
205
|
-
}
|
|
206
|
-
const host = reply.result.host;
|
|
207
|
-
if (host && (host.screenRecording === false || host.accessibility === false)) {
|
|
208
|
-
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)"] : []];
|
|
209
|
-
const detail = [...host.screenRecording === false ? ["Viewing hosted macOS apps needs Screen Recording."] : [], ...host.accessibility === false ? ["Controlling them needs the control permission."] : []];
|
|
210
|
-
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.`));
|
|
211
|
-
}
|
|
212
|
-
inspected.machines.push({
|
|
213
|
-
...known,
|
|
214
|
-
state: "approved"
|
|
215
|
-
});
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
if ("error" in reply && reply.error?.code === "approval-pending") {
|
|
219
|
-
inspected.machines.push({
|
|
220
|
-
...known,
|
|
221
|
-
state: "pending"
|
|
222
|
-
});
|
|
223
|
-
inspected.findings.push(note(`Hosting machine ${machine} has not approved this Mac yet`, `Requested at ${credential.requestedAt}.`, approval(machine, credential.deviceId)));
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
const revoked = "error" in reply && reply.error?.code === "unauthorized";
|
|
227
|
-
ask = revoked && fix;
|
|
228
|
-
if (!ask) {
|
|
229
|
-
inspected.machines.push({
|
|
230
|
-
...known,
|
|
231
|
-
state: revoked ? "revoked" : "unreachable"
|
|
232
|
-
});
|
|
233
|
-
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.`));
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
if (ask) {
|
|
238
|
-
const result = await request(machine, peer, parsed.port, deviceName, io);
|
|
239
|
-
if (result.credential) {
|
|
240
|
-
credentials = [...credentials.filter((each) => each.machine !== machine), result.credential];
|
|
241
|
-
store(credentials);
|
|
242
|
-
}
|
|
243
|
-
inspected.machines.push(result.report);
|
|
244
|
-
inspected.findings.push(result.finding);
|
|
245
|
-
} else {
|
|
246
|
-
inspected.machines.push({
|
|
247
|
-
...known,
|
|
248
|
-
state: "not-asked"
|
|
249
|
-
});
|
|
250
|
-
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."));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return inspected;
|
|
254
|
-
} finally {
|
|
255
|
-
releaseClaim(claim.acquired);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
//#endregion
|
|
259
|
-
export { inspectDeviceHostMachines as n, configuredMachines as t };
|