@stim-cli/core 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-DPo8Bexj.d.mts → index-DPp8a4-9.d.mts} +133 -20
- package/dist/phone-protocol.d.mts +20 -5
- package/dist/{protocol-B0KcaUee.d.mts → protocol-CfT6YiG_.d.mts} +54 -5
- package/dist/protocol.d.mts +2 -2
- package/dist/protocol.mjs +10 -2
- package/dist/receive-protocol.mjs +3838 -3343
- package/dist/state.d.mts +2 -2
- package/dist/state.mjs +274 -48
- package/package.json +1 -1
package/dist/state.mjs
CHANGED
|
@@ -39,20 +39,21 @@ function parseAgentEvents(lines) {
|
|
|
39
39
|
const { kind, command, summary } = entry;
|
|
40
40
|
const requestId = typeof entry.requestId === "string" ? entry.requestId : null;
|
|
41
41
|
if (kind === "request.started" && requestId && Number.isFinite(ts)) started.push([requestId, ts]);
|
|
42
|
-
|
|
42
|
+
const scopeChange = kind === "request.started" && (command === "open" || command === "close");
|
|
43
|
+
if (kind !== "action.recorded" && kind !== "request.finished" && !scopeChange) continue;
|
|
43
44
|
const failed = kind === "request.finished" && entry.status === "error";
|
|
44
45
|
if (kind === "request.finished" && !failed) {
|
|
45
46
|
if (requestId) finished.push(requestId);
|
|
46
47
|
continue;
|
|
47
48
|
}
|
|
48
49
|
if (!Number.isFinite(ts) || typeof entry.session !== "string" || typeof command !== "string") continue;
|
|
49
|
-
if (typeof summary !== "string") continue;
|
|
50
|
+
if (typeof summary !== "string" && !scopeChange) continue;
|
|
50
51
|
events.push({
|
|
51
52
|
ts,
|
|
52
53
|
session: entry.session,
|
|
53
54
|
kind,
|
|
54
55
|
command,
|
|
55
|
-
summary,
|
|
56
|
+
summary: typeof summary === "string" ? summary : "",
|
|
56
57
|
failed,
|
|
57
58
|
details: object$1(entry.details),
|
|
58
59
|
requestId
|
|
@@ -147,11 +148,14 @@ function readCompleteLines(path, start) {
|
|
|
147
148
|
let fd;
|
|
148
149
|
try {
|
|
149
150
|
fd = openSync(path, "r");
|
|
150
|
-
const
|
|
151
|
+
const stat = fstatSync(fd);
|
|
152
|
+
const size = stat.size;
|
|
153
|
+
const identity = `${stat.dev}:${stat.ino}`;
|
|
151
154
|
if (size <= start) return {
|
|
152
155
|
text: "",
|
|
153
156
|
next: start,
|
|
154
|
-
size
|
|
157
|
+
size,
|
|
158
|
+
identity
|
|
155
159
|
};
|
|
156
160
|
const buffer = Buffer.alloc(size - start);
|
|
157
161
|
const read = readSync(fd, buffer, 0, buffer.length, start);
|
|
@@ -159,7 +163,8 @@ function readCompleteLines(path, start) {
|
|
|
159
163
|
return {
|
|
160
164
|
text: end < 0 ? "" : buffer.toString("utf8", 0, end + 1),
|
|
161
165
|
next: start + end + 1,
|
|
162
|
-
size
|
|
166
|
+
size,
|
|
167
|
+
identity
|
|
163
168
|
};
|
|
164
169
|
} catch {
|
|
165
170
|
return null;
|
|
@@ -167,16 +172,23 @@ function readCompleteLines(path, start) {
|
|
|
167
172
|
if (fd !== void 0) closeSync(fd);
|
|
168
173
|
}
|
|
169
174
|
}
|
|
175
|
+
function trackMacosOpen(cursor, event, unknownVersion) {
|
|
176
|
+
const flags = object$1(event.details?.flags);
|
|
177
|
+
if (!event.failed) cursor.macos = event.details?.platform === "macos" || flags?.platform === "macos";
|
|
178
|
+
cursor.bundleId = cursor.macos && !event.failed && !unknownVersion && flags?.surface === "app" && typeof event.details?.appBundleId === "string" ? event.details.appBundleId : null;
|
|
179
|
+
}
|
|
170
180
|
/**
|
|
171
181
|
* Reads agent-device session actions incrementally from the supplied sessions directories. With targets,
|
|
172
182
|
* records are restricted to those devices; without targets, retained session metadata supplies attribution.
|
|
173
183
|
* Missing directories and unreadable files yield no records.
|
|
174
184
|
*/
|
|
175
|
-
function createAgentActionReader({ sessionsDirs, targets, sinceTs = -Infinity, now = Date.now, claimedDevice = () => null }) {
|
|
185
|
+
function createAgentActionReader({ sessionsDirs, targets: resolveTargets, sinceTs = -Infinity, now = Date.now, claimedDevice = () => null }) {
|
|
176
186
|
const cursors = /* @__PURE__ */ new Map();
|
|
177
|
-
const byId = new Map(targets?.map((target) => [target.id, target]));
|
|
178
187
|
return () => {
|
|
188
|
+
const targets = typeof resolveTargets === "function" ? resolveTargets() : resolveTargets;
|
|
179
189
|
if (targets && !targets.length) return [];
|
|
190
|
+
const byId = new Map(targets?.map((target) => [target.id, target]));
|
|
191
|
+
const macosByBundle = new Map(targets?.flatMap((target) => target.platform === "macos" ? [[target.bundleId, target]] : []));
|
|
180
192
|
const sessionDirs = (typeof sessionsDirs === "function" ? sessionsDirs() : sessionsDirs).flatMap((sessionsDir) => {
|
|
181
193
|
try {
|
|
182
194
|
return readdirSync(sessionsDir).map((name) => ({
|
|
@@ -205,7 +217,10 @@ function createAgentActionReader({ sessionsDirs, targets, sinceTs = -Infinity, n
|
|
|
205
217
|
runner: null,
|
|
206
218
|
session: null,
|
|
207
219
|
unknownReported: false,
|
|
208
|
-
started: /* @__PURE__ */ new Map()
|
|
220
|
+
started: /* @__PURE__ */ new Map(),
|
|
221
|
+
macos: false,
|
|
222
|
+
bundleId: null,
|
|
223
|
+
closing: null
|
|
209
224
|
};
|
|
210
225
|
cursors.set(dir, cursor);
|
|
211
226
|
try {
|
|
@@ -213,8 +228,12 @@ function createAgentActionReader({ sessionsDirs, targets, sinceTs = -Infinity, n
|
|
|
213
228
|
} catch {}
|
|
214
229
|
}
|
|
215
230
|
let chunk = readCompleteLines(eventsPath, cursor.offset);
|
|
216
|
-
if (chunk && chunk.size < cursor.offset
|
|
231
|
+
if (chunk && (chunk.size < cursor.offset || cursor.identity && chunk.identity !== cursor.identity)) {
|
|
232
|
+
cursor.bundleId = null;
|
|
233
|
+
chunk = readCompleteLines(eventsPath, 0);
|
|
234
|
+
}
|
|
217
235
|
if (!chunk) continue;
|
|
236
|
+
cursor.identity = chunk.identity;
|
|
218
237
|
cursor.offset = chunk.next;
|
|
219
238
|
lines.push(...chunk.text.split("\n"));
|
|
220
239
|
const parsed = parseAgentEvents(lines);
|
|
@@ -241,7 +260,7 @@ function createAgentActionReader({ sessionsDirs, targets, sinceTs = -Infinity, n
|
|
|
241
260
|
spans: parseRunnerSpans(text)
|
|
242
261
|
};
|
|
243
262
|
}
|
|
244
|
-
for (const event of parsed.events) if (event.command === "close" &&
|
|
263
|
+
for (const event of parsed.events) if (event.command === "close" && event.kind === "action.recorded") cursor.closes.push(event.ts);
|
|
245
264
|
const spans = sessionSpans(cursor.runner.spans, cursor.closes);
|
|
246
265
|
const deviceAt = (ts) => spans.length ? spanDevice(spans, ts) : session && claimedDevice(session, sessionsDir);
|
|
247
266
|
cursor.platform ??= parsed.events.map(platformOf).find((platform) => platform !== void 0);
|
|
@@ -252,13 +271,30 @@ function createAgentActionReader({ sessionsDirs, targets, sinceTs = -Infinity, n
|
|
|
252
271
|
});
|
|
253
272
|
if (parsed.unknownVersion && !cursor.unknownReported) {
|
|
254
273
|
const deviceId = spans.at(-1)?.deviceId ?? (session && claimedDevice(session, sessionsDir));
|
|
255
|
-
const target = targets ?
|
|
274
|
+
const target = !targets ? retainedTarget() : cursor.macos ? cursor.bundleId ? macosByBundle.get(cursor.bundleId) : void 0 : deviceId ? byId.get(deviceId) : void 0;
|
|
256
275
|
if (target) {
|
|
257
276
|
cursor.unknownReported = true;
|
|
258
277
|
out.push(formatUnknownRecord(session ?? name, parsed.unknownVersion, target, now()));
|
|
259
278
|
}
|
|
260
279
|
}
|
|
280
|
+
if (parsed.unknownVersion) cursor.bundleId = null;
|
|
261
281
|
for (const event of parsed.events) {
|
|
282
|
+
if (event.kind === "request.started") {
|
|
283
|
+
cursor.closing = event.command === "close" ? cursor.bundleId : null;
|
|
284
|
+
cursor.bundleId = null;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (targets && event.command === "open") trackMacosOpen(cursor, event, Boolean(parsed.unknownVersion));
|
|
288
|
+
if (targets && cursor.macos) {
|
|
289
|
+
if (event.command === "screenshot") continue;
|
|
290
|
+
const bundleId = cursor.bundleId ?? (event.command === "close" ? cursor.closing : null);
|
|
291
|
+
cursor.closing = null;
|
|
292
|
+
const target = bundleId ? macosByBundle.get(bundleId) : void 0;
|
|
293
|
+
const startedAt = event.requestId ? matched.get(event.requestId) : void 0;
|
|
294
|
+
if (target?.platform === "macos" && event.ts >= Math.max(sinceTs, target.launchedAt) && (startedAt ?? event.ts) >= target.launchedAt) out.push(agentRecord(event, target.id, target, startedAt));
|
|
295
|
+
if (event.command === "close") cursor.bundleId = null;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
262
298
|
if (event.ts < sinceTs) continue;
|
|
263
299
|
if (!targets) cursor.platform = platformOf(event) ?? cursor.platform;
|
|
264
300
|
const deviceId = !targets && cursor.platform === "android" ? null : deviceAt(event.ts);
|
|
@@ -591,6 +627,46 @@ function getConcurrencyLimits({ env = process.env } = {}) {
|
|
|
591
627
|
maxDevices: resolveLimit(env.STIM_MAX_DEVICES, c.maxDevices)
|
|
592
628
|
};
|
|
593
629
|
}
|
|
630
|
+
const MAX_SETTING = {
|
|
631
|
+
ios: {
|
|
632
|
+
key: "iosParkedMax",
|
|
633
|
+
env: "STIM_POOL_IOS_PARKED_MAX"
|
|
634
|
+
},
|
|
635
|
+
android: {
|
|
636
|
+
key: "androidParkedMax",
|
|
637
|
+
env: "STIM_POOL_ANDROID_PARKED_MAX"
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
function parseMax(raw, strings) {
|
|
641
|
+
const value = strings && typeof raw === "string" ? /^\d+$/.test(raw.trim()) ? Number(raw.trim()) : NaN : raw;
|
|
642
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) return null;
|
|
643
|
+
return value;
|
|
644
|
+
}
|
|
645
|
+
function getParkedMax(platform, { config, env = process.env } = {}) {
|
|
646
|
+
const { key, env: envKey } = MAX_SETTING[platform];
|
|
647
|
+
const fromEnv = env[envKey];
|
|
648
|
+
const explicit = fromEnv !== void 0 && fromEnv !== "";
|
|
649
|
+
const pool = (config === void 0 ? loadConfig() : config)?.pool;
|
|
650
|
+
if (!explicit && pool !== void 0 && (pool === null || typeof pool !== "object" || Array.isArray(pool))) return {
|
|
651
|
+
max: 0,
|
|
652
|
+
error: "Invalid pool value. Expected an object with simulator and emulator bounds."
|
|
653
|
+
};
|
|
654
|
+
const fromConfig = pool !== null && typeof pool === "object" && !Array.isArray(pool) ? pool[key] : void 0;
|
|
655
|
+
const raw = explicit ? fromEnv : fromConfig;
|
|
656
|
+
if (raw === void 0) return {
|
|
657
|
+
max: env.STIM_HOME ? 0 : 3,
|
|
658
|
+
error: null
|
|
659
|
+
};
|
|
660
|
+
const parsed = parseMax(raw, explicit);
|
|
661
|
+
if (parsed === null) return {
|
|
662
|
+
max: 0,
|
|
663
|
+
error: `Invalid ${explicit ? envKey : `pool.${key}`} value ${JSON.stringify(raw)}. Expected a whole number of parked ${platform === "ios" ? "simulators" : "emulators"}, 0 or more.`
|
|
664
|
+
};
|
|
665
|
+
return {
|
|
666
|
+
max: explicit ? parsed : env.STIM_HOME ? 0 : parsed,
|
|
667
|
+
error: null
|
|
668
|
+
};
|
|
669
|
+
}
|
|
594
670
|
function resolveLimit(envVal, cfgVal) {
|
|
595
671
|
const raw = envVal !== void 0 && envVal !== null && envVal !== "" ? Number(envVal) : typeof cfgVal === "number" ? cfgVal : NaN;
|
|
596
672
|
if (!Number.isFinite(raw) || raw <= 0) return 0;
|
|
@@ -735,11 +811,11 @@ const AGENT_PATH = /^\/device-host\/agent\/[a-f0-9-]{36}\/$/;
|
|
|
735
811
|
const LEASE_NAME = /^[A-Za-z0-9._-]{1,128}$/;
|
|
736
812
|
const LEASE_DEVICE_KEY = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)+@[1-9][0-9]{0,9}$/;
|
|
737
813
|
function parseHostedAgentLease(value) {
|
|
738
|
-
if (!isJsonObject(value) || Object.keys(value).length !== (value.backend === "ios-instance" ? 5 : 4) || ![
|
|
814
|
+
if (!isJsonObject(value) || Object.keys(value).length !== (value.backend === "ios-instance" || value.backend === "android-instance" ? 5 : 4) || ![
|
|
739
815
|
value.tenant,
|
|
740
816
|
value.runId,
|
|
741
817
|
value.clientId
|
|
742
|
-
].every((name) => typeof name === "string" && LEASE_NAME.test(name)) || typeof value.deviceKey !== "string" || !(value.backend === "ios-instance" ? /^ios:mobile:[a-fA-F0-9-]{36}$/.test(value.deviceKey) : value.backend === void 0 && LEASE_DEVICE_KEY.test(value.deviceKey))) return null;
|
|
818
|
+
].every((name) => typeof name === "string" && LEASE_NAME.test(name)) || typeof value.deviceKey !== "string" || !(value.backend === "ios-instance" ? /^ios:mobile:[a-fA-F0-9-]{36}$/.test(value.deviceKey) : value.backend === "android-instance" ? /^android:mobile:emulator-[0-9]+$/.test(value.deviceKey) : value.backend === void 0 && LEASE_DEVICE_KEY.test(value.deviceKey))) return null;
|
|
743
819
|
return value;
|
|
744
820
|
}
|
|
745
821
|
/** A grant naming a driver this client does not know is not usable; it parses as null. */
|
|
@@ -852,10 +928,6 @@ const RELATIVE_PATH = {
|
|
|
852
928
|
};
|
|
853
929
|
const BOOLEAN = { kind: "boolean" };
|
|
854
930
|
const OBJECT = { kind: "object" };
|
|
855
|
-
const REMOTE = {
|
|
856
|
-
kind: "choice",
|
|
857
|
-
choices: REMOTE_DEVICE_BACKENDS
|
|
858
|
-
};
|
|
859
931
|
const PARKED_MAX = {
|
|
860
932
|
kind: "number",
|
|
861
933
|
integer: true,
|
|
@@ -912,7 +984,7 @@ const SETTINGS = [
|
|
|
912
984
|
default: "auto"
|
|
913
985
|
},
|
|
914
986
|
optimization("android.gradleBuildCache", "Gradle build cache"),
|
|
915
|
-
optimization("android.targetAbiOnly", "Narrow Debug builds to the device ABI"),
|
|
987
|
+
optimization("android.targetAbiOnly", "Narrow Debug and hosted Android builds to the device ABI"),
|
|
916
988
|
{
|
|
917
989
|
key: "ios.deviceType",
|
|
918
990
|
type: STRING,
|
|
@@ -940,7 +1012,7 @@ const SETTINGS = [
|
|
|
940
1012
|
patternHelp: "eas, proxy, auto, or a tailnet machine name"
|
|
941
1013
|
},
|
|
942
1014
|
scopes: PROJECT,
|
|
943
|
-
description: "Default iOS remote target: eas, proxy, or an approved Mac in hosting.machines; auto is
|
|
1015
|
+
description: "Default iOS remote target: eas, proxy, or an approved Mac in hosting.machines; auto places on an approved Mac when this Mac is full or busy"
|
|
944
1016
|
},
|
|
945
1017
|
{
|
|
946
1018
|
key: "ios.simslimProfile",
|
|
@@ -1032,9 +1104,13 @@ const SETTINGS = [
|
|
|
1032
1104
|
},
|
|
1033
1105
|
{
|
|
1034
1106
|
key: "android.remote",
|
|
1035
|
-
type:
|
|
1107
|
+
type: {
|
|
1108
|
+
kind: "string",
|
|
1109
|
+
pattern: TAILNET_MACHINE_PATTERN,
|
|
1110
|
+
patternHelp: "eas, proxy, auto, or a tailnet machine name"
|
|
1111
|
+
},
|
|
1036
1112
|
scopes: PROJECT,
|
|
1037
|
-
description: "Default remote
|
|
1113
|
+
description: "Default Android remote target: eas, proxy, or an approved Mac in hosting.machines; auto places on an approved Mac when this Mac is full or busy"
|
|
1038
1114
|
},
|
|
1039
1115
|
{
|
|
1040
1116
|
key: "metro.tunnel",
|
|
@@ -1101,9 +1177,20 @@ const SETTINGS = [
|
|
|
1101
1177
|
minimum: 0
|
|
1102
1178
|
},
|
|
1103
1179
|
scopes: EVERY,
|
|
1104
|
-
default:
|
|
1180
|
+
default: 30,
|
|
1105
1181
|
description: "Minutes an owned simulator or emulator stays idle before the workspace supervisor shuts it down; 0 never"
|
|
1106
1182
|
},
|
|
1183
|
+
{
|
|
1184
|
+
key: "devices.reclaimIdleMinutes",
|
|
1185
|
+
type: {
|
|
1186
|
+
kind: "number",
|
|
1187
|
+
integer: true,
|
|
1188
|
+
minimum: 0
|
|
1189
|
+
},
|
|
1190
|
+
scopes: EVERY,
|
|
1191
|
+
default: 10,
|
|
1192
|
+
description: "Minutes an owned simulator or emulator stays idle before a waiting run reclaims its device slot; 0 never"
|
|
1193
|
+
},
|
|
1107
1194
|
{
|
|
1108
1195
|
key: "macos.product",
|
|
1109
1196
|
type: {
|
|
@@ -1557,7 +1644,7 @@ const SETTINGS = [
|
|
|
1557
1644
|
},
|
|
1558
1645
|
scopes: MACHINE,
|
|
1559
1646
|
default: "none",
|
|
1560
|
-
description: "Tool this Mac starts so a client's coding agent can drive the macOS apps
|
|
1647
|
+
description: "Tool this Mac starts so a client's coding agent can drive the macOS apps, iOS simulators and Android emulators it hosts for that client; none starts nothing"
|
|
1561
1648
|
},
|
|
1562
1649
|
{
|
|
1563
1650
|
key: "server.acceptClientBuilds",
|
|
@@ -1603,7 +1690,7 @@ const SETTINGS = [
|
|
|
1603
1690
|
},
|
|
1604
1691
|
scopes: MACHINE,
|
|
1605
1692
|
default: 2,
|
|
1606
|
-
description: "Load per core (5-minute load average divided by the CPU count) at which a Mac counts as saturated: a build machine declines offloaded builds, and auto offload stops preferring this Mac"
|
|
1693
|
+
description: "Load per core (5-minute load average divided by the CPU count) at which a Mac counts as saturated: a build machine declines offloaded builds, and auto offload stops preferring this Mac; also the busy threshold for automatic iOS and Android device placement"
|
|
1607
1694
|
},
|
|
1608
1695
|
{
|
|
1609
1696
|
key: "offload.workerRoot",
|
|
@@ -2594,7 +2681,7 @@ function listSegments(dir, closedOnly = false) {
|
|
|
2594
2681
|
return segments.toSorted((a, b) => a.start - b.start);
|
|
2595
2682
|
}
|
|
2596
2683
|
/** Every device slot recorded under a workspace's recordings directory. */
|
|
2597
|
-
function listRecordedDevices(recordingsDir) {
|
|
2684
|
+
function listRecordedDevices(recordingsDir, closedOnly = false) {
|
|
2598
2685
|
return listDir(recordingsDir).flatMap((name) => {
|
|
2599
2686
|
const match = DEVICE.exec(name);
|
|
2600
2687
|
if (!match) return [];
|
|
@@ -2603,7 +2690,7 @@ function listRecordedDevices(recordingsDir) {
|
|
|
2603
2690
|
platform: match[1],
|
|
2604
2691
|
slot: match[2],
|
|
2605
2692
|
dir,
|
|
2606
|
-
segments: listSegments(dir)
|
|
2693
|
+
segments: listSegments(dir, closedOnly)
|
|
2607
2694
|
}];
|
|
2608
2695
|
});
|
|
2609
2696
|
}
|
|
@@ -2840,6 +2927,8 @@ const WORKSPACE_PHASES = [
|
|
|
2840
2927
|
"live",
|
|
2841
2928
|
"idle"
|
|
2842
2929
|
];
|
|
2930
|
+
/** The tutorial format version the installed Stim writes into the tutorial app. */
|
|
2931
|
+
const TUTORIAL_VERSION = 1;
|
|
2843
2932
|
/**
|
|
2844
2933
|
* Where a memory figure comes from. `footprint` is each process's physical footprint, which Activity Monitor's Memory
|
|
2845
2934
|
* column shows. `rss` sums resident set sizes, which count pages shared between processes once per process, so a
|
|
@@ -3225,6 +3314,7 @@ function readIdleStop(state) {
|
|
|
3225
3314
|
idleMinutes: record.idleMinutes
|
|
3226
3315
|
};
|
|
3227
3316
|
}
|
|
3317
|
+
const DEVICE_RECLAIM_REASON = "reclaimed for a waiting run";
|
|
3228
3318
|
const DEVICE_IDLE_SHUTDOWN_KEY = "deviceIdleShutdowns";
|
|
3229
3319
|
/** The recorded idle shutdowns by device slot key (`ios`, `android`, `ios:<slot>`). */
|
|
3230
3320
|
function readDeviceIdleShutdowns(state) {
|
|
@@ -3235,7 +3325,8 @@ function readDeviceIdleShutdowns(state) {
|
|
|
3235
3325
|
const record = value;
|
|
3236
3326
|
if (typeof record?.at === "string" && typeof record.idleMinutes === "number") found[key] = {
|
|
3237
3327
|
at: record.at,
|
|
3238
|
-
idleMinutes: record.idleMinutes
|
|
3328
|
+
idleMinutes: record.idleMinutes,
|
|
3329
|
+
...typeof record.reason === "string" ? { reason: record.reason } : {}
|
|
3239
3330
|
};
|
|
3240
3331
|
}
|
|
3241
3332
|
return found;
|
|
@@ -3354,6 +3445,22 @@ function buildDiagnostics(value) {
|
|
|
3354
3445
|
}];
|
|
3355
3446
|
}).toSorted((a, b) => Number(b.file !== null) - Number(a.file !== null)).slice(0, DIAGNOSTIC_CAP);
|
|
3356
3447
|
}
|
|
3448
|
+
function devicePlacement(value) {
|
|
3449
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
3450
|
+
const record = value;
|
|
3451
|
+
if (record.decision !== "local" && record.decision !== "hosted" && record.decision !== "waited-locally" || typeof record.reason !== "string") return null;
|
|
3452
|
+
return {
|
|
3453
|
+
decision: record.decision,
|
|
3454
|
+
reason: record.reason,
|
|
3455
|
+
...typeof record.machine === "string" ? { machine: record.machine } : {}
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
function readDevicePlacement(state, platform, slot = "default") {
|
|
3459
|
+
const slots = isJsonObject(state?.deviceSlots) ? state.deviceSlots : {};
|
|
3460
|
+
const saved = isJsonObject(slots[slot]) ? slots[slot] : {};
|
|
3461
|
+
const device = slot === "default" ? state?.[platform] : saved[platform];
|
|
3462
|
+
return devicePlacement(isJsonObject(device) ? device.devicePlacement : void 0);
|
|
3463
|
+
}
|
|
3357
3464
|
function lastBuildReport(platform, value) {
|
|
3358
3465
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
3359
3466
|
const record = value;
|
|
@@ -3363,6 +3470,7 @@ function lastBuildReport(platform, value) {
|
|
|
3363
3470
|
const finished = new Date(Date.parse(record.startedAt) + (durationMs ?? NaN));
|
|
3364
3471
|
const reason = record.cacheHit === "local" || record.cacheHit === "remote" ? null : parseMissReason(record.missReason);
|
|
3365
3472
|
const diagnostics = record.status === "failed" ? buildDiagnostics(record.diagnostics) : [];
|
|
3473
|
+
const placed = devicePlacement(record.devicePlacement);
|
|
3366
3474
|
return {
|
|
3367
3475
|
platform,
|
|
3368
3476
|
status: record.status,
|
|
@@ -3374,6 +3482,7 @@ function lastBuildReport(platform, value) {
|
|
|
3374
3482
|
finishedAt: Number.isNaN(finished.getTime()) ? null : finished.toISOString(),
|
|
3375
3483
|
...typeof record.errorCode === "string" ? { errorCode: record.errorCode } : {},
|
|
3376
3484
|
...reason ? { missReason: reason } : {},
|
|
3485
|
+
...placed ? { devicePlacement: placed } : {},
|
|
3377
3486
|
...typeof record.buildMachine === "string" ? { buildMachine: record.buildMachine } : {},
|
|
3378
3487
|
...typeof record.builtOn === "string" ? { builtOn: record.builtOn } : {},
|
|
3379
3488
|
...typeof record.offloadedTo === "string" ? { offloadedTo: record.offloadedTo } : {},
|
|
@@ -3519,6 +3628,15 @@ const PLACEMENT_DECISIONS = [
|
|
|
3519
3628
|
"offloaded",
|
|
3520
3629
|
"fell-back"
|
|
3521
3630
|
];
|
|
3631
|
+
const CAPACITY_REFUSAL_LIMIT = 50;
|
|
3632
|
+
const CAPACITY_REFUSAL_MAX_AGE_MS = 6048e5;
|
|
3633
|
+
function trimCapacityRefusals(events, now) {
|
|
3634
|
+
return events.filter((event) => {
|
|
3635
|
+
const age = now - Date.parse(event.at);
|
|
3636
|
+
return age >= 0 && age <= 6048e5;
|
|
3637
|
+
}).toSorted((a, b) => Date.parse(a.at) - Date.parse(b.at)).slice(-50);
|
|
3638
|
+
}
|
|
3639
|
+
const trimCapacityWaits = trimCapacityRefusals;
|
|
3522
3640
|
const PLATFORMS = ["ios", "android"];
|
|
3523
3641
|
const OUTCOMES = ["hit", "cold"];
|
|
3524
3642
|
const HISTORY_LIMIT = 10;
|
|
@@ -3657,6 +3775,8 @@ function normalize(record) {
|
|
|
3657
3775
|
if (Object.keys(normalized).length) history[key] = normalized;
|
|
3658
3776
|
}
|
|
3659
3777
|
const placements = Array.isArray(record.placements) ? record.placements.flatMap(normalizePlacement) : [];
|
|
3778
|
+
const capacityRefusals = Array.isArray(record.capacityRefusals) ? record.capacityRefusals.flatMap(normalizeCapacityRefusal) : [];
|
|
3779
|
+
const capacityWaits = Array.isArray(record.capacityWaits) ? record.capacityWaits.flatMap(normalizeCapacityWait) : [];
|
|
3660
3780
|
const buildMachines = {};
|
|
3661
3781
|
if (isObject(record.buildMachines)) {
|
|
3662
3782
|
for (const [name, totals] of Object.entries(record.buildMachines)) if (isObject(totals)) buildMachines[name] = normalizeMachineTotals(totals);
|
|
@@ -3667,6 +3787,8 @@ function normalize(record) {
|
|
|
3667
3787
|
projects,
|
|
3668
3788
|
...Object.keys(history).length ? { history } : {},
|
|
3669
3789
|
...placements.length ? { placements } : {},
|
|
3790
|
+
...capacityRefusals.length ? { capacityRefusals } : {},
|
|
3791
|
+
...capacityWaits.length ? { capacityWaits } : {},
|
|
3670
3792
|
...Object.keys(buildMachines).length ? { buildMachines } : {}
|
|
3671
3793
|
};
|
|
3672
3794
|
}
|
|
@@ -3677,6 +3799,7 @@ function normalizePlacement(value) {
|
|
|
3677
3799
|
if (typeof reason !== "string" || typeof project !== "string") return [];
|
|
3678
3800
|
const buildMs = wholeMs(value.buildMs);
|
|
3679
3801
|
const slotWaitMs = wholeMs(value.slotWaitMs);
|
|
3802
|
+
const deviceSlotWaitMs = wholeMs(value.deviceSlotWaitMs);
|
|
3680
3803
|
const localEstimateMs = wholeMs(value.localEstimateMs);
|
|
3681
3804
|
return [{
|
|
3682
3805
|
at: timestamp(value.at),
|
|
@@ -3687,10 +3810,40 @@ function normalizePlacement(value) {
|
|
|
3687
3810
|
...typeof machine === "string" && machine !== "" ? { machine } : {},
|
|
3688
3811
|
...buildMs > 0 ? { buildMs } : {},
|
|
3689
3812
|
...slotWaitMs > 0 ? { slotWaitMs } : {},
|
|
3813
|
+
...deviceSlotWaitMs > 0 ? { deviceSlotWaitMs } : {},
|
|
3690
3814
|
...localEstimateMs > 0 ? { localEstimateMs } : {},
|
|
3691
3815
|
...value.failed === true ? { failed: true } : {}
|
|
3692
3816
|
}];
|
|
3693
3817
|
}
|
|
3818
|
+
function normalizeCapacityRefusal(value) {
|
|
3819
|
+
if (!isObject(value)) return [];
|
|
3820
|
+
const { at, kind, platform, max, workspace } = value;
|
|
3821
|
+
if (kind !== "device" || !PLATFORMS.includes(platform)) return [];
|
|
3822
|
+
if (typeof at !== "string" || !Number.isFinite(Date.parse(at))) return [];
|
|
3823
|
+
if (typeof max !== "number" || !Number.isInteger(max) || max <= 0) return [];
|
|
3824
|
+
if (typeof workspace !== "string" || workspace === "") return [];
|
|
3825
|
+
return [{
|
|
3826
|
+
at: new Date(at).toISOString(),
|
|
3827
|
+
kind,
|
|
3828
|
+
platform,
|
|
3829
|
+
max,
|
|
3830
|
+
workspace
|
|
3831
|
+
}];
|
|
3832
|
+
}
|
|
3833
|
+
function normalizeCapacityWait(value) {
|
|
3834
|
+
if (!isObject(value) || value.kind !== "device-wait") return [];
|
|
3835
|
+
const refusal = normalizeCapacityRefusal({
|
|
3836
|
+
...value,
|
|
3837
|
+
kind: "device"
|
|
3838
|
+
})[0];
|
|
3839
|
+
if (!refusal || typeof value.ms !== "number" || !Number.isFinite(value.ms) || value.ms < 0) return [];
|
|
3840
|
+
return [{
|
|
3841
|
+
...refusal,
|
|
3842
|
+
kind: "device-wait",
|
|
3843
|
+
ms: Math.round(value.ms),
|
|
3844
|
+
...typeof value.reclaimed === "number" && Number.isInteger(value.reclaimed) && value.reclaimed > 0 ? { reclaimed: value.reclaimed } : {}
|
|
3845
|
+
}];
|
|
3846
|
+
}
|
|
3694
3847
|
function normalizeMachineTotals(totals) {
|
|
3695
3848
|
const savedMs = Number(totals.savedMs);
|
|
3696
3849
|
return {
|
|
@@ -3810,6 +3963,8 @@ function decodeStats(text) {
|
|
|
3810
3963
|
function readStatsReport(key, now) {
|
|
3811
3964
|
const { record, note } = readStats();
|
|
3812
3965
|
const project = key ? record?.projects[key] : null;
|
|
3966
|
+
const capacityRefusals = trimCapacityRefusals(record?.capacityRefusals ?? [], now).toReversed();
|
|
3967
|
+
const capacityWaits = trimCapacityWaits(record?.capacityWaits ?? [], now).toReversed();
|
|
3813
3968
|
return {
|
|
3814
3969
|
report: {
|
|
3815
3970
|
version: 1,
|
|
@@ -3823,6 +3978,8 @@ function readStatsReport(key, now) {
|
|
|
3823
3978
|
android: record?.machine.android ?? null
|
|
3824
3979
|
},
|
|
3825
3980
|
offload: offloadSummary(record, now),
|
|
3981
|
+
...capacityRefusals.length ? { capacityRefusals } : {},
|
|
3982
|
+
...capacityWaits.length ? { capacityWaits } : {},
|
|
3826
3983
|
agentDevice: readAgentDeviceUsage(),
|
|
3827
3984
|
swiftpmCache: readSwiftpmCacheUsage()
|
|
3828
3985
|
},
|
|
@@ -3923,9 +4080,16 @@ function parseHostedOfferRequest(value) {
|
|
|
3923
4080
|
...typeof value.deviceProfile === "string" ? { deviceProfile: value.deviceProfile } : {}
|
|
3924
4081
|
};
|
|
3925
4082
|
}
|
|
4083
|
+
function parseHostedLocalDeviceCount(value) {
|
|
4084
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) return value;
|
|
4085
|
+
if (isJsonObject(value) && typeof value.unknown === "string" && value.unknown) return { unknown: value.unknown.slice(0, 4e3) };
|
|
4086
|
+
return null;
|
|
4087
|
+
}
|
|
3926
4088
|
function parseHostedNativeOffer(value) {
|
|
3927
4089
|
if (!isJsonObject(value) || !isJsonObject(value.resources)) return null;
|
|
3928
4090
|
const { resources } = value;
|
|
4091
|
+
const local = parseHostedLocalDeviceCount(resources.localDevices);
|
|
4092
|
+
if (resources.localDevices !== void 0 && local === null) return null;
|
|
3929
4093
|
if (value.platform !== "ios" && value.platform !== "android" && value.platform !== "macos" || value.declined !== null && (typeof value.declined !== "string" || !value.declined || value.declined.length > 4e3) || !Number.isInteger(resources.cpus) || Number(resources.cpus) < 1 || ["loadPerCore", "memoryFreeBytes"].some((key) => typeof resources[key] !== "number" || !Number.isFinite(resources[key]) || resources[key] < 0) || resources.workerDiskFreeBytes !== null && (typeof resources.workerDiskFreeBytes !== "number" || !Number.isFinite(resources.workerDiskFreeBytes) || resources.workerDiskFreeBytes < 0) || resources.memoryPressure !== null && resources.memoryPressure !== "normal" && resources.memoryPressure !== "warning" && resources.memoryPressure !== "critical") return null;
|
|
3930
4094
|
let choice = null;
|
|
3931
4095
|
if (value.choice !== null) {
|
|
@@ -3961,7 +4125,8 @@ function parseHostedNativeOffer(value) {
|
|
|
3961
4125
|
loadPerCore: resources.loadPerCore,
|
|
3962
4126
|
memoryFreeBytes: resources.memoryFreeBytes,
|
|
3963
4127
|
memoryPressure: resources.memoryPressure,
|
|
3964
|
-
workerDiskFreeBytes: resources.workerDiskFreeBytes
|
|
4128
|
+
workerDiskFreeBytes: resources.workerDiskFreeBytes,
|
|
4129
|
+
...local !== null ? { localDevices: local } : {}
|
|
3965
4130
|
}
|
|
3966
4131
|
};
|
|
3967
4132
|
}
|
|
@@ -3991,7 +4156,7 @@ function readHostedSessions() {
|
|
|
3991
4156
|
"stopping",
|
|
3992
4157
|
"stopped",
|
|
3993
4158
|
"unknown"
|
|
3994
|
-
].includes(String(entry.state)) || entry.platform === "android" && !hostedConsolePort(entry.consolePort) || (entry.platform === "macos" ? !hostedMacosAppSlot(entry.appSlot) : entry.appSlot !== void 0) || entry.device !== null && !parseHostedPlatformDevice(entry.device, entry.platform) || entry.platform === "macos" && entry.device !== null && entry.device.appSlot !== entry.appSlot || entry.platform === "android" && entry.device !== null && entry.device.consolePort !== entry.consolePort || entry.state === "ready" && entry.device === null || entry.notice !== void 0 && typeof entry.notice !== "string" || entry.appAttempt !== void 0 && (typeof entry.appAttempt !== "string" || !/^[a-zA-Z0-9_-]{1,128}$/.test(entry.appAttempt)) || entry.metroPort !== void 0 && (typeof entry.metroPort !== "number" || !Number.isInteger(entry.metroPort) || entry.metroPort < 1 || entry.metroPort > 65535)) throw new Error(`Malformed hosted session record: ${root}`);
|
|
4159
|
+
].includes(String(entry.state)) || entry.platform === "android" && !hostedConsolePort(entry.consolePort) || (entry.platform === "macos" ? !hostedMacosAppSlot(entry.appSlot) : entry.appSlot !== void 0) || entry.device !== null && !parseHostedPlatformDevice(entry.device, entry.platform) || entry.platform === "macos" && entry.device !== null && entry.device.appSlot !== entry.appSlot || entry.platform === "android" && entry.device !== null && entry.device.consolePort !== entry.consolePort || entry.state === "ready" && entry.device === null || entry.parked !== void 0 && (!isJsonObject(entry.parked) || typeof entry.parked.at !== "string" || !Number.isFinite(Date.parse(entry.parked.at)) || new Date(entry.parked.at).toISOString() !== entry.parked.at || entry.state !== "stopped" || entry.platform === "macos") || entry.notice !== void 0 && typeof entry.notice !== "string" || entry.appAttempt !== void 0 && (typeof entry.appAttempt !== "string" || !/^[a-zA-Z0-9_-]{1,128}$/.test(entry.appAttempt)) || entry.clientMetroPort !== void 0 && (typeof entry.clientMetroPort !== "number" || !Number.isInteger(entry.clientMetroPort) || entry.clientMetroPort < 1 || entry.clientMetroPort > 65535 || entry.platform !== "android") || entry.metroPort !== void 0 && (typeof entry.metroPort !== "number" || !Number.isInteger(entry.metroPort) || entry.metroPort < 1 || entry.metroPort > 65535)) throw new Error(`Malformed hosted session record: ${root}`);
|
|
3995
4160
|
ids.add(entry.id);
|
|
3996
4161
|
return entry;
|
|
3997
4162
|
});
|
|
@@ -4270,27 +4435,45 @@ function macosAppState(record) {
|
|
|
4270
4435
|
};
|
|
4271
4436
|
}
|
|
4272
4437
|
//#endregion
|
|
4273
|
-
//#region state/hosted-
|
|
4274
|
-
function
|
|
4438
|
+
//#region state/hosted-native.ts
|
|
4439
|
+
function parseHostedNativePlacement(value, platform) {
|
|
4275
4440
|
if (!isJsonObject(value) || typeof value.machine !== "string" || !parseMachine(value.machine) || typeof value.session !== "string" || !/^[a-f0-9-]{36}$/.test(value.session)) return null;
|
|
4276
|
-
const device = parseHostedDevice(value.device);
|
|
4277
4441
|
return {
|
|
4278
4442
|
machine: value.machine,
|
|
4279
4443
|
session: value.session,
|
|
4280
4444
|
appAttempt: typeof value.appAttempt === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value.appAttempt) ? value.appAttempt : "",
|
|
4281
|
-
selected: value.machine,
|
|
4282
|
-
|
|
4445
|
+
selected: typeof value.selected === "string" && parseMachine(value.selected) ? value.selected : value.machine,
|
|
4446
|
+
...typeof value.reason === "string" ? { reason: value.reason } : {},
|
|
4447
|
+
device: parseHostedPlatformDevice(value.device, platform),
|
|
4283
4448
|
agent: parseHostedAgentAccess(value.agent) ?? {
|
|
4284
4449
|
driver: "none",
|
|
4285
4450
|
setting: "hosting.agentDriver"
|
|
4286
4451
|
}
|
|
4287
4452
|
};
|
|
4288
4453
|
}
|
|
4454
|
+
function hostedNativeRecords(state, platform) {
|
|
4455
|
+
const slots = isJsonObject(state?.deviceSlots) ? state.deviceSlots : {};
|
|
4456
|
+
const records = {
|
|
4457
|
+
default: state?.[platform],
|
|
4458
|
+
...Object.fromEntries(Object.entries(slots).map(([slot, value]) => [slot, isJsonObject(value) ? value[platform] : void 0]))
|
|
4459
|
+
};
|
|
4460
|
+
return Object.fromEntries(Object.entries(records).flatMap(([slot, record]) => isJsonObject(record) && record.host !== void 0 ? [[slot, record.host]] : []));
|
|
4461
|
+
}
|
|
4462
|
+
function unreadableHostedNative(slot, platform) {
|
|
4463
|
+
const key = slot === "default" ? `${platform}.host` : `deviceSlots.${slot}.${platform}.host`;
|
|
4464
|
+
return `The hosted ${platform === "ios" ? "iOS" : "Android"} placement at workspace-state key ${key} is unreadable. Inspect ${key}.machine and ${key}.session before reconciling it.`;
|
|
4465
|
+
}
|
|
4466
|
+
//#endregion
|
|
4467
|
+
//#region state/hosted-ios.ts
|
|
4468
|
+
function parseHostedIosPlacement(value) {
|
|
4469
|
+
return parseHostedNativePlacement(value, "ios");
|
|
4470
|
+
}
|
|
4289
4471
|
function hostedIosStatus(placement) {
|
|
4290
4472
|
return {
|
|
4291
4473
|
machine: placement.machine,
|
|
4292
4474
|
session: placement.session,
|
|
4293
4475
|
selected: placement.selected,
|
|
4476
|
+
...placement.reason ? { reason: placement.reason } : {},
|
|
4294
4477
|
agent: placement.agent,
|
|
4295
4478
|
device: placement.device ? {
|
|
4296
4479
|
name: placement.device.deviceType,
|
|
@@ -4298,18 +4481,11 @@ function hostedIosStatus(placement) {
|
|
|
4298
4481
|
} : null
|
|
4299
4482
|
};
|
|
4300
4483
|
}
|
|
4301
|
-
/** Includes unreadable placements so callers retain every recorded hosting slot. */
|
|
4302
4484
|
function hostedIosRecords(state) {
|
|
4303
|
-
|
|
4304
|
-
const records = {
|
|
4305
|
-
default: state?.ios,
|
|
4306
|
-
...Object.fromEntries(Object.entries(slots).map(([slot, value]) => [slot, isJsonObject(value) ? value.ios : void 0]))
|
|
4307
|
-
};
|
|
4308
|
-
return Object.fromEntries(Object.entries(records).flatMap(([slot, record]) => isJsonObject(record) && record.host !== void 0 ? [[slot, record.host]] : []));
|
|
4485
|
+
return hostedNativeRecords(state, "ios");
|
|
4309
4486
|
}
|
|
4310
4487
|
function unreadableHostedIos(slot) {
|
|
4311
|
-
|
|
4312
|
-
return `The hosted iOS placement at workspace-state key ${key} is unreadable. Inspect ${key}.machine and ${key}.session before reconciling it.`;
|
|
4488
|
+
return unreadableHostedNative(slot, "ios");
|
|
4313
4489
|
}
|
|
4314
4490
|
function hostedIosPlacements(state) {
|
|
4315
4491
|
return Object.assign(Object.create(null), Object.fromEntries(Object.entries(hostedIosRecords(state)).flatMap(([slot, record]) => {
|
|
@@ -4318,6 +4494,40 @@ function hostedIosPlacements(state) {
|
|
|
4318
4494
|
})));
|
|
4319
4495
|
}
|
|
4320
4496
|
//#endregion
|
|
4497
|
+
//#region state/hosted-android.ts
|
|
4498
|
+
function parseHostedAndroidPlacement(value) {
|
|
4499
|
+
return parseHostedNativePlacement(value, "android");
|
|
4500
|
+
}
|
|
4501
|
+
function hostedAndroidStatus(placement) {
|
|
4502
|
+
return {
|
|
4503
|
+
machine: placement.machine,
|
|
4504
|
+
session: placement.session,
|
|
4505
|
+
selected: placement.selected,
|
|
4506
|
+
...placement.reason ? { reason: placement.reason } : {},
|
|
4507
|
+
agent: placement.agent,
|
|
4508
|
+
device: placement.device ? {
|
|
4509
|
+
name: hostedAndroidDeviceName(placement.device),
|
|
4510
|
+
systemImage: placement.device.systemImage,
|
|
4511
|
+
api: Number(/^system-images;android-(\d+);/.exec(placement.device.systemImage)?.[1])
|
|
4512
|
+
} : null
|
|
4513
|
+
};
|
|
4514
|
+
}
|
|
4515
|
+
function hostedAndroidRecords(state) {
|
|
4516
|
+
return hostedNativeRecords(state, "android");
|
|
4517
|
+
}
|
|
4518
|
+
function unreadableHostedAndroid(slot) {
|
|
4519
|
+
return unreadableHostedNative(slot, "android");
|
|
4520
|
+
}
|
|
4521
|
+
function hostedAndroidPlacements(state) {
|
|
4522
|
+
return Object.assign(Object.create(null), Object.fromEntries(Object.entries(hostedAndroidRecords(state)).flatMap(([slot, record]) => {
|
|
4523
|
+
const placement = parseHostedAndroidPlacement(record);
|
|
4524
|
+
return placement ? [[slot, placement]] : [];
|
|
4525
|
+
})));
|
|
4526
|
+
}
|
|
4527
|
+
function hostedAndroidDeviceName(device) {
|
|
4528
|
+
return `${device.deviceProfile} (API ${/^system-images;android-(\d+);/.exec(device.systemImage)?.[1]})`;
|
|
4529
|
+
}
|
|
4530
|
+
//#endregion
|
|
4321
4531
|
//#region state/hosted-metro.ts
|
|
4322
4532
|
const HOSTED_METRO_REQUESTS_KEY = "hostedMetroRequests";
|
|
4323
4533
|
const HOSTED_METRO_GATEWAYS_KEY = "hostedMetroGateways";
|
|
@@ -4360,15 +4570,19 @@ function hostedMacosLogsDir(home) {
|
|
|
4360
4570
|
return null;
|
|
4361
4571
|
}
|
|
4362
4572
|
}
|
|
4363
|
-
const
|
|
4573
|
+
const hostedNativeLogsDir = (home, platform) => join(home, `${platform}-logs`);
|
|
4364
4574
|
/** The completed query window and overlap digests, or null for a missing, malformed or unreadable checkpoint. */
|
|
4365
|
-
function
|
|
4575
|
+
function readHostedNativeLogsCheckpoint(home, platform = "ios") {
|
|
4366
4576
|
try {
|
|
4367
|
-
const value = JSON.parse(readFileSync(join(
|
|
4577
|
+
const value = JSON.parse(readFileSync(join(hostedNativeLogsDir(home, platform), "checkpoint.json"), "utf8"));
|
|
4368
4578
|
if (!isJsonObject(value) || !Number.isSafeInteger(value.until) || value.until < 0 || value.windowMs !== void 0 && (!Number.isSafeInteger(value.windowMs) || value.windowMs < 1e3) || !Array.isArray(value.boundary) || !value.boundary.every((each) => typeof each === "string" && /^[a-f0-9]{64}$/.test(each))) return null;
|
|
4369
4579
|
return {
|
|
4370
4580
|
until: value.until,
|
|
4371
4581
|
boundary: value.boundary,
|
|
4582
|
+
...typeof value.appAttempt === "string" && Number.isSafeInteger(value.pid) && value.pid > 0 ? {
|
|
4583
|
+
appAttempt: value.appAttempt,
|
|
4584
|
+
pid: value.pid
|
|
4585
|
+
} : {},
|
|
4372
4586
|
...typeof value.windowMs === "number" ? { windowMs: value.windowMs } : {}
|
|
4373
4587
|
};
|
|
4374
4588
|
} catch {
|
|
@@ -4535,6 +4749,7 @@ function readArchive(id) {
|
|
|
4535
4749
|
if (!id || id.startsWith(".") || basename(id) !== id || id.includes("\0")) return null;
|
|
4536
4750
|
try {
|
|
4537
4751
|
if (!lstatSync(join(archiveRoot(), id)).isDirectory()) return null;
|
|
4752
|
+
if (!lstatSync(join(archiveRoot(), id, "archive.json")).isFile()) return null;
|
|
4538
4753
|
} catch {
|
|
4539
4754
|
return null;
|
|
4540
4755
|
}
|
|
@@ -4556,6 +4771,17 @@ function readArchive(id) {
|
|
|
4556
4771
|
].some((key) => typeof bytes[key] !== "number" || !Number.isFinite(bytes[key]) || bytes[key] < 0)) return null;
|
|
4557
4772
|
return record;
|
|
4558
4773
|
}
|
|
4774
|
+
function readArchiveState(id) {
|
|
4775
|
+
if (!id || id.startsWith(".") || basename(id) !== id || id.includes("\0")) return null;
|
|
4776
|
+
const file = join(archiveRoot(), id, "state.json");
|
|
4777
|
+
try {
|
|
4778
|
+
if (!lstatSync(join(archiveRoot(), id)).isDirectory()) return null;
|
|
4779
|
+
if (!lstatSync(file).isFile()) return null;
|
|
4780
|
+
} catch {
|
|
4781
|
+
return null;
|
|
4782
|
+
}
|
|
4783
|
+
return readJsonObject(file);
|
|
4784
|
+
}
|
|
4559
4785
|
function archivedUsage(archives) {
|
|
4560
4786
|
const usage = {
|
|
4561
4787
|
count: archives.length,
|
|
@@ -4649,4 +4875,4 @@ function isSetupJournalPrunable(journal, now) {
|
|
|
4649
4875
|
return Date.parse(journal.expiresAt) + 36e5 < now;
|
|
4650
4876
|
}
|
|
4651
4877
|
//#endregion
|
|
4652
|
-
export { ACTIVITY_RECENCY_BASES, AGENT_TOOLS, ANDROID_COMPILER_CACHE_CHOICES, ANDROID_EMULATOR_APPS, ANDROID_PCH_CHOICES, APP_PROCESS_STATES, BUILD_HISTORY_KEY, BUILD_HISTORY_LIMIT, BUILD_PHASES, BUILD_RESULTS, DEVICE_IDLE_SHUTDOWN_KEY, DEV_SERVER_STOP_REQUEST_KEY, ENDED_AGENT_RETENTION_MS, ERROR_SOURCES, HISTORY_LIMIT, HOSTED_AGENT_DRIVERS, HOSTED_APP_CHUNK_BYTES, HOSTED_LOGS_FIRST_BYTES, HOSTED_LOGS_PAGE_BYTES, HOSTED_MACOS_APP_SLOTS, HOSTED_METRO_GATEWAYS_KEY, HOSTED_METRO_REQUESTS_KEY, IDLE_STOP_KEY, IOS_SIMULATOR_APPS, LAST_BUILD_KEYS, LEVELS, MACHINE_OWNER_KINDS, MEMORY_SOURCES, NATIVE_BUILD_STEPS, NATIVE_CRASH_LOG_PREFIX, OFFLOAD_MODES, PHYSICAL_DEVICE_CONNECTIONS, PLACEMENT_DECISIONS, READY_PHASE_MS, RECORDING_CAP_BYTES, RECORDING_FOOTAGE_MS, RECORDING_PLATFORMS, REMOTE_DEVICE_BACKENDS, SETTINGS, SETTINGS_SCHEMA_FILE, SETTINGS_SCHEMA_URL, SETTING_GROUPS, SETTING_SCOPES, SOURCES, STATS_VERSION, STATUS_ISSUE_CODES, TAILNET_MACHINE_PATTERN, TUNNEL_MODES, WARM_KEY, WEB_PAGE_STATES, WEB_VIEWPORTS, WORKSPACE_AGENT_KEY, WORKSPACE_PHASES, WORKSPACE_STAGE_KINDS, acceptsShape, advanceTail, agentDeviceStateDirs, agentDeviceUsageFile, agentSessionOf, agentSessionsCacheFile, allMetroPorts, archiveDir, archiveEnabled, archiveLock, archiveRoot, archivedUsage, assertHostedDeviceLedger, attachExpoErrorContext, buildCriteria, buildDiagnostics, buildMachinesFile, buildMachinesLock, buildSlotPath, buildSlotsDir, buildWorkerRoot, bundleMarkerPlatform, capDrops, closedSegmentName, coerceSettingText, compileGrep, configCorruptRepair, configLockPath, countErrorEntries, createAgentActionReader, createdDevicesFile, createdDevicesLock, decodeStats, deviceHostArea, deviceHostMachinesClaims, deviceHostMachinesFile, deviceHostMachinesLock, deviceHostRoot, deviceViewersDir, diskUsageCacheDir, diskUsageCacheFile, easMachineStateRoot, easSessionLedgerFile, easSessionLedgerLock, emulatorLogFile, endedAgentSessionOf, endpoint, expectedShape, fileSizes, findEnclosingWorktreeRoot, findPeer, findProjectByMetroPort, findProjectRoot, followLogs, footageDrops, getConcurrencyLimits, getConfigPath, getProject, getProjectSetting, getProjectSettings, getRepoSettings, gitMergeCacheDir, hostedAppArea, hostedAppAttempt, hostedAppBlobs, hostedConsolePort, hostedDeviceId,
|
|
4878
|
+
export { ACTIVITY_RECENCY_BASES, AGENT_TOOLS, ANDROID_COMPILER_CACHE_CHOICES, ANDROID_EMULATOR_APPS, ANDROID_PCH_CHOICES, APP_PROCESS_STATES, BUILD_HISTORY_KEY, BUILD_HISTORY_LIMIT, BUILD_PHASES, BUILD_RESULTS, CAPACITY_REFUSAL_LIMIT, CAPACITY_REFUSAL_MAX_AGE_MS, DEVICE_IDLE_SHUTDOWN_KEY, DEVICE_RECLAIM_REASON, DEV_SERVER_STOP_REQUEST_KEY, ENDED_AGENT_RETENTION_MS, ERROR_SOURCES, HISTORY_LIMIT, HOSTED_AGENT_DRIVERS, HOSTED_APP_CHUNK_BYTES, HOSTED_LOGS_FIRST_BYTES, HOSTED_LOGS_PAGE_BYTES, HOSTED_MACOS_APP_SLOTS, HOSTED_METRO_GATEWAYS_KEY, HOSTED_METRO_REQUESTS_KEY, IDLE_STOP_KEY, IOS_SIMULATOR_APPS, LAST_BUILD_KEYS, LEVELS, MACHINE_OWNER_KINDS, MEMORY_SOURCES, NATIVE_BUILD_STEPS, NATIVE_CRASH_LOG_PREFIX, OFFLOAD_MODES, PHYSICAL_DEVICE_CONNECTIONS, PLACEMENT_DECISIONS, READY_PHASE_MS, RECORDING_CAP_BYTES, RECORDING_FOOTAGE_MS, RECORDING_PLATFORMS, REMOTE_DEVICE_BACKENDS, SETTINGS, SETTINGS_SCHEMA_FILE, SETTINGS_SCHEMA_URL, SETTING_GROUPS, SETTING_SCOPES, SOURCES, STATS_VERSION, STATUS_ISSUE_CODES, TAILNET_MACHINE_PATTERN, TUNNEL_MODES, TUTORIAL_VERSION, WARM_KEY, WEB_PAGE_STATES, WEB_VIEWPORTS, WORKSPACE_AGENT_KEY, WORKSPACE_PHASES, WORKSPACE_STAGE_KINDS, acceptsShape, advanceTail, agentDeviceStateDirs, agentDeviceUsageFile, agentSessionOf, agentSessionsCacheFile, allMetroPorts, archiveDir, archiveEnabled, archiveLock, archiveRoot, archivedUsage, assertHostedDeviceLedger, attachExpoErrorContext, buildCriteria, buildDiagnostics, buildMachinesFile, buildMachinesLock, buildSlotPath, buildSlotsDir, buildWorkerRoot, bundleMarkerPlatform, capDrops, closedSegmentName, coerceSettingText, compileGrep, configCorruptRepair, configLockPath, countErrorEntries, createAgentActionReader, createdDevicesFile, createdDevicesLock, decodeStats, deviceHostArea, deviceHostMachinesClaims, deviceHostMachinesFile, deviceHostMachinesLock, deviceHostRoot, deviceViewersDir, diskUsageCacheDir, diskUsageCacheFile, easMachineStateRoot, easSessionLedgerFile, easSessionLedgerLock, emulatorLogFile, endedAgentSessionOf, endpoint, expectedShape, fileSizes, findEnclosingWorktreeRoot, findPeer, findProjectByMetroPort, findProjectRoot, followLogs, footageDrops, getConcurrencyLimits, getConfigPath, getParkedMax, getProject, getProjectSetting, getProjectSettings, getRepoSettings, gitMergeCacheDir, hostedAndroidDeviceName, hostedAndroidPlacements, hostedAndroidRecords, hostedAndroidStatus, hostedAppArea, hostedAppAttempt, hostedAppBlobs, hostedConsolePort, hostedDeviceId, hostedIosPlacements, hostedIosRecords, hostedIosStatus, hostedMacosAppSlot, hostedMacosBundleId, hostedMacosLogsDir, hostedMetroGateways, hostedMetroRequests, hostedNativeLogsDir, hostedNativeRecords, isJournalExpired, isJsonObject, isLayeredSetting, isPathPrefix, isSensitiveReference, isSetupJournalPrunable, lastUseFrom, levelRank, linkReplacedArchives, listRecordedDevices, listSegments, listStatusCacheEntries, loadConfig, logFiles, machineCapacity, macosAppState, maintenanceAttemptFile, maintenanceChildLogFile, maintenanceDir, maintenanceNdjsonFile, maintenanceRunClaims, maintenanceStateFile, markerWindow, offloadSummary, openSegmentName, parseHostedAgentAccess, parseHostedAgentGrant, parseHostedAndroidDevice, parseHostedAndroidPlacement, parseHostedAppManifest, parseHostedAppOffer, parseHostedChoice, parseHostedDevice, parseHostedIosPlacement, parseHostedLocalDeviceCount, parseHostedLogsCursor, parseHostedMacosChoice, parseHostedMacosDevice, parseHostedMacosPlacement, parseHostedNativeOffer, parseHostedNativePlacement, parseHostedOfferRequest, parseHostedPlatformDevice, parseHostedRequest, parseMachine, parseMacosRecord, parseMaintenanceState, parseMissReason, parseNdjsonLine, parseNdjsonText, parseSetupJournal, parseSince, pinnedEndpoint, pullRequestCacheDir, pullRequestCacheFile, queryJsonLogs, queryLogs, readAgentDeviceUsage, readAgentDeviceUsageCache, readAgentSessionsCache, readArchive, readArchiveState, readArchives, readBuildDetail, readBuildHistory, readBuildMachines, readCreatedDevices, readDevServerStop, readDevServerStopRequest, readDeviceHostMachines, readDeviceIdleShutdowns, readDevicePlacement, readDiskUsage, readEasSessionLedger, readEndedAgentSessions, readHostedApp, readHostedAppMetadata, readHostedDevice, readHostedDeviceLedger, readHostedMacosApp, readHostedNativeLogsCheckpoint, readHostedSessions, readIdleStop, readJsonFile, readJsonObject, readLastBuilds, readLogRecords, readLogTimeline, readLogsSince, readMacosRecord, readMaintenanceAttempt, readMaintenanceState, readNdjsonGenerations, readPullRequestCache, readRunEstimates, readSetupJournal, readStateFile, readStats, readStatsReport, readSwiftpmCacheUsage, readViewedDevices, readWarmRecord, readWorkspaceAgent, readWorkspaceState, recordMatches, recordedBytes, recordingDeviceName, recordingEnabled, rememberHostedAppBlobRename, saturation, serverSetupDir, settingDefinition, settingValueError, settingsJsonSchema, setupJournalFile, sharedCcache, sharedCompilationCache, sharedGradle, sharedPods, sortByTs, statsFile, statsProjectKey, stimBuildDigest, supervisorLogFile, supervisorPidFile, swiftpmCacheUsageFile, tailRead, trimCapacityRefusals, trimCapacityWaits, trimSamples, tryAcquireBuildSlotClaim, unreadableHostedAndroid, unreadableHostedIos, unreadableHostedNative, validEasSessionClaim, validHostedAppArguments, validHostedAppBlob, wholePhases, withConfigLock, withDerivedFacts, withGitChip, withStateReadCache, workspaceAgentDeviceDir, workspaceBuildDetailFile, workspaceDerivedData, workspaceEndedAgentsFile, workspaceEndedAgentsLock, workspaceGradleBuild, workspaceLastUsed, workspaceLogErrorIndex, workspaceLogsDir, workspaceMetadataFile, workspaceRecordingsDir, workspaceStateFile, workspaceStateLock };
|