stim 1.25.0 → 1.27.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/README.md +1 -1
- package/dist/{acquire-artifact-zA4S7RHE.mjs → acquire-artifact-BKN2Yuhg.mjs} +2 -2
- package/dist/{act-BbHS3oV8.mjs → act-ROviTF_t.mjs} +415 -118
- package/dist/{activity-BpjwrQXU.mjs → activity-Cd-ysFes.mjs} +6 -7
- package/dist/{agent-device-usage-output-Bnkerfod.mjs → agent-device-usage-output-C5nCKWp9.mjs} +5 -5
- package/dist/{android-CNZgRC-4.mjs → android--HBgpvpW.mjs} +5 -5
- package/dist/{android-BGwuK3Mc.mjs → android-Cd6w7_qw.mjs} +134 -57
- package/dist/{android-saBeB4lr.mjs → android-Deb9X3_Y.mjs} +37 -15
- package/dist/{android-Cjcj8MCw.mjs → android-DriNoAVy.mjs} +99 -47
- package/dist/{android-cas-BVqh2Zep.mjs → android-cas-D5e6ecUg.mjs} +4 -4
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{android-project-CTYaETc0.d.mts → android-project-DUGMbq28.d.mts} +55 -1
- package/dist/api.d.mts +20 -7
- package/dist/api.mjs +42 -32
- package/dist/apk-signed-tail-BHQHx3Rm.mjs +142 -0
- package/dist/{artifact-Co8zixVx.mjs → artifact-0AGJYB9O.mjs} +28 -28
- package/dist/{artifact-DN8JNwqS.mjs → artifact-faemIZv5.mjs} +40 -30
- package/dist/{artifact-platform-CMRc0M_r.mjs → artifact-platform-BO2Uvk_A.mjs} +1 -1
- package/dist/{attempt-DW-mQqM7.mjs → attempt-ClyJlZPT.mjs} +4 -2
- package/dist/auto-placement-z-H5pJza.mjs +350 -0
- package/dist/{browser-web-CCgZsHmI.mjs → browser-web-slD8AvLp.mjs} +16 -17
- package/dist/{build-cache-CKCzxzxA.mjs → build-cache-gBJ-WKzA.mjs} +141 -37
- package/dist/{build-CAiw8Ezm.mjs → build-hS0QyFlQ.mjs} +38 -31
- package/dist/{build-plan-zCuHGlA8.mjs → build-plan-CBqYbzYM.mjs} +7 -6
- package/dist/{cache-manifest-BPXRONJz.mjs → cache-manifest-DEjqGGat.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{budget-HyShrIpL.mjs → ccache-BWTze1Ls.mjs} +634 -1514
- package/dist/{cdp-BG3FJCCx.mjs → cdp-C3svvXiE.mjs} +1 -1
- package/dist/{chrome-BvV8ssI3.mjs → chrome-DyDWurWG.mjs} +1 -1
- package/dist/{cli-C4w607Pz.mjs → cli-ftXDLto1.mjs} +24 -23
- package/dist/cli.mjs +1 -1
- package/dist/{client-BQsmyp_-.mjs → client-xsdxrhWA.mjs} +77 -116
- package/dist/collector-run.d.mts +10 -9
- package/dist/collector-run.mjs +42 -27
- package/dist/{command-output-BBvS1H6v.mjs → command-output-Dj-WT4BP.mjs} +1 -1
- package/dist/{config-BZhXY15H.mjs → config-Cn94Jc0g.mjs} +343 -18
- package/dist/{launch-DscF1QfM.mjs → dependencies-BGokZ-Ng.mjs} +330 -279
- package/dist/{dependency-state-BwNCYG5Q.mjs → dependency-state-C3OtgG94.mjs} +14 -7
- package/dist/{detached-entry-KmieOKgN.mjs → detached-entry-BPlSaCwC.mjs} +1 -1
- package/dist/{dev-client-CHoXXZeg.mjs → dev-client-BMca82xo.mjs} +2 -2
- package/dist/{device-F5-4z9ef.mjs → device-BJm0irrq.mjs} +16 -16
- package/dist/{device-capacity-ZYaRALv5.mjs → device-capacity-6LP7QsaA.mjs} +103 -25
- package/dist/device-host-worker.mjs +19 -21
- package/dist/{device-ios-DkHBrmvH.mjs → device-ios-534c14fz.mjs} +288 -28
- package/dist/{device-lease-run-ogEeJ62K.mjs → device-lease-run-DwsGfjUN.mjs} +5 -5
- package/dist/{device-pool-BIg8MU8l.mjs → device-pool-BnQqvs6o.mjs} +3 -3
- package/dist/{device-B9FqTBYW.mjs → device-tM41-TUq.mjs} +25 -24
- package/dist/{doctor-A_Nr_fDe.mjs → doctor-Dbx1ZpD2.mjs} +1 -1
- package/dist/{doctor-k3P3PizH.mjs → doctor-DyqE9n1J.mjs} +28 -16
- package/dist/{doctor-Bm8TVpGn.mjs → doctor-F-3VUUyN.mjs} +128 -35
- package/dist/{doctor-fingerprint-zlujA1Gs.mjs → doctor-fingerprint-o7ZI9YLn.mjs} +9 -3
- package/dist/doctor-fingerprint.mjs +1 -1
- package/dist/{doctor-watchman-BovI1y3-.mjs → doctor-watchman-CxDbyrrt.mjs} +5 -5
- package/dist/{eas-build-CyDKNTBP.mjs → eas-build-CjNSWmjo.mjs} +4 -4
- package/dist/eas-fallback-9fqb46kW.mjs +87 -0
- package/dist/{eas-session-ledger-BMCA1gdO.mjs → eas-session-ledger-DrP4OuuM.mjs} +6 -6
- package/dist/{error-diagnostics-B1Mj_-7h.mjs → error-diagnostics-snej9k4j.mjs} +51 -25
- package/dist/{exec-VFRpu1iA.mjs → exec-aULwtNly.mjs} +57 -6
- package/dist/{gc-eaz403W6.mjs → gc-BRwAY3qT.mjs} +2 -2
- package/dist/{gc-B9jcnmIk.mjs → gc-CGZ1Yg88.mjs} +218 -72
- package/dist/{guide-DR0K0xW9.mjs → guide-_rfEh_zM.mjs} +839 -222
- package/dist/{hosted-android-Bvb2-ZxD.mjs → hosted-android-BMKzUNnY.mjs} +4 -4
- package/dist/{hosted-client-rsRqysJc.mjs → hosted-client-D7Pk_Oo1.mjs} +13 -9
- package/dist/{hosted-logs-CWKGYWNX.mjs → hosted-logs-B1gqQuva.mjs} +18 -12
- package/dist/hosted-logs-sync-Dg3qrZmV.mjs +195 -0
- package/dist/{hosted-macos-d0pv9Tth.mjs → hosted-macos-B9w4H8hc.mjs} +6 -6
- package/dist/{hosted-native-BxTizGU2.mjs → hosted-native-CdgaiuyP.mjs} +17 -17
- package/dist/{idle-shutdown-BIyfl0Zj.mjs → idle-shutdown-Cbvo5KEh.mjs} +21 -9
- package/dist/{ios-BRVM8b0P.mjs → ios-B5HlVMhQ.mjs} +138 -55
- package/dist/{ios-Dvt97HgQ.mjs → ios-Bk4mWbtS.mjs} +8 -8
- package/dist/{ios-ENC4sRPA.mjs → ios-CsRVik64.mjs} +7 -1
- package/dist/{ios-BQzvJlzw.mjs → ios-E26qSpkb.mjs} +94 -11
- package/dist/{ios-device-CKSvk0bu.mjs → ios-device-C_QkIf1Z.mjs} +3 -3
- package/dist/{ios-device-B6HK951C.mjs → ios-device-CiBA-er9.mjs} +1 -1
- package/dist/{ios-project-DCUDo06I.mjs → ios-project-D8Jcy_br.mjs} +22 -9
- package/dist/{ios-signing-4c_qXvQm.mjs → ios-signing-BJzvDPfU.mjs} +2 -2
- package/dist/{ios-state-CT9nV5SP.mjs → ios-state-BZ03NFgF.mjs} +1 -1
- package/dist/{js-swap-Z9HbC4cj.mjs → js-swap-Dg2a945i.mjs} +8 -7
- package/dist/{launch-D0lPvFFu.mjs → launch-CRyBeNsA.mjs} +53 -44
- package/dist/lightened-BCsdAGj_.mjs +171 -0
- package/dist/{logs-BsIRre8x.mjs → logs-CC_-ZzEE.mjs} +10 -174
- package/dist/{logs-Crs9y34Z.mjs → logs-aJQb-RDg.mjs} +112 -38
- package/dist/{macos-CcUlJPMu.mjs → macos-BGmTpnv9.mjs} +1 -1
- package/dist/{macos-D477nfzH.mjs → macos-DlCBhMw5.mjs} +73 -39
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +30 -13
- package/dist/{metro-1pJgCX4U.mjs → metro-CvyD-xwe.mjs} +4 -4
- package/dist/{metro-gateway-Bp9gJQAN.mjs → metro-gateway-BnHdqMO_.mjs} +3 -4
- package/dist/{named-ports-BVbn8SvC.mjs → named-ports-BCyMWA6b.mjs} +3 -3
- package/dist/{native-android-BvMizJn1.mjs → native-android-DlpxCyOP.mjs} +17 -18
- package/dist/{native-gradle-artifact-inputs-BJscK4vN.mjs → native-gradle-artifact-inputs-C13qT7R_.mjs} +5 -5
- package/dist/{native-runtime-B9w_Qyz3.mjs → native-runtime-DqaIBO5w.mjs} +9 -9
- package/dist/{native-source-D-4Dh4JU.mjs → native-source-D1AzWiuP.mjs} +1 -1
- package/dist/{native-xcode-inputs-Cv4l-obn.mjs → native-xcode-inputs-B1W8SrxB.mjs} +3 -3
- package/dist/{native-xcode-ios-DGrg90hE.mjs → native-xcode-ios-QPP0Rm68.mjs} +17 -18
- package/dist/{ndjson-CgE_o3Dl.mjs → ndjson-Dy2W4vRa.mjs} +2 -2
- package/dist/offload-worker.d.mts +3 -1
- package/dist/offload-worker.mjs +34 -25
- package/dist/{operation-exec-CUZifp6Z.mjs → operation-exec-Dp4FuIGR.mjs} +1 -1
- package/dist/{ownership-jxxz-19m.mjs → ownership-Bx-0cku-.mjs} +3 -3
- package/dist/{page-CzEA_-5_.mjs → page-CQJlcj3x.mjs} +2 -2
- package/dist/{phone-reverses-CYb7RRAx.mjs → phone-reverses-C9yAaKEv.mjs} +18 -4
- package/dist/{placement-log-DjOXJRIH.mjs → placement-log-BXUbiZM6.mjs} +3 -3
- package/dist/{plan-placement-BqKgKHCi.mjs → plan-placement-UxnQjv5Z.mjs} +36 -10
- package/dist/{ports-DtS1Amb0.mjs → ports-uUrghqWN.mjs} +3 -3
- package/dist/prebuild-BDpToDgR.mjs +1246 -0
- package/dist/{project-BiC68mIp.mjs → project-DYj-HVx6.mjs} +17 -18
- package/dist/{pull-request-CbNlkFok.mjs → pull-request-BzLmzAIQ.mjs} +2 -2
- package/dist/pull-requests.mjs +1 -1
- package/dist/{react-native-android-DRYqoJc4.mjs → react-native-android-DDBccgJw.mjs} +56 -36
- package/dist/{react-native-build-CwZB-agu.mjs → react-native-build-DaK2IFyP.mjs} +9 -9
- package/dist/{react-native-doctor-BCrytK7h.mjs → react-native-doctor-B-Mw6DcR.mjs} +50 -9
- package/dist/{react-native-ios-Cr1buNFo.mjs → react-native-ios-DjYwUF9U.mjs} +45 -25
- package/dist/{recordings-BaoQ0t-J.mjs → recordings-C20iUZfp.mjs} +1 -1
- package/dist/{registration-CN6nlhcD.mjs → registration-BwCe64PH.mjs} +1 -1
- package/dist/{reload-Beftw2X3.mjs → reload-DJBeF_rC.mjs} +1 -1
- package/dist/{reload-FbSgMF8x.mjs → reload-fUdkH2_p.mjs} +16 -16
- package/dist/{remote-BuZj6AyU.mjs → remote-y6n99WMW.mjs} +22 -9
- package/dist/{result-CR2vk2cj.mjs → result-CTqBw2Te.mjs} +32 -21
- package/dist/{run-BtzE09Dl.mjs → run-DaCeNKRm.mjs} +3 -3
- package/dist/{selection-D5cxEl3t.mjs → selection-Bs8phwhN.mjs} +183 -62
- package/dist/{server-bare-CH9X0RLn.mjs → server-bare-BOh-iZEj.mjs} +2 -2
- package/dist/{server-command-CaBbDNU2.mjs → server-command-8CuA51Cv.mjs} +6 -6
- package/dist/{server-expo-Ck8EIidM.mjs → server-expo-ABQs2XFA.mjs} +4 -4
- package/dist/server-expo-Ba_Y21vB.mjs +2 -0
- package/dist/{settings-B5tCoq9N.mjs → settings-BDVI8Ivc.mjs} +331 -62
- package/dist/{settings-DzH9mGQZ.mjs → settings-CMHhQNwI.mjs} +24 -3
- package/dist/settings-payload-cm60Zhib.mjs +257 -0
- package/dist/settings-write-BdNRLlBn.mjs +185 -0
- package/dist/settings.schema.json +84 -39
- package/dist/{simslim-BjxszUAb.mjs → simslim-DCQwzoPy.mjs} +4 -3
- package/dist/{slot-launch-QeuJMlnJ.mjs → slot-launch-0l7RqSpr.mjs} +6 -6
- package/dist/{stage-D8xlRU2D.mjs → stage-F4cAfJGi.mjs} +2 -2
- package/dist/{start-C1JLjkNw.mjs → start-DcqnF9p8.mjs} +1 -1
- package/dist/{start-YMpNwLhh.mjs → start-DrPwER_2.mjs} +27 -23
- package/dist/{state-DMRNP7-4.mjs → state-BX1y5syV.mjs} +4 -5
- package/dist/{state-DPaM3u5K.mjs → state-CZxLyetD.mjs} +2 -3
- package/dist/{stats-CP7STzZw.mjs → stats-GxuCbles.mjs} +5 -5
- package/dist/{stats-report-n2U5plhk.mjs → stats-report-B86Cv7Uc.mjs} +1 -1
- package/dist/{status-Fect2w9R.mjs → status-7ULDEJ1e.mjs} +1 -1
- package/dist/{status-CRLiJ-O_.mjs → status-BfnK9pUd.mjs} +25 -26
- package/dist/{status-cNw6i0_t.mjs → status-Bj7ZYvvc.mjs} +1 -1
- package/dist/{status-watch-Bc2J4TZn.mjs → status-watch-BfabQn03.mjs} +5 -5
- package/dist/status-worker.mjs +1 -1
- package/dist/{stim-desktop-DS0mzR8U.mjs → stim-desktop-CbWo21kb.mjs} +2 -2
- package/dist/{stop-COP6PQqw.mjs → stop-CH79S3IZ.mjs} +5 -6
- package/dist/{stop-Db9Kh-UA.mjs → stop-D-EAr25B.mjs} +25 -22
- package/dist/{stop-Br7LKxBE.mjs → stop-x1Yu3He4.mjs} +2 -2
- package/dist/supervisor-run.d.mts +1 -0
- package/dist/supervisor-run.mjs +17 -18
- package/dist/{support-qKcbgmI5.mjs → support-Be0UeVZU.mjs} +10 -10
- package/dist/{support-Cxj9CmII.mjs → support-CNg_YsOk.mjs} +6 -6
- package/dist/{swiftpm-artifact-inputs-iY9fsmOc.mjs → swiftpm-artifact-inputs-uPqYvGBv.mjs} +4 -4
- package/dist/{swiftpm-macos-Bp_sn9P4.mjs → swiftpm-macos-Ch4YjY_A.mjs} +11 -12
- package/dist/toolchain-CbCG6C5Z.mjs +2413 -0
- package/dist/toolchain-JYAOquPg.mjs +2 -0
- package/dist/{trigger-CBMe2YLH.mjs → trigger-DMou-X7j.mjs} +3 -3
- package/dist/trigger-qp2ji5wE.mjs +2 -0
- package/dist/{warm-progress-BV3ZPmcY.mjs → warm-progress-C0RsbngN.mjs} +731 -728
- package/dist/{web-B4Yjyt1G.mjs → web-Cq_AQ4DK.mjs} +9 -10
- package/dist/web-Je6abcpz.mjs +2 -0
- package/dist/web-run.mjs +9 -9
- package/dist/windows-report.mjs +1 -1
- package/dist/{workspace-state-CPSp3mav.mjs → workspace-state-BjqdjnJR.mjs} +46 -8
- package/dist/{workspaces-C9w4aeXF.mjs → workspaces-DFbj07jp.mjs} +1801 -76
- package/dist/{worktree-CclFIe1Z.mjs → worktree-COAJ3-OD.mjs} +934 -511
- package/dist/{worktree-Ct9Gk3KL.mjs → worktree-JGUd_53L.mjs} +1 -1
- package/package.json +8 -8
- package/dist/ccache-h1FKhZeg.mjs +0 -143
- package/dist/created-devices-D2Aca-xu.mjs +0 -52
- package/dist/eas-fallback-gaZi-dJL.mjs +0 -381
- package/dist/hosted-ios-RtVcJH3u.mjs +0 -15
- package/dist/native-run-D5OJsGwA.mjs +0 -276
- package/dist/paths-DQtTgsBf.mjs +0 -42
- package/dist/prebuild-JUaqOLpc.mjs +0 -501
- package/dist/server-expo-C16Zf2k7.mjs +0 -2
- package/dist/settings-write-n0zwfohx.mjs +0 -360
- package/dist/toolchain-DB1aeqxO.mjs +0 -2
- package/dist/toolchain-VzLJzZ7O.mjs +0 -908
- package/dist/trigger-BLsHgAGR.mjs +0 -2
- package/dist/web-DS4UDWvf.mjs +0 -2
- package/dist/xcode-CiIIDnuD.mjs +0 -910
package/dist/collector-run.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { S as process_identity_exports } from "./exec-
|
|
2
|
-
import {
|
|
3
|
-
import { V as deviceSlotKey, q as validateDeviceSlot } from "./config-
|
|
1
|
+
import { S as process_identity_exports } from "./exec-aULwtNly.mjs";
|
|
2
|
+
import { k as workspaceLogsDir } from "./workspace-state-BjqdjnJR.mjs";
|
|
3
|
+
import { V as deviceSlotKey, q as validateDeviceSlot } from "./config-Cn94Jc0g.mjs";
|
|
4
4
|
import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
5
|
-
import { c as watchAppPid, i as parseLogcatLine, o as startAndroidLogcat, s as waitForAppPid, t as androidClockOffset } from "./android-
|
|
6
|
-
import {
|
|
7
|
-
import { a as startIosDeviceConsole, i as parseDeviceConsoleLine } from "./ios-device-
|
|
8
|
-
import { a as unregisterCollector, i as registerCollector, r as readCollectors, t as collectorProcessTitle } from "./ownership-
|
|
9
|
-
import { n as parseLogStreamLine, r as startIosLogStream, t as appNameFromBundleId } from "./ios-
|
|
5
|
+
import { c as watchAppPid, i as parseLogcatLine, o as startAndroidLogcat, s as waitForAppPid, t as androidClockOffset } from "./android-Deb9X3_Y.mjs";
|
|
6
|
+
import { i as createNdjsonWriter } from "./ndjson-Dy2W4vRa.mjs";
|
|
7
|
+
import { a as startIosDeviceConsole, i as parseDeviceConsoleLine } from "./ios-device-CiBA-er9.mjs";
|
|
8
|
+
import { a as unregisterCollector, i as registerCollector, r as readCollectors, t as collectorProcessTitle } from "./ownership-Bx-0cku-.mjs";
|
|
9
|
+
import { n as parseLogStreamLine, r as startIosLogStream, t as appNameFromBundleId } from "./ios-CsRVik64.mjs";
|
|
10
10
|
import { existsSync } from "node:fs";
|
|
11
11
|
import { isAbsolute, join, resolve } from "node:path";
|
|
12
12
|
import { LOG_ROTATE_BYTES } from "@stim-cli/core";
|
|
@@ -130,11 +130,13 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
130
130
|
return null;
|
|
131
131
|
}
|
|
132
132
|
let finished = false;
|
|
133
|
+
const shutdown = new AbortController();
|
|
133
134
|
let captured = 0;
|
|
134
135
|
let watcher = null;
|
|
135
136
|
const finish = (code, level, msg, event, { signalled = false } = {}) => {
|
|
136
137
|
if (finished) return;
|
|
137
138
|
finished = true;
|
|
139
|
+
shutdown.abort();
|
|
138
140
|
watcher?.stop();
|
|
139
141
|
if (physical && captured === 0 && !signalled) writer.write({
|
|
140
142
|
...attribution,
|
|
@@ -187,7 +189,8 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
187
189
|
}) : await waitForAppPid({
|
|
188
190
|
serial,
|
|
189
191
|
packageName,
|
|
190
|
-
timeoutMs: pidTimeoutMs
|
|
192
|
+
timeoutMs: pidTimeoutMs,
|
|
193
|
+
signal: shutdown.signal
|
|
191
194
|
});
|
|
192
195
|
if (!resolved.ok) {
|
|
193
196
|
finish(1, "error", `device log collector could not attach: ${resolved.reason}`, "collector_failed");
|
|
@@ -214,8 +217,11 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
214
217
|
})
|
|
215
218
|
});
|
|
216
219
|
const parse = platform === "ios" ? physical ? parseDeviceConsoleLine : parseLogStreamLine : parseLogcatLine;
|
|
217
|
-
const
|
|
218
|
-
|
|
220
|
+
const readClockOffset = async () => platform === "android" ? await resolveClockOffset(serial, {
|
|
221
|
+
now,
|
|
222
|
+
signal: shutdown.signal
|
|
223
|
+
}) : null;
|
|
224
|
+
const attach = (streamPid, clockOffsetMs) => {
|
|
219
225
|
if (platform === "android") writer.write({
|
|
220
226
|
...attribution,
|
|
221
227
|
src: "device",
|
|
@@ -284,17 +290,21 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
284
290
|
spawned.on("exit", (code, signal) => {
|
|
285
291
|
if (spawned !== child) return;
|
|
286
292
|
flushReaders();
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
293
|
+
const settle = (next) => {
|
|
294
|
+
if (spawned !== child) return;
|
|
295
|
+
if (next && next !== pid) {
|
|
296
|
+
reattach(next, "the log stream ended");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const how = signal ? `signal ${signal}` : `exit code ${code}`;
|
|
300
|
+
if (physical && code) {
|
|
301
|
+
finish(1, "error", `the devicectl console ended with ${how}; treat the run as failed`, "collector_failed");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
finish(0, "info", `device log stream ended (${how}); the app or device is gone`, "collector_stopped");
|
|
305
|
+
};
|
|
306
|
+
if (platform === "android" && watcher) watcher.probe().then(settle);
|
|
307
|
+
else settle(null);
|
|
298
308
|
});
|
|
299
309
|
spawned.on("error", (err) => {
|
|
300
310
|
if (spawned !== child) return;
|
|
@@ -302,12 +312,13 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
302
312
|
});
|
|
303
313
|
return spawned;
|
|
304
314
|
};
|
|
305
|
-
const reattach = (nextPid, why = "the app restarted") => {
|
|
306
|
-
if (finished) return;
|
|
315
|
+
const reattach = async (nextPid, why = "the app restarted") => {
|
|
316
|
+
if (finished || nextPid === pid) return;
|
|
307
317
|
const previous = pid;
|
|
308
318
|
flushReaders();
|
|
309
319
|
killChild(child);
|
|
310
320
|
child = null;
|
|
321
|
+
pid = nextPid;
|
|
311
322
|
writer.write({
|
|
312
323
|
...attribution,
|
|
313
324
|
src: "device",
|
|
@@ -316,15 +327,18 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
316
327
|
event: "collector_reattached",
|
|
317
328
|
msg: `${why}: ${packageName} is now pid ${nextPid} on ${serial} (was ${previous}); reattaching the log stream`
|
|
318
329
|
});
|
|
319
|
-
|
|
330
|
+
const clockOffsetMs = await readClockOffset();
|
|
331
|
+
if (finished || pid !== nextPid) return;
|
|
320
332
|
try {
|
|
321
|
-
child = attach(nextPid);
|
|
333
|
+
child = attach(nextPid, clockOffsetMs);
|
|
322
334
|
} catch (err) {
|
|
323
335
|
finish(1, "error", `device log collector could not reattach to pid ${nextPid}: ${describe(err)}`, "collector_failed");
|
|
324
336
|
}
|
|
325
337
|
};
|
|
338
|
+
const initialClockOffsetMs = await readClockOffset();
|
|
339
|
+
if (finished) return null;
|
|
326
340
|
try {
|
|
327
|
-
child = attach(pid);
|
|
341
|
+
child = attach(pid, initialClockOffsetMs);
|
|
328
342
|
} catch (err) {
|
|
329
343
|
finish(1, "error", `device log collector could not start: ${describe(err)}`, "collector_failed");
|
|
330
344
|
return null;
|
|
@@ -334,6 +348,7 @@ async function runCollector({ slot = "default", platform, root, udid = null, app
|
|
|
334
348
|
packageName,
|
|
335
349
|
pid,
|
|
336
350
|
intervalMs: pidWatchMs,
|
|
351
|
+
signal: shutdown.signal,
|
|
337
352
|
resolve: pidOf || void 0,
|
|
338
353
|
onChange: (nextPid) => reattach(nextPid)
|
|
339
354
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as stripAnsi } from "./process-output-Fpl8PYi8.mjs";
|
|
2
|
-
import { t as NO_PROJECT_REFUSAL } from "./project-
|
|
2
|
+
import { t as NO_PROJECT_REFUSAL } from "./project-DYj-HVx6.mjs";
|
|
3
3
|
import { quotedPath as quotedPath$1 } from "@stim-cli/core";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
//#region src/diagnostics/launch-error-preview.ts
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { C as reportSignal, D as ownership_claim_exports, S as process_identity_exports, T as trackReportResource, l as WorkspaceProcessCleanupError, t as getExecutor } from "./exec-
|
|
2
|
-
import { t as runOperationFile } from "./operation-exec-
|
|
1
|
+
import { C as reportSignal, D as ownership_claim_exports, S as process_identity_exports, T as trackReportResource, l as WorkspaceProcessCleanupError, t as getExecutor } from "./exec-aULwtNly.mjs";
|
|
2
|
+
import { t as runOperationFile } from "./operation-exec-Dp4FuIGR.mjs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { ambientEnv, configDir, ensurePrivateDir } from "@stim-cli/core";
|
|
5
|
-
import { allMetroPorts, cannotHost, clientIdentity, configCorruptRepair, endpoint, findEnclosingWorktreeRoot, findPeer as findPeer$1, getConcurrencyLimits, getConfigPath, getConfigPath as getConfigPath$1, getProject, getProjectSettings, getRepoSettings, isJsonObject, isPathPrefix, loadConfig, loadConfig as loadConfig$1, machineEndpoint, parseMachine, parseMachine as parseMachine$1, validateAutomaticPool, withConfigLock, withConfigLock as withConfigLock$1, withConfigLockAsync, withConfigLockAsync as withConfigLockAsync$1 } from "@stim-cli/core/state";
|
|
5
|
+
import { allMetroPorts, cannotHost, clientIdentity, configCorruptRepair, endpoint, endpoint as endpoint$1, findEnclosingWorktreeRoot, findPeer, findPeer as findPeer$1, getConcurrencyLimits, getConfigPath, getConfigPath as getConfigPath$1, getProject, getProjectSettings, getRepoSettings, isJsonObject, isPathPrefix as isPathPrefix$1, loadConfig, loadConfig as loadConfig$1, machineEndpoint, parseMachine, parseMachine as parseMachine$1, validateAutomaticPool, withConfigLock, withConfigLock as withConfigLock$1, withConfigLockAsync, withConfigLockAsync as withConfigLockAsync$1 } from "@stim-cli/core/state";
|
|
6
6
|
import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, normalize, parse, sep as sep$1 } from "path";
|
|
7
|
+
import { request } from "node:https";
|
|
7
8
|
import { WebSocket } from "ws";
|
|
8
9
|
import { HandshakeRefusal, MAX_SERVER_MESSAGE_BYTES, TRANSPORT_OVERHEAD_BYTES, decodeSecret, initiateNoise, newSecret } from "@stim-cli/core/noise-session";
|
|
9
10
|
import { existsSync, lstatSync, readdirSync, readlinkSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -23,19 +24,246 @@ function readHostPermissions(value) {
|
|
|
23
24
|
} : void 0;
|
|
24
25
|
}
|
|
25
26
|
//#endregion
|
|
27
|
+
//#region src/offload/diagnosis.ts
|
|
28
|
+
const SILENT_ERRORS = /* @__PURE__ */ new Set([
|
|
29
|
+
"ETIMEDOUT",
|
|
30
|
+
"EHOSTUNREACH",
|
|
31
|
+
"ENETUNREACH",
|
|
32
|
+
"EHOSTDOWN"
|
|
33
|
+
]);
|
|
34
|
+
function classifyTransport(error, status) {
|
|
35
|
+
if (status !== void 0) return {
|
|
36
|
+
kind: "http",
|
|
37
|
+
status
|
|
38
|
+
};
|
|
39
|
+
const code = error.code ?? "";
|
|
40
|
+
if (code === "ECONNREFUSED") return { kind: "refused" };
|
|
41
|
+
if (SILENT_ERRORS.has(code) || /handshake has timed out/i.test(error.message)) return { kind: "silent" };
|
|
42
|
+
if (/^(ERR_TLS_|CERT_|UNABLE_TO_|DEPTH_ZERO|SELF_SIGNED|ERR_SSL_)/.test(code) || /certificate|SSL|TLS/i.test(error.message)) return {
|
|
43
|
+
kind: "tls",
|
|
44
|
+
message: error.message
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
kind: "other",
|
|
48
|
+
message: error.message
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** The `startup` a 503 `/health` body from a peer carries, when it is the shape stim-server sends. */
|
|
52
|
+
function parsePeerStartup(body) {
|
|
53
|
+
const startup = isJsonObject(body) && isJsonObject(body.startup) ? body.startup : null;
|
|
54
|
+
if (!startup || startup.state !== "pending" && startup.state !== "degraded") return null;
|
|
55
|
+
const cause = startup.cause === "home-unreadable" || startup.cause === "start-failed" ? startup.cause : void 0;
|
|
56
|
+
return {
|
|
57
|
+
state: startup.state,
|
|
58
|
+
...cause ? { cause } : {}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function readTailscale(status) {
|
|
62
|
+
if (!isJsonObject(status)) return {
|
|
63
|
+
running: false,
|
|
64
|
+
code: "tailscale-unavailable"
|
|
65
|
+
};
|
|
66
|
+
switch (status.BackendState) {
|
|
67
|
+
case "Running": return { running: true };
|
|
68
|
+
case "NeedsLogin":
|
|
69
|
+
case "NeedsMachineAuth": return {
|
|
70
|
+
running: false,
|
|
71
|
+
code: "tailscale-logged-out"
|
|
72
|
+
};
|
|
73
|
+
default: return {
|
|
74
|
+
running: false,
|
|
75
|
+
code: "tailscale-stopped"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function tailscaleDiagnosis(code) {
|
|
80
|
+
switch (code) {
|
|
81
|
+
case "tailscale-logged-out": return {
|
|
82
|
+
layer: "tailscale",
|
|
83
|
+
code,
|
|
84
|
+
title: "Tailscale signed out",
|
|
85
|
+
message: "Tailscale on this Mac is signed out, so it cannot reach any remote machine.",
|
|
86
|
+
remedy: "Sign in to Tailscale on this Mac (its menu bar app, or `tailscale up`), then run `stim doctor` again."
|
|
87
|
+
};
|
|
88
|
+
case "tailscale-stopped": return {
|
|
89
|
+
layer: "tailscale",
|
|
90
|
+
code,
|
|
91
|
+
title: "Tailscale is off",
|
|
92
|
+
message: "Tailscale is turned off on this Mac, so it cannot reach any remote machine.",
|
|
93
|
+
remedy: "Turn on Tailscale on this Mac (its menu bar app, or `tailscale up`), then run `stim doctor` again."
|
|
94
|
+
};
|
|
95
|
+
case "tailscale-unavailable": return {
|
|
96
|
+
layer: "tailscale",
|
|
97
|
+
code,
|
|
98
|
+
title: "Tailscale is off",
|
|
99
|
+
message: "Tailscale is not running on this Mac or is not installed, so it cannot reach any remote machine.",
|
|
100
|
+
remedy: "Install Tailscale or open it on this Mac and sign in, then run `stim doctor` again."
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function notOnTailnetDiagnosis(entry, ambiguous) {
|
|
105
|
+
return {
|
|
106
|
+
layer: "peer",
|
|
107
|
+
code: ambiguous ? "peer-ambiguous" : "peer-not-found",
|
|
108
|
+
title: ambiguous ? "Name is ambiguous" : "Not on the tailnet",
|
|
109
|
+
message: ambiguous ? `Several peers on this tailnet match ${entry}.` : `No peer on this tailnet is named ${entry}.`,
|
|
110
|
+
remedy: ambiguous ? `Use the full MagicDNS name in remote.machines (see \`tailscale status\`).` : `Check the name in \`tailscale status\`, or sign in to Tailscale on ${entry} with the same account.`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function ago(iso, now) {
|
|
114
|
+
const then = iso ? Date.parse(iso) : NaN;
|
|
115
|
+
if (!Number.isFinite(then) || then <= 0) return null;
|
|
116
|
+
const minutes = Math.max(0, Math.round((now - then) / 6e4));
|
|
117
|
+
if (minutes < 1) return "less than a minute ago";
|
|
118
|
+
if (minutes < 60) return `${minutes} min ago`;
|
|
119
|
+
const hours = Math.round(minutes / 60);
|
|
120
|
+
if (hours < 48) return `${hours} h ago`;
|
|
121
|
+
return `${Math.round(hours / 24)} days ago`;
|
|
122
|
+
}
|
|
123
|
+
const sentence = (text) => text.replace(/\s+$/, "").replace(/([^.!?])$/, "$1.");
|
|
124
|
+
/**
|
|
125
|
+
* Tailscale's `Online` lags a reachable peer, so `peer-offline` needs a silent connection; any other outcome,
|
|
126
|
+
* including one past the transport (no `transport`), shows the peer answered.
|
|
127
|
+
*/
|
|
128
|
+
function diagnoseUnreachable(input) {
|
|
129
|
+
const { entry, peer, port, transport, now = Date.now() } = input;
|
|
130
|
+
const route = `https port ${port}`;
|
|
131
|
+
if (transport?.kind === "silent" && input.pinged === true) return {
|
|
132
|
+
layer: "server",
|
|
133
|
+
code: "server-no-answer",
|
|
134
|
+
title: "Server not answering",
|
|
135
|
+
message: `${entry} answers on Tailscale, but its serve route and stim-server sent no reply in time. stim-server may be starting, stalled or restarting.`,
|
|
136
|
+
remedy: `On ${entry}, run \`stim-server service status\`; it reports a restart loop or a Stim home it is still reading, and the log file to read.`
|
|
137
|
+
};
|
|
138
|
+
if (peer.online === false && transport?.kind === "silent") {
|
|
139
|
+
const seen = ago(peer.lastSeen, now);
|
|
140
|
+
return {
|
|
141
|
+
layer: "peer",
|
|
142
|
+
code: "peer-offline",
|
|
143
|
+
title: "Mac is offline",
|
|
144
|
+
message: `${entry} is offline on Tailscale${seen ? ` (last seen ${seen})` : ""}.`,
|
|
145
|
+
remedy: `Wake ${entry} or check that it is powered on, connected to a network and signed in to Tailscale, then run \`stim doctor\` again.`
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (transport?.kind === "refused") return {
|
|
149
|
+
layer: "route",
|
|
150
|
+
code: "route-missing",
|
|
151
|
+
title: "No serve route",
|
|
152
|
+
message: `${entry} answers on the tailnet, but nothing accepts connections on ${route}, so it has no tailscale serve route for stim-server.`,
|
|
153
|
+
remedy: `On ${entry}, run \`stim-server service status\`; \`stim-server service install --serve\` creates the route.`
|
|
154
|
+
};
|
|
155
|
+
if (transport?.kind === "silent") return {
|
|
156
|
+
layer: "peer",
|
|
157
|
+
code: "peer-no-answer",
|
|
158
|
+
title: "Mac not answering",
|
|
159
|
+
message: `${entry} did not answer the connection, and Tailscale does not list it as offline.`,
|
|
160
|
+
remedy: `Check that ${entry} is awake and its network is up; \`tailscale ping ${entry}\` on this Mac tests the path.`
|
|
161
|
+
};
|
|
162
|
+
if (transport?.kind === "tls") return {
|
|
163
|
+
layer: "route",
|
|
164
|
+
code: "route-tls",
|
|
165
|
+
title: "Route certificate",
|
|
166
|
+
message: `The tailscale serve route on ${entry} (${route}) did not present a certificate this Mac accepts: ${sentence(transport.message)}`,
|
|
167
|
+
remedy: `On ${entry}, run \`tailscale serve status\`, then \`stim-server service install --serve\` to recreate the route.`
|
|
168
|
+
};
|
|
169
|
+
if (transport?.kind === "http") {
|
|
170
|
+
const { status } = transport;
|
|
171
|
+
if (status === 502) return {
|
|
172
|
+
layer: "server",
|
|
173
|
+
code: "server-not-running",
|
|
174
|
+
title: "Server not running",
|
|
175
|
+
message: `Tailscale and the serve route on ${entry} work, but nothing listens behind the route: stim-server is not running there (HTTP 502).`,
|
|
176
|
+
remedy: `On ${entry}, run \`stim-server service status\`; \`stim-server service install --serve\` starts it and keeps it running.`
|
|
177
|
+
};
|
|
178
|
+
if (status === 503) {
|
|
179
|
+
const startup = input.startup;
|
|
180
|
+
const why = startup?.cause === "home-unreadable" ? " It cannot read its Stim home, usually because the disk holding ~/.stim is disconnected or not responding." : startup?.cause === "start-failed" ? " It failed while starting." : startup?.state === "pending" ? " It is still starting." : "";
|
|
181
|
+
const check = startup?.cause === "home-unreadable" ? `Check the disk that holds the Stim home on ${entry} (reconnect or wake an external drive); stim-server serves again by itself once it answers. \`stim-server service status\` there shows the exact reason.` : startup?.state === "pending" ? `Wait a few seconds and run \`stim doctor\` again.` : `On ${entry}, run \`stim-server service status\` for the reason; the log file it prints has the rest.`;
|
|
182
|
+
return {
|
|
183
|
+
layer: "server",
|
|
184
|
+
code: "server-not-ready",
|
|
185
|
+
title: "Server not ready",
|
|
186
|
+
message: `stim-server on ${entry} is running but is not serving clients (HTTP 503).${why}`,
|
|
187
|
+
remedy: check
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
layer: "route",
|
|
192
|
+
code: "route-http-error",
|
|
193
|
+
title: `Route answers ${status}`,
|
|
194
|
+
message: `The tailscale serve route on ${entry} (${route}) answered HTTP ${status}, which stim-server does not send for a connection.`,
|
|
195
|
+
remedy: `On ${entry}, run \`tailscale serve status\` and check what the route on port ${port} points to.`
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
layer: "server",
|
|
200
|
+
code: "connect-failed",
|
|
201
|
+
title: "Not answering",
|
|
202
|
+
message: `Could not connect to stim-server on ${entry}: ${sentence(input.reason)}`,
|
|
203
|
+
remedy: `On ${entry}, run \`stim-server service status\`. To keep it running there, run \`stim-server service install --serve\`.`
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function diagnosisFinding(code, entry, diagnosis) {
|
|
207
|
+
return {
|
|
208
|
+
code,
|
|
209
|
+
level: "note",
|
|
210
|
+
title: `Remote machine ${entry}: ${diagnosis.title}`,
|
|
211
|
+
detail: diagnosis.message,
|
|
212
|
+
fix: diagnosis.remedy
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
26
216
|
//#region src/offload/tailnet.ts
|
|
27
217
|
const MAC_APP_TAILSCALE = "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
|
|
28
218
|
const HELLO_TIMEOUT_MS = 1e4;
|
|
219
|
+
const HEALTH_TIMEOUT_MS = 3e3;
|
|
29
220
|
/** How long a setup ticket `stim doctor --fix` generates stays valid; `stim-server setup` takes at most 2 hours. */
|
|
30
221
|
const GENERATED_TICKET_MS = 714e4;
|
|
31
|
-
function
|
|
222
|
+
function parseJson(output) {
|
|
32
223
|
try {
|
|
33
|
-
|
|
34
|
-
return isJsonObject(status) && status.BackendState === "Running" ? status : null;
|
|
224
|
+
return JSON.parse(output);
|
|
35
225
|
} catch {
|
|
36
226
|
return null;
|
|
37
227
|
}
|
|
38
228
|
}
|
|
229
|
+
function parseTailscaleStatus(output) {
|
|
230
|
+
const status = parseJson(output);
|
|
231
|
+
return isJsonObject(status) && status.BackendState === "Running" ? status : null;
|
|
232
|
+
}
|
|
233
|
+
function rawTailscaleStatus() {
|
|
234
|
+
for (const binary of ["tailscale", MAC_APP_TAILSCALE]) {
|
|
235
|
+
const output = getExecutor().runFileQuiet(binary, ["status", "--json"], { timeoutMs: 5e3 });
|
|
236
|
+
if (output !== null) return parseJson(output);
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
/** Reads the `startup` of a peer's `/health`, which stim-server answers with 503 while it is not serving clients. */
|
|
241
|
+
function peerStartup({ url, servername, host }) {
|
|
242
|
+
return new Promise((resolve) => {
|
|
243
|
+
const base = new URL(url);
|
|
244
|
+
const request$1 = request({
|
|
245
|
+
host: base.hostname.replace(/^\[|\]$/g, ""),
|
|
246
|
+
port: base.port || 443,
|
|
247
|
+
path: "/health",
|
|
248
|
+
servername,
|
|
249
|
+
headers: { Host: host },
|
|
250
|
+
timeout: HEALTH_TIMEOUT_MS
|
|
251
|
+
}, (response) => {
|
|
252
|
+
const chunks = [];
|
|
253
|
+
let size = 0;
|
|
254
|
+
response.on("data", (chunk) => {
|
|
255
|
+
size += chunk.length;
|
|
256
|
+
if (size > 4096) request$1.destroy();
|
|
257
|
+
else chunks.push(chunk);
|
|
258
|
+
});
|
|
259
|
+
response.on("end", () => resolve(parsePeerStartup(parseJson(Buffer.concat(chunks).toString("utf8")))));
|
|
260
|
+
response.on("error", () => resolve(null));
|
|
261
|
+
});
|
|
262
|
+
request$1.on("timeout", () => request$1.destroy());
|
|
263
|
+
request$1.on("error", () => resolve(null));
|
|
264
|
+
request$1.end();
|
|
265
|
+
});
|
|
266
|
+
}
|
|
39
267
|
function tailscaleStatus() {
|
|
40
268
|
for (const binary of ["tailscale", MAC_APP_TAILSCALE]) {
|
|
41
269
|
const output = getExecutor().runFileQuiet(binary, ["status", "--json"], { timeoutMs: 5e3 });
|
|
@@ -43,7 +271,7 @@ function tailscaleStatus() {
|
|
|
43
271
|
}
|
|
44
272
|
return null;
|
|
45
273
|
}
|
|
46
|
-
async function
|
|
274
|
+
async function tailscaleOutputAsync(signal) {
|
|
47
275
|
for (const binary of ["tailscale", MAC_APP_TAILSCALE]) {
|
|
48
276
|
signal?.throwIfAborted();
|
|
49
277
|
let output;
|
|
@@ -57,10 +285,42 @@ async function tailscaleStatusAsync(signal) {
|
|
|
57
285
|
continue;
|
|
58
286
|
}
|
|
59
287
|
signal?.throwIfAborted();
|
|
60
|
-
return
|
|
288
|
+
return output;
|
|
61
289
|
}
|
|
62
290
|
return null;
|
|
63
291
|
}
|
|
292
|
+
async function tailscaleStatusAsync(signal) {
|
|
293
|
+
const output = await tailscaleOutputAsync(signal);
|
|
294
|
+
return output === null ? null : parseTailscaleStatus(output);
|
|
295
|
+
}
|
|
296
|
+
async function pingPeer(address, signal) {
|
|
297
|
+
for (const binary of ["tailscale", MAC_APP_TAILSCALE]) {
|
|
298
|
+
signal?.throwIfAborted();
|
|
299
|
+
try {
|
|
300
|
+
await getExecutor().runFileAsync(binary, [
|
|
301
|
+
"ping",
|
|
302
|
+
"--c",
|
|
303
|
+
"1",
|
|
304
|
+
"--until-direct=false",
|
|
305
|
+
"--timeout",
|
|
306
|
+
"4s",
|
|
307
|
+
address
|
|
308
|
+
], {
|
|
309
|
+
timeoutMs: 6e3,
|
|
310
|
+
signal
|
|
311
|
+
});
|
|
312
|
+
return true;
|
|
313
|
+
} catch (error) {
|
|
314
|
+
signal?.throwIfAborted();
|
|
315
|
+
if (!/ENOENT|not found/i.test(String(error))) return false;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
async function rawTailscaleStatusAsync(signal) {
|
|
321
|
+
const output = await tailscaleOutputAsync(signal);
|
|
322
|
+
return output === null ? null : parseJson(output);
|
|
323
|
+
}
|
|
64
324
|
const TURNED_AWAY = /* @__PURE__ */ new Set([
|
|
65
325
|
"unauthorized",
|
|
66
326
|
"forbidden",
|
|
@@ -106,6 +366,7 @@ function openAuthenticated(target, auth, timeoutMs, signal, operation) {
|
|
|
106
366
|
socket.removeAllListeners("open");
|
|
107
367
|
socket.removeAllListeners("message");
|
|
108
368
|
socket.removeAllListeners("error");
|
|
369
|
+
socket.removeAllListeners("unexpected-response");
|
|
109
370
|
socket.on("error", () => {});
|
|
110
371
|
socket.terminate();
|
|
111
372
|
}
|
|
@@ -116,18 +377,33 @@ function openAuthenticated(target, auth, timeoutMs, signal, operation) {
|
|
|
116
377
|
refused: false,
|
|
117
378
|
code: "STIM_CANCELLED"
|
|
118
379
|
});
|
|
380
|
+
let opened = false;
|
|
119
381
|
const timer = setTimeout(() => finish({
|
|
120
382
|
failure: "no reply in time",
|
|
121
|
-
refused: false
|
|
383
|
+
refused: false,
|
|
384
|
+
transport: opened ? {
|
|
385
|
+
kind: "other",
|
|
386
|
+
message: "the handshake got no reply"
|
|
387
|
+
} : { kind: "silent" }
|
|
122
388
|
}), timeoutMs);
|
|
123
389
|
signal?.addEventListener("abort", cancel, { once: true });
|
|
124
390
|
if (signal?.aborted) return cancel();
|
|
125
|
-
const failed = (error) => finish({
|
|
126
|
-
failure:
|
|
127
|
-
refused: false
|
|
391
|
+
const failed = (error, status) => finish({
|
|
392
|
+
failure: status === 502 ? "no stim-server answers behind its tailscale serve route (HTTP 502)" : status === 503 ? "stim-server answers behind its tailscale serve route but is not serving clients (HTTP 503)" : status !== void 0 ? `its tailscale serve route answered HTTP ${status}` : error.message,
|
|
393
|
+
refused: false,
|
|
394
|
+
transport: opened && status === void 0 ? {
|
|
395
|
+
kind: "other",
|
|
396
|
+
message: error.message
|
|
397
|
+
} : classifyTransport(error, status)
|
|
398
|
+
});
|
|
399
|
+
socket.once("unexpected-response", (request, response) => {
|
|
400
|
+
response.resume();
|
|
401
|
+
request.destroy();
|
|
402
|
+
failed(/* @__PURE__ */ new Error(`Unexpected server response: ${response.statusCode}`), response.statusCode);
|
|
128
403
|
});
|
|
129
404
|
socket.once("error", failed);
|
|
130
405
|
socket.once("open", () => {
|
|
406
|
+
opened = true;
|
|
131
407
|
const secret = "ticket" in auth ? decodeSecret(auth.ticket) : null;
|
|
132
408
|
let keyPair;
|
|
133
409
|
try {
|
|
@@ -215,7 +491,10 @@ async function hello(target, auth, operation) {
|
|
|
215
491
|
if (!("send" in socket)) return socket.code ? { error: {
|
|
216
492
|
code: socket.code,
|
|
217
493
|
message: socket.failure
|
|
218
|
-
} } : {
|
|
494
|
+
} } : {
|
|
495
|
+
failed: socket.failure,
|
|
496
|
+
...socket.transport ? { transport: socket.transport } : {}
|
|
497
|
+
};
|
|
219
498
|
try {
|
|
220
499
|
if ("onServerKey" in auth) await auth.onServerKey?.(socket.remoteKey.toString("base64url"));
|
|
221
500
|
} catch (error) {
|
|
@@ -241,14 +520,58 @@ async function hello(target, auth, operation) {
|
|
|
241
520
|
}
|
|
242
521
|
const realIo = {
|
|
243
522
|
status: tailscaleStatus,
|
|
244
|
-
hello
|
|
523
|
+
hello,
|
|
524
|
+
rawStatus: rawTailscaleStatus,
|
|
525
|
+
startup: peerStartup,
|
|
526
|
+
ping: (address) => pingPeer(address)
|
|
245
527
|
};
|
|
246
528
|
function doctorIo(operation) {
|
|
247
529
|
return {
|
|
248
530
|
status: () => tailscaleStatusAsync(operation?.signal),
|
|
249
|
-
hello: (target, auth) => hello(target, auth, operation)
|
|
531
|
+
hello: (target, auth) => hello(target, auth, operation),
|
|
532
|
+
rawStatus: () => rawTailscaleStatusAsync(operation?.signal),
|
|
533
|
+
startup: peerStartup,
|
|
534
|
+
ping: (address) => pingPeer(address, operation?.signal)
|
|
250
535
|
};
|
|
251
536
|
}
|
|
537
|
+
/**
|
|
538
|
+
* Names the layer an unreachable `entry` stopped at. A 503 reads the peer's `/health` for why the server is not
|
|
539
|
+
* serving; an older stim-server answers without it, and the diagnosis then says only that it is not ready.
|
|
540
|
+
*/
|
|
541
|
+
async function diagnoseOutcome(io, input) {
|
|
542
|
+
const { target, ...rest } = input;
|
|
543
|
+
const startup = rest.transport?.kind === "http" && rest.transport.status === 503 ? await io.startup?.(target) : null;
|
|
544
|
+
const pinged = rest.transport?.kind === "silent" ? await io.ping?.(rest.peer.address) : void 0;
|
|
545
|
+
return diagnoseUnreachable({
|
|
546
|
+
...rest,
|
|
547
|
+
startup,
|
|
548
|
+
pinged
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* The diagnosis `stim doctor` gives for a machine that resolved to a tailnet peer but did not connect, without
|
|
553
|
+
* doctor's `tailscale ping`: the connection has already waited out its timeout. Reads `tailscale status` only here,
|
|
554
|
+
* after the failure. Null when Tailscale no longer lists the machine.
|
|
555
|
+
*/
|
|
556
|
+
async function diagnoseConnectFailure(machine, failure, signal) {
|
|
557
|
+
const parsed = parseMachine(machine);
|
|
558
|
+
if (!parsed) return null;
|
|
559
|
+
const peer = findPeer(await tailscaleStatusAsync(signal), parsed.name);
|
|
560
|
+
if (typeof peer === "string") return null;
|
|
561
|
+
return diagnoseOutcome({ startup: peerStartup }, {
|
|
562
|
+
entry: machine,
|
|
563
|
+
peer,
|
|
564
|
+
port: parsed.port,
|
|
565
|
+
target: endpoint(peer, parsed.port),
|
|
566
|
+
reason: failure.failure,
|
|
567
|
+
...failure.transport ? { transport: failure.transport } : {}
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
/** Why `status` is null, as a diagnosis of the Tailscale layer; falls back to the daemon being unavailable. */
|
|
571
|
+
async function diagnoseTailscale(io) {
|
|
572
|
+
const reading = readTailscale(io.rawStatus ? await io.rawStatus() : null);
|
|
573
|
+
return tailscaleDiagnosis(reading.running ? "tailscale-unavailable" : reading.code);
|
|
574
|
+
}
|
|
252
575
|
/** Where to reach a paired machine now, or why Stim cannot; the handshake then checks its pinned key. */
|
|
253
576
|
async function resolveEndpoint(credential, status = () => tailscaleStatusAsync(signal), signal) {
|
|
254
577
|
return machineEndpoint(credential.machine, parseMachine(credential.machine) ? await status() : null);
|
|
@@ -357,7 +680,8 @@ async function reachMachine(input) {
|
|
|
357
680
|
return {
|
|
358
681
|
state: "unreachable",
|
|
359
682
|
credential: pending,
|
|
360
|
-
reason: "failed" in reply ? reply.failed : "error" in reply ? reply.error.message : "it did not grant this access"
|
|
683
|
+
reason: "failed" in reply ? reply.failed : "error" in reply ? reply.error.message : "it did not grant this access",
|
|
684
|
+
..."failed" in reply && reply.transport ? { transport: reply.transport } : {}
|
|
361
685
|
};
|
|
362
686
|
};
|
|
363
687
|
if (!credential) return input.fix ? ask() : { state: "not-asked" };
|
|
@@ -389,7 +713,8 @@ async function reachMachine(input) {
|
|
|
389
713
|
return {
|
|
390
714
|
state: "unreachable",
|
|
391
715
|
credential,
|
|
392
|
-
reason: "failed" in reply ? reply.failed : reply.error.message
|
|
716
|
+
reason: "failed" in reply ? reply.failed : reply.error.message,
|
|
717
|
+
..."failed" in reply && reply.transport ? { transport: reply.transport } : {}
|
|
393
718
|
};
|
|
394
719
|
}
|
|
395
720
|
if (input.fix && given && given !== credential.ticket) return ask();
|
|
@@ -952,4 +1277,4 @@ function allConsolePortsAndSerials({ isMounted = isOnMountedVolume } = {}) {
|
|
|
952
1277
|
return result;
|
|
953
1278
|
}
|
|
954
1279
|
//#endregion
|
|
955
|
-
export {
|
|
1280
|
+
export { endpoint$1 as $, writeConfigSettingAsync as A, deviceSlotFileKey as B, saveConfig as C, withConfigLock$1 as D, upsertProject as E, isOnMountedVolume as F, projectDeviceSlots as G, deviceSlotPlatforms as H, listMountedVolumes as I, SERVER_OUTDATED as J, removeSlotDevice as K, measuredDirectorySizeAsync as L, directorySize as M, directorySizeAsync as N, withConfigLockAsync$1 as O, formatBytes as P, doctorIo as Q, volumeRootFor as R, removeProject as S, setSupervisor as T, parseDeviceSlotKey as U, deviceSlotKey as V, parseDeviceSlotOption as W, diagnoseOutcome as X, diagnoseConnectFailure as Y, diagnoseTailscale as Z, loadConfig$1 as _, clearDevice as a, realIo as at, releaseAndroidConsolePort as b, ensureConfig as c, setupRemedy as ct, getConfigDir as d, notOnTailnetDiagnosis as dt, findPeer$1 as et, getConfigPath$1 as f, readHostPermissions as ft, isPathPrefix$1 as g, getRepoSettings as h, clearAndroidBootPending as i, reachMachine as it, acquireAvdClaim as j, writeConfigSetting as k, findEnclosingWorktreeRoot as l, tailscaleStatusAsync as lt, getProjectSettings as m, allMetroPorts as n, outdatedServer as nt, clearSupervisor as o, resolveEndpoint as ot, getProject as p, validateDeviceSlot as q, claimMetroPort as r, parseMachine$1 as rt, configCorruptRepair as s, sayHello as st, allConsolePortsAndSerials as t, openAuthenticated as tt, getConcurrencyLimits as u, diagnosisFinding as ut, recordIosSchemeApprovals as v, setDevice as w, releaseMetroPort as x, refuseRelativeStimPaths as y, assignSlotDevice as z };
|