@stim-cli/core 1.27.0 → 1.29.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.
@@ -160,690 +160,690 @@ interface MachineCacheUsage {
160
160
  declare function machineCacheUsageFile(): string;
161
161
  declare function readMachineCacheUsage(): MachineCacheUsage | null;
162
162
  //#endregion
163
- //#region state/build-capacity.d.ts
164
- declare function buildSlotsDir(): string;
165
- declare function buildSlotPath(index: number): string;
163
+ //#region state/hosted-app.d.ts
164
+ interface HostedAppFile {
165
+ path: string;
166
+ kind: "file" | "exec" | "link";
167
+ size: number;
168
+ sha256: string;
169
+ }
170
+ interface HostedAppOffer {
171
+ session: string;
172
+ attempt: string;
173
+ bundleId: string;
174
+ mode: "development" | "release" | "process";
175
+ devClientScheme?: string;
176
+ arguments?: string[];
177
+ manifest: {
178
+ sha256: string;
179
+ size: number;
180
+ };
181
+ }
182
+ interface HostedAppDelivery extends Omit<HostedAppOffer, "manifest"> {
183
+ state: "receiving" | "installing" | "installed" | "unknown";
184
+ launched: true | "unverified" | null;
185
+ notice?: string;
186
+ }
187
+ interface HostedAppRecord extends HostedAppDelivery {
188
+ manifest: HostedAppOffer["manifest"];
189
+ files: HostedAppFile[];
190
+ }
191
+ declare const HOSTED_APP_CHUNK_BYTES: number;
192
+ declare function hostedAppAttempt(value: unknown): value is string;
193
+ declare function hostedAppArea(session: string, attempt: string, workerHome?: string): string;
194
+ declare function hostedAppBlobs(session: string, workerHome?: string): string;
195
+ /** Transfers cached verification after a rename, only for the same inode, size, mtime and digest. */
196
+ declare function rememberHostedAppBlobRename(from: string, to: string, file: {
197
+ size: number;
198
+ sha256: string;
199
+ }): void;
200
+ /** Checks regular blob bytes, including their size, before content-addressed reuse. */
201
+ declare function validHostedAppBlob(path: string, file: {
202
+ size: number;
203
+ sha256: string;
204
+ }): boolean;
205
+ /** At most 32 plain arguments of 1024 characters (8192 in total), with no NUL, CR or LF. */
206
+ declare function validHostedAppArguments(value: unknown): value is string[];
207
+ declare function parseHostedAppOffer(value: unknown): HostedAppOffer | null;
208
+ /** Relative normalized paths, with no file or link as another entry's ancestor. */
209
+ declare function parseHostedAppManifest(value: unknown): HostedAppFile[] | null;
210
+ /** Reads a server-written receipt; malformed or missing established state remains unresolved. */
211
+ declare function readHostedAppMetadata(session: string, attempt: string, workerHome?: string): Omit<HostedAppRecord, "files">;
212
+ declare function readHostedApp(session: string, attempt: string, workerHome?: string): HostedAppRecord;
213
+ //#endregion
214
+ //#region state/hosted-macos.d.ts
215
+ /** App slots one host offers; a slot names the host-assigned bundle id `<bundleId>.hosted<slot>`. */
216
+ declare const HOSTED_MACOS_APP_SLOTS = 64;
217
+ /** The drivers `hosting.agentDriver` can name: `none` and the first-party driver packages stim-server ships. */
218
+ declare const HOSTED_AGENT_DRIVERS: readonly ["none", "agent-device"];
219
+ type HostedAgentDriverName = (typeof HOSTED_AGENT_DRIVERS)[number];
220
+ /** A driver name a grant or access record can carry; a client resolves only the drivers it knows. */
221
+ declare function hostedAgentDriverName(value: unknown): value is string;
222
+ /** What a host reports in a macOS offer: no simulator is created, so the choice is the host itself. */
223
+ interface HostedMacosChoice {
224
+ architecture: "arm64" | "x86_64";
225
+ macosVersion: string;
226
+ }
227
+ /** A ready hosted macOS session's identity: the host it runs on and the app slot reserved for it. */
228
+ interface HostedMacosDevice extends HostedMacosChoice {
229
+ appSlot: number;
230
+ }
166
231
  /**
167
- * Takes the first free one of `max` `concurrency.maxBuilds` slots, recording `details` and the slot `index`. Returns
168
- * null while every slot is held; throws the claim refusal when no slot is busy but one cannot be resolved.
232
+ * The agent-device lease owner scope a client names on every request. `deviceKey` is `<bundleId>@<pid>`
233
+ * for a macOS app, or `ios:mobile:<udid>` with `backend: ios-instance` for a hosted simulator, or `android:mobile:<serial>` with `backend: android-instance` for a hosted emulator.
169
234
  */
170
- declare function tryAcquireBuildSlotClaim({ max, details }: {
171
- max: number;
172
- details: Record<string, unknown>;
173
- }): {
174
- claim: ClaimHandle;
175
- index: number;
176
- path: string;
177
- } | null;
235
+ interface HostedAgentLease {
236
+ tenant: string;
237
+ runId: string;
238
+ clientId: string;
239
+ deviceKey: string;
240
+ backend?: "ios-instance" | "android-instance";
241
+ }
178
242
  /**
179
- * How busy this machine is for native builds. `loadPerCore` is the 5-minute load average divided by the CPU count,
180
- * rounded to one decimal. `builds` counts this Stim home's runs that hold a live native-run claim and are in
181
- * prebuild, pods or compile here, not on a remote machine, plus the build slots this machine holds for other machines'
182
- * offloaded builds. `maxBuilds` is `concurrency.maxBuilds`, 0 when unlimited.
243
+ * Agent control a driver issues for one installed hosted app. `path` is the session's base route on the host,
244
+ * `scope` the driver's handle that limits it to this one app, and `lease`, for a driver that allocates leases,
245
+ * the scope that pins the allocation.
183
246
  */
184
- interface MachineCapacity {
185
- cpus: number;
186
- loadPerCore: number;
187
- builds: number;
188
- maxBuilds: number;
189
- maxLoadPerCore: number;
247
+ interface HostedAgentIssuedGrant {
248
+ driver: string;
249
+ path: string;
250
+ token: string;
251
+ scope: string;
252
+ lease?: HostedAgentLease;
190
253
  }
191
- declare function maxLoadPerCore(): number;
192
- declare function machineCapacity(): MachineCapacity;
193
- /** Why a machine with this capacity should not take another native build; null when it can. */
194
- declare function saturation(capacity: MachineCapacity): string | null;
195
- //#endregion
196
- //#region state/build-machines.d.ts
197
254
  /**
198
- * What this machine keeps about a remote machine it asked for access. `machine` is the `remote.machines` entry and
199
- * `address` the name it resolved to. Once approved, `serverKey` is the worker's pinned stim-server key: every later
200
- * connection must prove it holds that key. While pending, `ticket` is the setup ticket this machine generated or was
201
- * given, and `expiresAt` its expiry when known. Records of protocol 1, which pinned a tailnet node, are not read.
255
+ * Agent control the host grants a client for one installed hosted app, sent only over that client's approved
256
+ * device-host connection and never journaled. `none` means the host's `hosting.agentDriver` starts no driver.
202
257
  */
203
- interface MachineCredential {
258
+ type HostedAgentGrant = {
259
+ driver: "none";
260
+ } | HostedAgentIssuedGrant;
261
+ /** `app.launch` and `app.attach` results; a host includes `agent` once the app is installed. */
262
+ type HostedAppLaunch = HostedAppDelivery & {
263
+ agent?: HostedAgentGrant;
264
+ };
265
+ /**
266
+ * Agent control as the client hands it to a coding agent: secrets stay in files Stim writes with mode
267
+ * 0600, and `command` shows how to address the hosted app. Never carries a token inline. `setting` names
268
+ * the hosting Mac's setting that turns a driver on.
269
+ */
270
+ type HostedAgentAccess = {
271
+ driver: "none";
272
+ setting: "hosting.agentDriver";
273
+ } | {
274
+ driver: string;
275
+ remoteConfig: string;
276
+ command: string;
277
+ };
278
+ /** Where a workspace's macOS app runs when `stim macos --remote` placed it on another Mac. */
279
+ interface HostedMacosPlacement {
204
280
  machine: string;
205
- transport: "tailscale";
206
- address: string;
207
- state: "pending" | "approved";
208
- requestedAt: string;
209
- serverKey?: string;
210
- deviceId?: string;
211
- ticket?: string;
212
- expiresAt?: string;
281
+ session: string;
282
+ appSlot: number;
283
+ appAttempt: string;
284
+ /** The host-assigned id the running app uses, `hostedMacosBundleId(<offered id>, appSlot)`. */
285
+ bundleId: string;
286
+ agent: HostedAgentAccess;
213
287
  }
214
- type BuildMachineCredential = MachineCredential;
215
- declare const MACHINE_CREDENTIALS_VERSION = 2;
216
- /** A stored credential, or null when it is malformed or from an older version. */
217
- declare function parseMachineCredential(value: unknown): MachineCredential | null;
218
- declare function readBuildMachines(): BuildMachineCredential[];
219
- //#endregion
220
- //#region state/client-identity.d.ts
221
- /** This machine's long-term X25519 key as a client of other machines, in `$STIM_HOME/client-identity.key`. */
222
- declare function clientIdentity(): NoiseKeyPair;
288
+ declare function hostedMacosAppSlot(value: unknown): value is number;
289
+ declare function hostedMacosBundleId(bundleId: string, appSlot: number): string;
290
+ declare function parseHostedMacosChoice(value: unknown): HostedMacosChoice | null;
291
+ declare function parseHostedMacosDevice(value: unknown): HostedMacosDevice | null;
292
+ /** A grant naming a driver this client does not know is not usable; it parses as null. */
293
+ declare function parseHostedAgentGrant(value: unknown): HostedAgentGrant | null;
294
+ declare function parseHostedAgentAccess(value: unknown): HostedAgentAccess | null;
295
+ declare function parseHostedMacosPlacement(value: unknown): HostedMacosPlacement | null;
223
296
  //#endregion
224
- //#region state/config-types.d.ts
225
- type SettingsObject = Record<string, unknown>;
226
- interface SupervisorRecord {
227
- processToken?: string;
228
- pid?: number;
229
- port?: number;
230
- startedAt?: string;
231
- serverPid?: number;
232
- mode?: string;
233
- [key: string]: unknown;
297
+ //#region state/device-host.d.ts
298
+ type HostedDevicePlatform = "ios" | "android" | "macos";
299
+ /** Deadlines stim-server gives each hosted worker run. Clients derive their waits from these. */
300
+ interface DeviceHostLimits {
301
+ prepareMs: number;
302
+ offerMs: number;
303
+ stopMs: number;
304
+ logsMs: number;
305
+ killGraceMs: number;
234
306
  }
235
- interface IosDeviceRecord {
236
- deviceUdid?: string;
237
- deviceName?: string | null;
238
- owned?: boolean;
239
- serial?: string;
240
- simslimManaged?: boolean;
241
- simslimApplied?: {
242
- profile: string;
243
- springBoardPid: number;
244
- };
245
- schemeApprovals?: string[];
246
- [key: string]: unknown;
307
+ declare const DEVICE_HOST_LIMITS: Readonly<DeviceHostLimits>;
308
+ /** How many parked sessions one reserve inspects, and possibly retires, before it creates a device. */
309
+ declare const HOSTED_PARKED_CANDIDATES: number;
310
+ /** How long a cancelled hosted worker run can take to settle: SIGTERM, SIGKILL after the grace, then it is abandoned. */
311
+ declare function hostedWorkerSettleMs(limits?: Pick<DeviceHostLimits, "killGraceMs">): number;
312
+ interface HostedIosChoice {
313
+ deviceTypeId: string;
314
+ runtimeId: string;
315
+ deviceType: string;
316
+ runtime: string;
317
+ architecture: "arm64" | "x86_64";
247
318
  }
248
- interface AndroidDeviceRecord {
249
- avdName?: string;
250
- consolePort?: number;
251
- serial?: string;
252
- kind?: string;
253
- deviceName?: string | null;
254
- owned?: boolean;
255
- setupIncomplete?: boolean;
256
- bootPending?: boolean;
257
- [key: string]: unknown;
319
+ interface HostedIosDevice extends HostedIosChoice {
320
+ udid: string;
321
+ name: string;
258
322
  }
259
- type DeviceRecord = IosDeviceRecord | AndroidDeviceRecord;
260
- interface PlatformRecords {
261
- ios?: IosDeviceRecord;
262
- android?: AndroidDeviceRecord;
263
- [platform: string]: DeviceRecord | undefined;
323
+ interface HostedAndroidChoice {
324
+ systemImage: string;
325
+ deviceProfile: string;
326
+ architecture: "arm64-v8a" | "x86_64";
264
327
  }
265
- interface ProjectRecord {
266
- ports?: Record<string, number>;
267
- metroPort?: number | null;
268
- platforms?: PlatformRecords;
269
- deviceSlots?: Record<string, PlatformRecords>;
270
- supervisor?: SupervisorRecord;
271
- settings?: SettingsObject;
272
- worktreeRoot?: boolean;
273
- worktreeBranch?: string;
274
- worktreeBranchOwned?: boolean;
275
- worktreeMainRoot?: string;
276
- worktreeRemovalComplete?: boolean;
277
- worktreePendingBranchSha?: string;
278
- label?: string;
279
- bundleId?: string;
280
- androidPackage?: string;
281
- isExpo?: boolean;
282
- lastBuild?: Record<string, unknown>;
283
- doctorRuns?: Partial<Record<"ios" | "android", DoctorRunRecord>>;
284
- [key: string]: unknown;
285
- }
286
- interface DoctorRunRecord {
287
- at: string;
288
- version: string;
328
+ interface HostedAndroidDevice extends HostedAndroidChoice {
329
+ avdName: string;
330
+ serial: string;
331
+ consolePort: number;
289
332
  }
290
- interface RepoRecord {
291
- settings?: SettingsObject;
292
- [key: string]: unknown;
333
+ type HostedDevice = HostedIosDevice | HostedAndroidDevice | HostedMacosDevice;
334
+ interface HostedDeviceSelectors {
335
+ deviceType?: string;
336
+ runtime?: string;
337
+ systemImage?: string;
338
+ deviceProfile?: string;
293
339
  }
294
- interface ConcurrencyLimits {
295
- maxBuilds: number;
296
- maxDevices: number;
340
+ interface HostedDeviceOfferRequest extends HostedDeviceSelectors {
341
+ platform: HostedDevicePlatform;
297
342
  }
298
- interface StimConfig {
299
- version?: number;
300
- homeId?: string;
301
- projects: Record<string, ProjectRecord>;
302
- repos: Record<string, RepoRecord>;
303
- tempDir?: unknown;
304
- iosSimulatorApp?: unknown;
305
- androidEmulatorApp?: unknown;
306
- optimizations?: unknown;
307
- concurrency?: {
308
- maxBuilds?: unknown;
309
- maxDevices?: unknown;
343
+ type HostedNativeOffer = {
344
+ resources: {
345
+ cpus: number;
346
+ /** Five-minute system load divided by available CPU count. */
347
+ loadPerCore: number;
348
+ maxLoadPerCore?: number;
349
+ memoryFreeBytes: number;
350
+ memoryPressure: "normal" | "warning" | "critical" | null;
351
+ /** Available bytes on the Stim home volume, not necessarily the Android AVD volume. */
352
+ workerDiskFreeBytes: number | null;
353
+ localDevices?: number | {
354
+ unknown: string;
355
+ };
310
356
  };
311
- pool?: {
312
- iosParkedMax?: unknown;
313
- androidParkedMax?: unknown;
357
+ declined: string | null;
358
+ } & ({
359
+ platform: "ios";
360
+ choice: HostedIosChoice | null;
361
+ } | {
362
+ platform: "android";
363
+ choice: HostedAndroidChoice | null;
364
+ } | {
365
+ platform: "macos";
366
+ choice: HostedMacosChoice | null;
367
+ });
368
+ type HostedDeviceOffer = HostedNativeOffer & {
369
+ capacity: {
370
+ running: number;
371
+ max: number;
372
+ available: number | null;
373
+ local?: number | {
374
+ unknown: string;
375
+ };
314
376
  };
377
+ };
378
+ interface HostedDeviceRequest extends HostedDeviceOfferRequest {
379
+ workspace: string;
380
+ slot: string;
381
+ attempt: string;
382
+ }
383
+ type HostedDevicePhase = "preparing" | "ready" | "stopping" | "stopped" | "unknown";
384
+ interface HostedDeviceSession extends HostedDeviceRequest {
385
+ id: string;
386
+ client: string;
387
+ state: HostedDevicePhase;
388
+ device: HostedDevice | null;
389
+ consolePort?: number;
390
+ appSlot?: number;
391
+ createdAt: string;
315
392
  parked?: {
316
- ios?: unknown;
317
- android?: unknown;
318
- };
319
- caches?: {
320
- buildCache?: unknown;
321
- metroCache?: unknown;
322
- };
323
- hosting?: {
324
- agentDriver?: unknown;
325
- };
326
- debug?: {
327
- logs?: unknown;
328
- };
329
- server?: {
330
- buildsPaused?: unknown;
331
- devicesPaused?: unknown;
332
- acceptClientBuilds?: unknown;
333
- maxLoadPerCore?: unknown;
334
- workerRoot?: unknown;
335
- gradleDaemonIdleMinutes?: unknown;
336
- };
337
- remote?: {
338
- machines?: unknown;
339
- easMaxDurationMinutes?: unknown;
340
- build?: unknown;
341
- buildPoolDisabled?: unknown;
342
- devicePoolDisabled?: unknown;
393
+ at: string;
343
394
  };
344
- [key: string]: unknown;
395
+ notice?: string;
396
+ appAttempt?: string;
397
+ agent?: HostedAgentGrant;
398
+ metroPort?: number;
399
+ clientMetroPort?: number;
345
400
  }
346
- type Config = StimConfig;
347
- //#endregion
348
- //#region state/automatic-pools.d.ts
349
- type AutomaticPoolRole = "build" | "device";
350
- declare function automaticMachineEnabled(role: AutomaticPoolRole, machine: string, config?: StimConfig | null): boolean;
351
- declare function requireAutomaticMachine(role: AutomaticPoolRole, machine: string): void;
352
- /**
353
- * Refuses a pool with no enabled member: local, or a configured remote approved for the role. `canHost` excludes
354
- * device-pool remotes that can never host, such as a Linux machine.
355
- */
356
- declare function validateAutomaticPool(role: AutomaticPoolRole, config: StimConfig, canHost?: (machine: string) => boolean): void;
401
+ declare function deviceHostRoot(): string;
402
+ declare function deviceHostArea(id: string): string;
403
+ declare function parseHostedChoice(value: unknown): HostedIosChoice | null;
404
+ declare function parseHostedDevice(value: unknown): HostedIosDevice | null;
405
+ declare function parseHostedAndroidDevice(value: unknown): HostedAndroidDevice | null;
406
+ /** Android Emulator -help-port defines the supported even console ports as 5554 through 5584. */
407
+ declare function hostedConsolePort(value: unknown): value is number;
408
+ declare function parseHostedPlatformDevice(value: unknown, platform: HostedDevicePlatform): HostedDevice | null;
409
+ declare function hostedDeviceId(device: HostedDevice): string;
410
+ declare function parseHostedRequest(value: unknown): HostedDeviceRequest | null;
411
+ declare function parseHostedOfferRequest(value: unknown): HostedDeviceOfferRequest | null;
412
+ declare function parseHostedLocalDeviceCount(value: unknown): number | {
413
+ unknown: string;
414
+ } | null;
415
+ declare function parseHostedNativeOffer(value: unknown): HostedNativeOffer | null;
416
+ /** A missing established journal or malformed record is a refusal, never an empty reservation set. */
417
+ declare function readHostedSessions(): HostedDeviceSession[];
418
+ /** The private worker's exact device record; an absent record is unresolved after creation started. */
419
+ declare function readHostedDevice(home: string): HostedIosDevice;
420
+ declare function readHostedDevice(home: string, platform: "android"): HostedAndroidDevice;
421
+ declare function readHostedDevice(home: string, platform: "macos"): HostedMacosDevice;
422
+ declare function readHostedDevice(home: string, platform: HostedDevicePlatform): HostedDevice;
423
+ declare function readHostedDeviceLedger(home: string, options?: {
424
+ required?: boolean;
425
+ }): {
426
+ ios: string[];
427
+ android: string[];
428
+ web: string[];
429
+ } | null;
430
+ /** Hosted sessions use isolated homes: only one exact device of their platform may appear in this ledger. */
431
+ declare function assertHostedDeviceLedger(home: string, id: string, platform?: HostedDevicePlatform, options?: {
432
+ allowEmpty?: boolean;
433
+ }): "listed" | "empty";
357
434
  //#endregion
358
- //#region state/config.d.ts
359
- declare function withConfigLock<T>(fn: () => T): T;
360
- /** Waits for the config lock without blocking the event loop. The signal cancels acquisition; `fn` runs synchronously. */
361
- declare function withConfigLockAsync<T>(fn: () => T, signal?: AbortSignal): Promise<T>;
362
- declare function configCorruptRepair(path?: string): string;
363
- /** The build worker root: `server.workerRoot` when it is absolute, else `$STIM_HOME/build-worker`. */
364
- declare function buildWorkerRoot(config?: Config | null): string;
365
- /**
366
- * A short directory at the drive root that stands in for the build worker directory `path` on Windows. The NDK's
367
- * ninja is not long-path aware (STIM_PATH_TOO_LONG), and it opens paths under the checkout and under the Gradle user
368
- * home's transforms cache, both of which are too long under the worker root. A subst drive does not help: Node's
369
- * native realpath, which expo-modules-autolinking uses, resolves it back to the long path.
370
- */
371
- declare function windowsShortDir(path: string, systemDrive?: string): string;
372
- /** The ASCII key a Windows build worker records beside a `windowsShortDir` to tell which `kind` and `path` it serves. */
373
- declare function windowsShortDirKey(kind: "checkout" | "gradle", path: string): string;
374
- declare function loadConfig(): Config | null;
375
- declare function getProject(projectPath: string): ProjectRecord | null;
376
- declare function getConcurrencyLimits({ env }?: {
377
- env?: NodeJS.ProcessEnv;
378
- }): ConcurrencyLimits;
379
- interface ParkedMax {
380
- max: number;
381
- error: string | null;
435
+ //#region state/workspace-state.d.ts
436
+ interface WorkspaceState {
437
+ supervisor?: Record<string, unknown>;
438
+ collectors?: Record<string, unknown>;
439
+ lastBuild?: Record<string, unknown>;
440
+ launches?: Record<string, unknown>;
441
+ remoteDevice?: Record<string, unknown>;
442
+ metroTunnel?: Record<string, unknown>;
443
+ [key: string]: unknown;
382
444
  }
383
- declare function getParkedMax(platform: "ios" | "android", { config, env }?: {
384
- config?: Config | null;
385
- env?: NodeJS.ProcessEnv;
386
- }): ParkedMax;
387
- declare function getProjectSettings(projectPath: string): Record<string, unknown>;
388
- declare function getRepoSettings(gitCommonDir: string): Record<string, unknown>;
389
- declare function isPathPrefix(prefix: string, path: string): boolean;
390
- declare function findEnclosingWorktreeRoot(projectPath: string): string | null;
391
- declare function allMetroPorts(): number[];
392
- //#endregion
393
- //#region state/debug-log.d.ts
394
- declare const DEBUG_ENV = "STIM_DEBUG";
395
- declare function debugLogDir(): string;
396
- /** The `STIM_DEBUG` override (1/true on, 0/false off, anything else off), else the machine setting `debug.logs`. Never throws. */
397
- declare function debugLoggingEnabled(env?: NodeJS.ProcessEnv): boolean;
398
- interface DebugLog {
399
- /** Whether debug logging is on right now; re-read every few seconds so a long-running process follows the setting. */
400
- enabled(): boolean;
401
- /** Appends one record to `$STIM_HOME/logs/debug/<component>.ndjson` when enabled. Never throws; keys that look like secrets are redacted. */
402
- log(event: string, fields?: Record<string, unknown>): void;
445
+ declare const IDLE_STOP_KEY = "devServerStop";
446
+ /** Why the supervisor last stopped the dev server on its own: `metro.idleStopMinutes` with no use. */
447
+ interface IdleStopRecord {
448
+ reason: "idle";
449
+ at: string;
450
+ idleMinutes: number;
451
+ }
452
+ declare function readIdleStop(state: WorkspaceState | null | undefined): IdleStopRecord | null;
453
+ declare const DEVICE_RECLAIM_REASON = "reclaimed for a waiting run";
454
+ declare const DEVICE_IDLE_SHUTDOWN_KEY = "deviceIdleShutdowns";
455
+ /** An owned simulator or emulator shut down after being idle. */
456
+ interface DeviceIdleShutdownRecord {
457
+ at: string;
458
+ idleMinutes: number;
459
+ reason?: string;
460
+ }
461
+ /** The recorded idle shutdowns by device slot key (`ios`, `android`, `ios:<slot>`). */
462
+ declare function readDeviceIdleShutdowns(state: WorkspaceState | null | undefined): Record<string, DeviceIdleShutdownRecord>;
463
+ declare const DEV_SERVER_STOP_REQUEST_KEY = "devServerStopRequest";
464
+ /** A Stim component's intent to stop the supervisor with `processToken`, written before it signals. */
465
+ interface DevServerStopRequest {
466
+ processToken: string;
467
+ by: string;
468
+ pid: number;
469
+ at: string;
470
+ workspace?: string;
403
471
  }
472
+ declare function readDevServerStopRequest(state: WorkspaceState | null | undefined): DevServerStopRequest | null;
404
473
  /**
405
- * Debug-level records for one component (`cli` or `server`). Off by default; `STIM_DEBUG=1` or `debug.logs` turns it
406
- * on. Callers pass names, durations and codes, never arguments, environment values, tokens or tickets.
474
+ * Why the dev server last stopped, stored under `IDLE_STOP_KEY`. `requested` names the Stim component that
475
+ * asked; `signal` is a SIGTERM or SIGINT no Stim component asked for; `server-exited` is the dev server
476
+ * process exiting on its own.
407
477
  */
408
- declare function createDebugLog(component: "cli" | "server", { env, now, base }?: {
409
- env?: NodeJS.ProcessEnv;
410
- now?: () => number;
411
- base?: () => Record<string, unknown>;
412
- }): DebugLog;
413
- //#endregion
414
- //#region state/trace.d.ts
415
- declare const TRACE_ENV = "STIM_TRACE";
416
- /**
417
- * `args` with the value of any flag or assignment that looks like a secret, credentials in URLs, token shapes and
418
- * the `exact` values replaced. `program` selects the flags that carry a credential without a secret-looking name,
419
- * such as `curl -u`.
420
- */
421
- declare function redactArgv(args: readonly string[], exact?: readonly string[], program?: string): string[];
422
- /** A shell command line with the same redactions as `redactArgv`, clipped to a fixed length. */
423
- declare function redactCommandLine(command: string, exact?: readonly string[]): string;
424
- type TraceArgs = Record<string, unknown>;
425
- type EndSpan = (args?: TraceArgs) => void;
426
- type Args = TraceArgs;
427
- interface TracePhases {
428
- /** Ends the phase in progress and starts `name`; entering the phase already in progress does nothing. */
429
- enter(name: string, args?: Args): void;
430
- end(args?: Args): void;
431
- }
432
- interface Trace {
433
- enabled(): boolean;
434
- /** Starts a span; the returned function ends it, with optional extra args. */
435
- begin(name: string, args?: TraceArgs): EndSpan;
436
- /** `begin` for the outermost span of a process: it ends at exit with the exit code. */
437
- command(name: string, args?: TraceArgs): void;
438
- /** `begin` for a child process: same event with the `exec` category. */
439
- beginExec(name: string, args?: TraceArgs): EndSpan;
440
- /** Runs `fn` inside a span and ends it when `fn` returns or throws. */
441
- span<T>(name: string, fn: () => T, args?: TraceArgs): T;
442
- /** `span` for an async `fn`: ends when its promise settles. */
443
- spanAsync<T>(name: string, fn: () => Promise<T>, args?: TraceArgs): Promise<T>;
444
- mark(name: string, args?: TraceArgs): void;
445
- /** A strip of consecutive, non-overlapping phases on its own track. */
446
- phases(track: string): TracePhases;
447
- }
478
+ type DevServerStopRecord = IdleStopRecord | {
479
+ reason: "requested";
480
+ at: string;
481
+ by: string;
482
+ byPid: number;
483
+ byWorkspace?: string;
484
+ } | {
485
+ reason: "signal";
486
+ at: string;
487
+ signal: string;
488
+ } | {
489
+ reason: "server-exited";
490
+ at: string;
491
+ mode: string;
492
+ code: number | null;
493
+ signal: string | null;
494
+ };
495
+ /** A stopped dev server's last cause in status; `vanished` is a supervisor proven gone that recorded none. */
496
+ type MetroLastStop = DevServerStopRecord | {
497
+ reason: "vanished";
498
+ pid: number;
499
+ startedAt: string | null;
500
+ };
501
+ declare function readDevServerStop(state: WorkspaceState | null | undefined): DevServerStopRecord | null;
502
+ /** Each platform's latest run; `lastBuild` holds whichever platform ran last. */
503
+ declare const LAST_BUILD_KEYS: Readonly<Record<StatsPlatform, string>>;
504
+ declare const BUILD_HISTORY_KEY = "buildHistory";
505
+ declare const BUILD_HISTORY_LIMIT = 10;
506
+ /** A recorded miss reason in the shape status reports, without `baseline.cacheKey`; null when malformed. */
507
+ declare function parseMissReason(value: unknown): BuildMissReason | null;
448
508
  /**
449
- * Timing spans for one stim process tree, written as Chrome trace events (open the file in Perfetto or
450
- * chrome://tracing) when `STIM_TRACE=<path>` is set. Unset, every call is one environment check. A stim command
451
- * (`trace.command`) truncates the file and writes `[`; every other process that inherits the variable appends one
452
- * event per line, creating the file if it is missing, so the file has no closing bracket. Child process arguments are written through `redactArgv`.
509
+ * The first diagnostics of a failed build in the shape a last-build record stores and `status` reports, the
510
+ * ones with a source position first.
453
511
  */
454
- declare const trace: Trace;
455
- //#endregion
456
- //#region state/home-identity.d.ts
457
- /** The persistent, nonsecret identity of this Stim home; null until the CLI initializes it. */
458
- declare function readHomeIdentity(config?: Config | null): string | null;
459
- //#endregion
460
- //#region state/json-file.d.ts
512
+ declare function buildDiagnostics(value: unknown): BuildDiagnostic[];
513
+ declare function readDevicePlacement(state: WorkspaceState | null, platform: StatsPlatform, slot?: string): DevicePlacement | null;
514
+ declare function readLastBuilds(state: WorkspaceState | null | undefined): Partial<Record<StatsPlatform, LastBuildReport>>;
515
+ /** Each platform's recorded runs, newest first, at most `BUILD_HISTORY_LIMIT` each. */
516
+ declare function readBuildHistory(state: WorkspaceState | null | undefined): Partial<Record<BuildPlatform, BuildHistoryEntry[]>>;
517
+ declare function readWorkspaceState(root: string): WorkspaceState | null;
518
+ declare function lastUseFrom(state: WorkspaceState | null, logMtimes: readonly number[]): number;
461
519
  /**
462
- * Runs `fn` with each state file read from disk at most once: inside it, including after an `await`, `readStateFile`
463
- * returns the text of the first read of that path, and a path found missing stays missing. Callers still parse every time, so no two
464
- * share a mutable object. Only for computations that write none of the files they read.
520
+ * When an ios, android or macos run last finished in the workspace, from its recorded builds (a cache hit counts),
521
+ * in epoch milliseconds; NaN when it records none. Dev server starts, reloads and logs do not count.
465
522
  */
466
- declare function withStateReadCache<T>(fn: () => T): T;
467
- declare function readStateFile(path: string): string;
468
- declare function readJsonFile(path: string): unknown;
469
- declare function isJsonObject(value: unknown): value is Record<string, unknown>;
470
- declare function readJsonObject(path: string): Record<string, unknown> | null;
471
- //#endregion
472
- //#region state/ledgers.d.ts
473
- type CreatedDevicePlatform = "ios" | "android" | "web";
474
- /** `web` lists the absolute paths of browser profile directories Stim created. */
475
- interface CreatedDevices {
476
- ios: ReadonlySet<string>;
477
- android: ReadonlySet<string>;
478
- web: ReadonlySet<string>;
479
- }
480
- declare function readCreatedDevices(): CreatedDevices;
481
- interface EasSessionClaim {
482
- sessionId: string;
483
- name: string;
484
- platform: "ios" | "android";
485
- workspaceRoot: string;
486
- workspaceHome: string;
487
- stateFile: string;
488
- }
489
- interface EasSessionLedgerRead {
490
- claims: Map<string, EasSessionClaim>;
491
- notice: string | null;
492
- safe: boolean;
493
- }
494
- /** Returns the claim normalized to canonical paths, or null when it is not a valid claim for `id`. */
495
- declare function validEasSessionClaim(id: string, value: unknown): EasSessionClaim | null;
496
- declare function readEasSessionLedger(root?: string): EasSessionLedgerRead;
497
- //#endregion
498
- //#region state/error-entries.d.ts
523
+ declare function lastNativeUseFrom(state: WorkspaceState | null): number;
524
+ declare const MAINTENANCE_LOG_NAME = "maintenance.ndjson";
499
525
  /**
500
- * The number of rows a log list shows for `records`, an error query's result in log order. Stim Desktop's
501
- * `LogEntryList` and the phone's `groupRecords` group the same way and replay the same vectors: in Expo's dev
502
- * server one failed bundle is a `Bundling failed` marker line, the error line and a failed bundle response,
503
- * which the apps list as one row, and every other record is its own row.
526
+ * When the workspace was last used, in epoch milliseconds. A workspace that recorded no use falls back to the mtime
527
+ * of its workspace.json, which is written once when the workspace is created, so it is a lower bound on the last
528
+ * use. NaN when its state.json exists but does not parse, or when workspace.json cannot be read either.
504
529
  */
505
- declare function countErrorEntries(records: readonly NdjsonRecord[]): number;
506
- //#endregion
507
- //#region state/fingerprint-memo.d.ts
508
- declare const FINGERPRINT_MEMO_SCHEMA = 1;
509
- /** Full native fingerprints shared by every workspace of this Stim home, one file per platform and static digest. */
510
- declare function fingerprintMemoDir(platform?: string): string;
511
- declare function fingerprintMemoFile(platform: string, digest: string): string;
512
- declare function fingerprintMemoLock(): string;
513
- /** The full fingerprint a tree with this static digest produced, written by the CLI. */
514
- interface FingerprintMemoRecord {
515
- schema: typeof FINGERPRINT_MEMO_SCHEMA;
516
- platform: string;
517
- digest: string;
518
- recordedAt: number;
519
- hash: string;
520
- sources: unknown[];
521
- /** The packages the static check counted as native, to name the ones a disagreeing check found it missed. */
522
- nativePackages: string[];
523
- }
524
- declare function readFingerprintMemo(platform: string, digest: string): FingerprintMemoRecord | null;
525
- //#endregion
526
- //#region state/logs-query.d.ts
527
- interface SinceResult {
528
- ms?: number;
529
- error?: string;
530
- }
531
- interface GrepResult {
532
- re?: RegExp;
533
- error?: string;
534
- }
535
- interface MarkerWindow {
536
- launchTs: number | null;
537
- bundleTs: number | null;
538
- pageLoadTs: number | null;
539
- }
540
- interface QueryCriteria {
541
- slot?: string;
542
- includeAppDeviceErrors?: boolean;
543
- sources?: string[];
544
- minLevel?: string;
545
- grep?: RegExp;
546
- sinceTs?: number;
547
- errorsOnly?: boolean;
548
- markerTs?: number;
549
- bundleMarkerTs?: number;
550
- }
551
- interface TailState {
552
- offset: number;
553
- partial: string;
554
- }
555
- declare function parseSince(text: unknown): SinceResult;
556
- declare function compileGrep(pattern: unknown): GrepResult;
557
- declare const ERROR_SOURCES: string[];
558
- declare function markerWindow(records: NdjsonRecord[]): MarkerWindow;
530
+ declare function workspaceLastUsed(root: string): number;
531
+ declare const WARM_KEY = "warm";
532
+ /** How long a successful warm keeps a workspace `ready` when nothing runs in it. */
533
+ declare const READY_PHASE_MS: number;
559
534
  /**
560
- * The platform a bundle marker speaks for, lowercased: its `platform` field, or the tag @expo/cli prints before
561
- * "Bundled" ("iOS", "Android", "Web", "RSC(iOS)" as ios, or an environment tag such as "DOM"). Null when unknown.
535
+ * What `stim worktree warm` last recorded in a workspace: a warm in progress, held by the ownership claim
536
+ * `claim` names, or a successful warm that finished at `at`.
562
537
  */
563
- declare function bundleMarkerPlatform(record: NdjsonRecord): string | null;
564
- declare function recordMatches(record: NdjsonRecord | null | undefined, criteria?: QueryCriteria): boolean;
565
- declare function buildCriteria({ slot, sources, minLevel, since, grep, errorsOnly, markerTs, bundleMarkerTs, now }?: {
566
- slot?: string;
567
- sources?: string[];
568
- minLevel?: string;
569
- since?: string | null;
570
- grep?: string | RegExp | null;
571
- errorsOnly?: boolean;
572
- markerTs?: number;
573
- bundleMarkerTs?: number;
574
- now?: number;
575
- }): QueryCriteria;
576
- declare function logFiles(dir: string): string[];
577
- declare function fileSizes(dir: string): Record<string, number>;
578
- declare function readLogRecords(dir: string, include?: (name: string) => boolean): NdjsonRecord[];
579
- declare const NATIVE_CRASH_LOG_PREFIX = "native-crash-";
580
- /** Reads the CLI timeline with retained native crash records after other records at the same timestamp. */
581
- declare function readLogTimeline(dir: string, records?: NdjsonRecord[]): NdjsonRecord[];
582
- declare function isExpoErrorContext(record: NdjsonRecord, event: unknown): boolean;
583
- type ExpoContextTarget = "msg" | "field";
538
+ type WarmRecord = {
539
+ phase: "warming";
540
+ step: WarmStep;
541
+ startedAt: string;
542
+ claim: {
543
+ root: string;
544
+ claimId: string;
545
+ };
546
+ } | {
547
+ phase: "ready";
548
+ at: string;
549
+ };
550
+ declare function readWarmRecord(state: WorkspaceState | null | undefined): WarmRecord | null;
551
+ declare const WORKSPACE_AGENT_KEY = "agentSession";
584
552
  /**
585
- * Adds the code frame and stack lines Expo printed after each Expo error record, either joined into
586
- * `msg` or as a `context` string array.
553
+ * The coding-agent session whose shell ran the Stim command that last recorded `lastUsedAt`, with that command's
554
+ * working directory as `cwd` and its time as `lastActiveAt`.
587
555
  */
588
- declare function attachExpoErrorContext(all: NdjsonRecord[], matched: NdjsonRecord[], into?: ExpoContextTarget): NdjsonRecord[];
589
- interface LogQueryOptions {
590
- slot?: string;
591
- sources?: string[];
592
- minLevel?: string;
593
- since?: string;
594
- grep?: string | RegExp;
595
- errorsOnly?: boolean;
596
- now?: number;
556
+ declare function readWorkspaceAgent(state: WorkspaceState | null | undefined): AgentSession | null;
557
+ //#endregion
558
+ //#region state/hosted-native.d.ts
559
+ interface HostedNativePlacement<D> {
560
+ machine: string;
561
+ session: string;
562
+ appAttempt: string;
563
+ device: D | null;
564
+ selected: string;
565
+ reason?: string;
566
+ agent: HostedAgentAccess;
597
567
  }
598
- interface LogQueryPlan {
599
- /** Whether a record belongs to the queried slot's timeline, before any other filter. */
600
- inTimeline: (record: NdjsonRecord) => boolean;
601
- matches: (record: NdjsonRecord) => boolean;
602
- launchTs: number | null;
603
- sinceTs: number | undefined;
604
- /**
605
- * Whether no record with a timestamp at most `maxTs`, or without one when `maxTs` is Infinity, can match.
606
- * `slots` lists every `slot ?? 'default'` value the records can carry; without it, a query that depends on
607
- * per-slot launch markers cannot rule out any timestamp.
608
- */
609
- excludesUpTo: (maxTs: number, slots?: ReadonlySet<unknown>) => boolean;
610
- }
611
- /** Decides which records a query matches from the timeline's marker records alone. */
612
- declare function planLogQuery(markers: readonly NdjsonRecord[], { slot, sources, minLevel, since, grep, errorsOnly, now }?: LogQueryOptions): LogQueryPlan;
613
- declare function queryLogs({ slot, dir, records, sources, minLevel, since, grep, tail, errorsOnly, errorContext, now }?: LogQueryOptions & {
614
- dir?: string;
615
- records?: NdjsonRecord[];
616
- tail?: number;
617
- errorContext?: boolean;
618
- }): NdjsonRecord[];
619
- /** Queries the JSON timeline, including Expo context fields on errors as `stim logs --json` does. */
620
- declare function queryJsonLogs(options?: Parameters<typeof queryLogs>[0]): NdjsonRecord[];
621
- declare function tailRead(prev: TailState | null | undefined, size: number): {
622
- start: number;
623
- prev: TailState;
568
+ declare function parseHostedNativePlacement(value: unknown, platform: "ios"): HostedNativePlacement<HostedIosDevice> | null;
569
+ declare function parseHostedNativePlacement(value: unknown, platform: "android"): HostedNativePlacement<HostedAndroidDevice> | null;
570
+ declare function parseHostedNativePlacement(value: unknown, platform: "ios" | "android"): HostedNativePlacement<HostedIosDevice | HostedAndroidDevice> | null;
571
+ declare function hostedNativeRecords(state: WorkspaceState | null, platform: "ios" | "android"): Record<string, unknown>;
572
+ declare function unreadableHostedNative(slot: string, platform: "ios" | "android"): string;
573
+ //#endregion
574
+ //#region state/hosted-android.d.ts
575
+ /** A workspace slot's placement; null device means its reservation has not reached ready yet. */
576
+ interface HostedAndroidPlacement extends HostedNativePlacement<HostedAndroidDevice> {}
577
+ /** Public hosting facts; the host's serial, AVD name and private Metro links are excluded. */
578
+ type HostedAndroidStatus = {
579
+ machine: string;
580
+ session: string;
581
+ selected: string;
582
+ reason?: string;
583
+ agent: HostedAgentAccess;
584
+ device: {
585
+ name: string;
586
+ systemImage: string;
587
+ api: number;
588
+ } | null;
589
+ /** The latest session probe in status, independent of a conflicting local emulator's state. */
590
+ state?: string;
624
591
  };
625
- declare function advanceTail(prev: TailState | null | undefined, chunk: string | null | undefined, size: number): {
626
- state: TailState;
627
- records: NdjsonRecord[];
592
+ declare function parseHostedAndroidPlacement(value: unknown): HostedAndroidPlacement | null;
593
+ declare function hostedAndroidStatus(placement: HostedAndroidPlacement): HostedAndroidStatus;
594
+ declare function hostedAndroidRecords(state: WorkspaceState | null): Record<string, unknown>;
595
+ declare function unreadableHostedAndroid(slot: string): string;
596
+ declare function hostedAndroidPlacements(state: WorkspaceState | null): Record<string, HostedAndroidPlacement>;
597
+ declare function hostedAndroidDeviceName(device: HostedAndroidDevice): string;
598
+ //#endregion
599
+ //#region state/hosted-ios.d.ts
600
+ /** A workspace slot's placement; null device means its reservation has not reached ready yet. */
601
+ interface HostedIosPlacement extends HostedNativePlacement<HostedIosDevice> {}
602
+ /** Public hosting facts; the host's UDID and private Metro links are excluded. */
603
+ type HostedIosStatus = {
604
+ machine: string;
605
+ session: string;
606
+ selected: string;
607
+ reason?: string;
608
+ agent: HostedAgentAccess;
609
+ device: {
610
+ name: string;
611
+ runtime: string;
612
+ } | null;
613
+ /** The latest session probe in status, independent of a conflicting local simulator's state. */
614
+ state?: string;
628
615
  };
629
- declare function followLogs({ dir, onRecord, criteria, intervalMs, offsets, identities: seededIdentities, errorContext }: {
630
- dir: string;
631
- onRecord: (record: NdjsonRecord) => void;
632
- criteria?: QueryCriteria;
633
- /** An explicit polling interval. Omit for filesystem hints with a ten-second repair scan. */
634
- intervalMs?: number;
635
- offsets?: Record<string, number> | null;
636
- /** `fileIdentity` of each file `offsets` was read from, so a replacement made since is recognized. */
637
- identities?: Record<string, string> | null;
638
- /** Holds each Expo error until its code frame and stack stop growing, then attaches them. */
639
- errorContext?: ExpoContextTarget;
640
- }): () => void;
641
- declare function fileIdentity(stat: BigIntStats): string | undefined;
642
- declare function sortByTs(records: NdjsonRecord[]): NdjsonRecord[];
616
+ declare function parseHostedIosPlacement(value: unknown): HostedIosPlacement | null;
617
+ declare function hostedIosStatus(placement: HostedIosPlacement): HostedIosStatus;
618
+ declare function hostedIosRecords(state: WorkspaceState | null): Record<string, unknown>;
619
+ declare function unreadableHostedIos(slot: string): string;
620
+ declare function hostedIosPlacements(state: WorkspaceState | null): Record<string, HostedIosPlacement>;
643
621
  //#endregion
644
- //#region state/log-reader.d.ts
645
- interface LogDirQuery extends LogQueryOptions {
646
- tail?: number;
647
- /** Records from outside the directory, such as agent-device actions; they follow its records at equal timestamps. */
648
- extra?: readonly NdjsonRecord[];
622
+ //#region state/maintenance.d.ts
623
+ type MaintenanceMode = "off" | "report" | "on";
624
+ type MaintenanceCheck = "pressure" | "size" | "worktree" | "sweep" | "idle";
625
+ type MaintenanceActionKind = "would-clear-outputs" | "would-trim-cache" | "would-empty-cache" | "would-shutdown-device" | "would-stop-workspace" | "would-remove-worktree" | "would-remove-orphan" | "would-unregister-cache" | "would-delete-parked-device" | "would-delete-devices" | "would-clear-build-dirs" | "would-delete-dependencies" | "would-stop-gradle-daemons" | "would-delete-gradle-version" | "would-delete-temp-leftovers";
626
+ /** Disk pressure: 0 above every threshold, 1 under the budget floor, 2 under the urgent threshold, 3 under the critical threshold or the hard floor. */
627
+ type DiskPressureLevel = 0 | 1 | 2 | 3;
628
+ interface MaintenanceAction {
629
+ kind: MaintenanceActionKind;
630
+ target: string;
631
+ bytes: number;
632
+ reason: string;
633
+ workspace?: string;
634
+ dir?: string;
635
+ olderThanDays?: number;
636
+ check?: "worktree" | "sweep" | "disk" | "idle";
637
+ /** The disk pressure level that planned the action; it runs only while free disk stays under that level's floor. */
638
+ level?: 2 | 3;
649
639
  }
650
- /**
651
- * An indexed view of a workspace log directory, read in timestamp order without loading whole files. Each file
652
- * generation is indexed once per process (block timestamp bounds, markers, slots and launch attempts) and later
653
- * views read only what was appended since. Close it to release the open files.
654
- */
655
- interface LogDir {
656
- /** Bytes read from each current `.ndjson` file, through its last complete line. */
657
- readonly sizes: Record<string, number>;
658
- /** `fileIdentity` of each current `.ndjson` file that `sizes` was read from. */
659
- readonly identities: Record<string, string>;
660
- /** The oldest timestamp in the directory. */
661
- readonly firstTs: number | undefined;
662
- /** Launch attempt records outside native crash files, in timeline order. */
663
- launchAttempts(): NdjsonRecord[];
664
- /** The records `queryJsonLogs` returns for this directory's timeline followed by `extra`. */
665
- query(query?: LogDirQuery): Promise<NdjsonRecord[]>;
666
- /** Every matching record in timestamp order, in batches of at most 500, with the same filters and context as query. */
667
- batches(query?: Omit<LogDirQuery, "tail">): AsyncIterable<NdjsonRecord[]>;
668
- /** Prepares the directory marker predicate for records without markers, such as attributed agent actions. */
669
- filter(query?: LogQueryOptions): (record: NdjsonRecord) => boolean;
670
- close(): Promise<void>;
640
+ interface MaintenanceSize {
641
+ name: string;
642
+ dir: string;
643
+ bytes: number;
644
+ measuredAt: number;
645
+ category: "workspace-outputs" | "build-cache" | "metro-cache" | "compilation-cache" | "ccache" | "owned-devices" | "other";
646
+ workspace?: string;
647
+ idleDays?: number | null;
648
+ blocked?: string;
671
649
  }
672
650
  /**
673
- * Opens `dir` for reading. With `order: 'timeline'`, native crash files follow every other file at equal
674
- * timestamps, as `readLogTimeline` orders them; with `'name'`, every file is ranked by name, as `readLogRecords`.
675
- */
676
- declare function openLogDir(dir: string, { order, signal }?: {
677
- order?: "timeline" | "name";
678
- signal?: AbortSignal;
679
- }): Promise<LogDir>;
680
- //#endregion
681
- //#region state/ndjson-tail.d.ts
682
- /**
683
- * Returns a reader of `file` and its rotated generation that yields, on every call, the records a full
684
- * `readNdjsonGenerations(file)` would, while reading only the bytes appended since the previous call. A file renamed
685
- * to its rotated generation between two calls is finished from there; any other replacement or a shrunk file is
686
- * read again from its start. A file rewritten in place to at least its previous length is not detected.
687
- */
688
- declare function createNdjsonTail(file: string): () => NdjsonRecord[];
689
- //#endregion
690
- //#region state/paths.d.ts
691
- declare function getConfigPath(): string;
692
- declare function configLockPath(): string;
693
- declare function workspaceMetadataFile(projectRoot: string): string;
694
- declare function workspaceAgentDeviceDir(projectRoot: string): string;
695
- declare function workspaceLogsDir(projectRoot: string): string;
696
- /** Device screen footage stim-server records for the workspace, one directory per platform and slot. */
697
- declare function workspaceRecordingsDir(projectRoot: string): string;
698
- declare function workspaceLogErrorIndex(projectRoot: string): string;
699
- declare function workspaceDerivedData(projectRoot: string): string;
700
- declare function workspaceGradleBuild(projectRoot: string): string;
701
- declare function workspaceBuildDetailFile(projectRoot: string): string;
702
- /** The agent sessions that stopped running in the workspace in the last `ENDED_AGENT_RETENTION_MS`. */
703
- declare function workspaceEndedAgentsFile(projectRoot: string): string;
704
- declare function workspaceEndedAgentsLock(projectRoot: string): string;
705
- declare function supervisorPidFile(projectRoot: string): string;
706
- declare function workspaceStateFile(projectRoot: string): string;
707
- /** The dependency directories `gc --delete` deleted from the workspace's worktree, apart from state.json so writing it is not workspace activity. */
708
- declare function workspaceLightenedFile(projectRoot: string): string;
709
- declare function workspaceStateLock(projectRoot: string): string;
710
- declare function supervisorLogFile(projectRoot: string): string;
711
- declare function emulatorLogFile(projectRoot: string): string;
712
- declare function sharedCompilationCache(): string;
713
- declare function sharedCcache(): string;
714
- declare function sharedGradle(): string;
715
- declare function sharedPods(): string;
716
- declare function createdDevicesFile(): string;
717
- declare function gitMergeCacheDir(): string;
718
- declare function pullRequestCacheDir(): string;
719
- declare function agentSessionsCacheFile(): string;
720
- declare function diskUsageCacheDir(): string;
721
- /** The ownership claim root that admits one disk-usage walk at a time on this machine. */
722
- declare function diskUsageWalkClaims(): string;
723
- /** The remote machines this machine asked for build access, each with its pinned stim-server key once approved. */
724
- declare function buildMachinesFile(): string;
725
- declare function buildMachinesLock(): string;
726
- /** The remote machines this machine asked for access, separate from build credentials. */
727
- declare function deviceHostMachinesFile(): string;
728
- declare function deviceHostMachinesLock(): string;
729
- declare function deviceHostMachinesClaims(): string;
730
- declare function createdDevicesLock(): string;
731
- /** The EAS session ledger lives under the real home directory, never under STIM_HOME. */
732
- declare function easMachineStateRoot(): string;
733
- declare function easSessionLedgerFile(root?: string): string;
734
- declare function easSessionLedgerLock(root?: string): string;
735
- /** Where stim-server records, one file per server process, the devices whose frames a client subscribes to. */
736
- declare function deviceViewersDir(): string;
737
- declare function budgetSweepFile(): string;
738
- declare function budgetSweepLock(): string;
739
- declare function statsFile(): string;
740
- declare function maintenanceDir(): string;
741
- declare function maintenanceStateFile(): string;
742
- declare function maintenanceAttemptFile(): string;
743
- declare function maintenanceNdjsonFile(): string;
744
- declare function gcLogDir(): string;
745
- declare function gcLogFile(): string;
746
- declare function buildLedgerDir(): string;
747
- declare function buildLedgerFile(): string;
748
- declare function buildLedgerLock(): string;
749
- declare function maintenanceChildLogFile(): string;
750
- declare function maintenanceRunClaims(): string;
751
- /** Dependency trees `worktree remove` moved out of a worktree before git removed it; gc deletes what is left. */
752
- declare function trashRoot(): string;
753
- declare function archiveRoot(): string;
754
- declare function archiveDir(id: string): string;
755
- declare function archiveLock(): string;
756
- /** Uncommitted changes of removed worktrees: `<repository>/<branch-or-dir>-<timestamp>/` holding `manifest.json`. */
757
- declare function worktreeArchiveRoot(): string;
758
- /** This machine's own long-term X25519 key as a client of other machines' stim-servers. */
759
- declare function clientIdentityKeyFile(): string;
760
- /**
761
- * The local Unix socket. sun_path permits 103 UTF-8 bytes on macOS and 107 on Linux. Longer paths use
762
- * /tmp/stim-sockets-<uid>/<SHA-256 of the normalized absolute socket path>.sock.
763
- */
764
- declare function serverLocalSocket(): string;
765
- declare function serverSetupDir(): string;
766
- declare function setupJournalFile(hash: string): string | null;
767
- //#endregion
768
- //#region state/placement.d.ts
769
- /**
770
- * The placement words every workload shares (`remote.build`, `ios.remote`, `android.remote`, `macos.remote` and their
771
- * flags). Anything else a setting holds is a machine name, or for devices `eas` or `proxy`.
772
- *
773
- * - `local`: this machine only.
774
- * - `auto`: this machine first, a remote machine when this one is full or busy.
775
- * - `remote`: remote machines first, in `remote.machines` order; this machine only when none takes the run.
776
- * - `remote-only`: remote machines in `remote.machines` order, never this machine.
777
- */
778
- declare const PLACEMENT_WORDS: readonly ["local", "auto", "remote", "remote-only"];
779
- type PlacementWord = (typeof PLACEMENT_WORDS)[number];
780
- type AutomaticPlacement = Exclude<PlacementWord, "local">;
781
- declare function placementWord(value: string): PlacementWord | null;
782
- declare function prefersRemote(word: PlacementWord): boolean;
783
- declare function mayRunHere(word: PlacementWord): boolean;
784
- //#endregion
785
- //#region state/recordings.d.ts
786
- /** The footage stim-server keeps per device: the last 15 minutes recorded, however long ago. */
787
- declare const RECORDING_FOOTAGE_MS: number;
788
- /** The recordings of every workspace of one Stim home together; past it the oldest footage goes first. */
789
- declare const RECORDING_CAP_BYTES: number;
790
- /** The platforms stim-server records: owned simulators and emulators, and the Stim-owned Chrome page. */
791
- declare const RECORDING_PLATFORMS: readonly ["ios", "android", "web"];
792
- type RecordingPlatform = (typeof RECORDING_PLATFORMS)[number];
793
- /**
794
- * One file of H.264 footage. A closed segment is named `<start>-<end>.seg`, in epoch milliseconds of its first and
795
- * last access unit; the segment being written is `<start>.part`, and its end is its modification time.
651
+ * A process whose physical footprint, as `top` reports it, is abnormally large. `command` is the one command that
652
+ * safely restarts or stops it, null when Stim knows none; `note` says what that command does.
796
653
  */
797
- interface RecordedSegment {
798
- file: string;
799
- start: number;
800
- end: number;
654
+ interface MemoryCulprit {
655
+ pid: number;
656
+ name: string;
801
657
  bytes: number;
802
- open: boolean;
658
+ command: string | null;
659
+ note: string | null;
660
+ }
661
+ interface MaintenancePressure {
662
+ disk: {
663
+ volume: string;
664
+ freeMb: number;
665
+ }[];
666
+ memory: {
667
+ level: "normal" | "warning" | "critical" | null;
668
+ availableBytes: number | null;
669
+ pressured: boolean;
670
+ /** Read only while the level is warning or critical; absent otherwise. */
671
+ culprits?: MemoryCulprit[];
672
+ };
673
+ warningSince: number | null;
674
+ diskLevel?: DiskPressureLevel;
675
+ }
676
+ interface MaintenancePass {
677
+ startedAt: number;
678
+ durationMs: number;
679
+ trigger: string;
680
+ mode: "report" | "on";
681
+ freedBytes: number;
682
+ actions: number;
683
+ stopped: 0;
684
+ blocked: string[];
685
+ }
686
+ interface MaintenanceRecord extends NdjsonRecord {
687
+ ts: number;
688
+ src: "maintenance";
689
+ level: "debug" | "info" | "warn" | "error";
690
+ msg: string;
691
+ event: "maintenance_pass" | "maintenance_action" | "maintenance_skip" | "maintenance_failure" | "maintenance_check";
692
+ pass: string;
693
+ trigger: string;
694
+ mode: "report" | "on";
695
+ }
696
+ interface MaintenanceState {
697
+ version: 1;
698
+ lastAt: Partial<Record<MaintenanceCheck, number>>;
699
+ deferredAt?: Partial<Record<MaintenanceCheck, number>>;
700
+ pressure: MaintenancePressure | null;
701
+ sizes: MaintenanceSize[];
702
+ lastPass: MaintenancePass | null;
703
+ recent: MaintenanceRecord[];
704
+ plan: MaintenanceAction[];
705
+ skipKeys?: string[];
706
+ }
707
+ interface MaintenanceStatus {
708
+ mode: MaintenanceMode;
709
+ invalid?: string;
710
+ claim?: {
711
+ unresolved: string;
712
+ removeCommand: string;
713
+ };
714
+ lastChecks: {
715
+ pressure: number | null;
716
+ size: number | null;
717
+ worktree: number | null;
718
+ sweep: number | null;
719
+ };
720
+ pressure: MaintenancePressure | null;
721
+ sizes: MaintenanceSize[];
722
+ lastPass: MaintenancePass | null;
723
+ running: {
724
+ startedAt: string;
725
+ trigger: string;
726
+ } | null;
727
+ recent: MaintenanceRecord[];
728
+ plan: MaintenanceAction[];
729
+ }
730
+ /** Rejects missing, corrupt or unsupported payloads without throwing. */
731
+ declare function parseMaintenanceState(value: unknown): MaintenanceState | null;
732
+ declare function readMaintenanceState(): MaintenanceState | null;
733
+ declare function readMaintenanceAttempt(): number | undefined;
734
+ //#endregion
735
+ //#region state/archive.d.ts
736
+ interface ArchivedBytes {
737
+ logs: number;
738
+ recordings: number;
739
+ agentActions: number;
740
+ record: number;
741
+ total: number;
742
+ }
743
+ interface ArchivedWorkspace {
744
+ id: string;
745
+ projectRoot: string;
746
+ project: string;
747
+ workspace: string;
748
+ worktree: {
749
+ repository: string | null;
750
+ branch: string | null;
751
+ head: string | null;
752
+ subject: string | null;
753
+ merged: boolean | null;
754
+ pullRequest: {
755
+ number: number;
756
+ state: string;
757
+ title: string;
758
+ url: string;
759
+ } | null;
760
+ };
761
+ removedAt: string;
762
+ removedBy: string;
763
+ lastUsedAt: string | null;
764
+ builds: {
765
+ count: number;
766
+ last: LastBuildReport | null;
767
+ lastErrorCount: number;
768
+ };
769
+ agents: EndedAgentSession[];
770
+ bytes: ArchivedBytes;
771
+ expires: {
772
+ logs: string | null;
773
+ recordings: string | null;
774
+ agentActions: string | null;
775
+ record: string | null;
776
+ };
777
+ version: number;
778
+ replacedBy?: string;
803
779
  }
804
- interface RecordedDevice {
805
- platform: RecordingPlatform;
806
- slot: string;
807
- dir: string;
808
- segments: RecordedSegment[];
780
+ interface ArchivedUsage {
781
+ count: number;
782
+ bytes: number;
783
+ byKind: {
784
+ logs: number;
785
+ recordings: number;
786
+ agentActions: number;
787
+ record: number;
788
+ };
809
789
  }
810
- /** The directory under a workspace's recordings that holds one device slot's footage. */
811
- declare function recordingDeviceName(platform: RecordingPlatform, slot: string): string;
812
- /** Segment names hold whole milliseconds: the start rounded down and the end rounded up. */
813
- declare function closedSegmentName(start: number, end: number): string;
814
- declare function openSegmentName(start: number): string;
815
- /** The segments in one device directory, oldest first. Files that are not segments are left out; `closedOnly` excludes files still being written. */
816
- declare function listSegments(dir: string, closedOnly?: boolean): RecordedSegment[];
817
- /** Reads current segment files asynchronously, oldest first; cancellation propagates. */
818
- declare function listSegmentsAsync(dir: string, closedOnly?: boolean, signal?: AbortSignal): Promise<RecordedSegment[]>;
819
- /** Every device slot recorded under a workspace's recordings directory. */
820
- declare function listRecordedDevices(recordingsDir: string, closedOnly?: boolean): RecordedDevice[];
821
- /** Reads current device slots and their segment files asynchronously; cancellation propagates. */
822
- declare function listRecordedDevicesAsync(recordingsDir: string, closedOnly?: boolean, signal?: AbortSignal): Promise<RecordedDevice[]>;
823
- declare function recordedBytes(devices: readonly RecordedDevice[]): number;
824
- /**
825
- * The oldest closed segments to delete so that one device keeps at most `footageMs` of footage. Time between
826
- * segments, while nothing was recorded, does not count. The open segment is never chosen.
827
- */
828
- declare function footageDrops(segments: readonly RecordedSegment[], footageMs: number): RecordedSegment[];
829
- /** The oldest closed segments across all devices to delete so that together they hold at most `capBytes`. */
830
- declare function capDrops(devices: readonly RecordedDevice[], capBytes: number): RecordedSegment[];
831
- /**
832
- * Whether stim-server records a workspace's devices: `STIM_RECORDING`, then `recording.enabled` in the workspace,
833
- * repo and machine layers, in that order, and true by default.
834
- */
835
- declare function recordingEnabled(env: NodeJS.ProcessEnv, layers: readonly unknown[]): boolean;
790
+ declare function readArchives(): ArchivedWorkspace[];
791
+ /** Reads current archive files asynchronously, outside the synchronous state-read cache; cancellation propagates. */
792
+ declare function readArchivesAsync(signal?: AbortSignal): Promise<ArchivedWorkspace[]>;
793
+ declare function readArchive(id: string): ArchivedWorkspace | null;
794
+ /** Reads a current archive without the synchronous state-read cache; invalid or missing records return null. */
795
+ declare function readArchiveAsync(id: string, signal?: AbortSignal): Promise<ArchivedWorkspace | null>;
796
+ declare function readArchiveState(id: string): WorkspaceState | null;
797
+ declare function archivedUsage(archives: readonly ArchivedWorkspace[]): ArchivedUsage;
798
+ declare function linkReplacedArchives(archives: readonly ArchivedWorkspace[], paths: readonly string[]): ArchivedWorkspace[];
799
+ declare function archiveEnabled(env: NodeJS.ProcessEnv, layers: readonly unknown[]): boolean;
836
800
  //#endregion
837
- //#region state/run-id.d.ts
838
- declare const RUN_ID_ENV = "STIM_RUN_ID";
839
- /** A run id as the CLI and stim-server accept it: letters, digits, `.`, `_` and `-`, at most 64. Null otherwise. */
840
- declare function validRunId(value: unknown): string | null;
841
- /**
842
- * The run id of the ambient operation context, else of this CLI invocation: `STIM_RUN_ID` when it holds a valid id
843
- * (Stim Desktop passes one), else a random one. The CLI entry exports it back to `STIM_RUN_ID` so processes the run
844
- * starts inherit it; this function does not.
845
- */
846
- declare function runId(env?: NodeJS.ProcessEnv): string;
801
+ //#region state/macos.d.ts
802
+ interface MacosProcess {
803
+ pid: number;
804
+ processToken: string;
805
+ startedAtMicros: number;
806
+ }
807
+ interface MacosBuild {
808
+ state: "running" | "ok" | "failed";
809
+ startedAt: string;
810
+ finishedAt?: string;
811
+ durationMs?: number;
812
+ fingerprint?: string;
813
+ cacheKey?: string;
814
+ cacheHit?: boolean;
815
+ cacheSource?: "local" | "provider" | "peer";
816
+ error?: string;
817
+ errorCode?: string;
818
+ buildMachine?: string;
819
+ builtOn?: string;
820
+ offloadedTo?: string;
821
+ offloadFallback?: string;
822
+ }
823
+ interface MacosAppRecord {
824
+ launchId: string;
825
+ arguments: string[];
826
+ product: string;
827
+ /** The app's visible name: the product and the workspace label. Absent in older records. */
828
+ displayName?: string;
829
+ bundle: string;
830
+ bundleId: string;
831
+ executable: string;
832
+ build: MacosBuild;
833
+ supervisor?: MacosProcess;
834
+ app?: MacosProcess;
835
+ /** Set when `stim macos --remote` reserved a session on another Mac; the app then has no local process. */
836
+ host?: HostedMacosPlacement;
837
+ /** Host launch evidence: true for a live app; status can override it in memory after probing the session. */
838
+ hostLaunched?: boolean | "unverified";
839
+ }
840
+ interface MacosAppState extends MacosAppRecord {
841
+ state: "running" | "orphaned" | "stopped" | "unverified";
842
+ }
843
+ declare function parseMacosRecord(value: unknown): MacosAppRecord | null;
844
+ declare function readMacosRecord(root: string): MacosAppRecord | null;
845
+ declare function readHostedMacosApp(home: string): MacosAppRecord | null;
846
+ declare function macosAppState(record: MacosAppRecord | null): MacosAppState | null;
847
847
  //#endregion
848
848
  //#region state/settings-registry.d.ts
849
849
  declare const ANDROID_COMPILER_CACHE_CHOICES: readonly ["auto", "ccache", "cas", "none"];
@@ -941,1454 +941,1520 @@ declare const ANDROID_EMULATOR_APPS: readonly ["emulator", "stim-desktop"];
941
941
  declare const REPLACED_SETTINGS: Readonly<Record<string, {
942
942
  key: string;
943
943
  env?: string;
944
- value: (old: unknown) => string | null;
945
- }>>;
946
- declare function replacementAdvice(key: string, old: unknown): {
947
- key: string;
948
- command: string | null;
949
- } | null;
950
- declare const WEB_VIEWPORTS: readonly ["desktop", "phone"];
951
- type WebViewport = (typeof WEB_VIEWPORTS)[number];
952
- declare const SETTINGS: readonly SettingDefinition[];
953
- declare function settingDefinition(key: string): SettingDefinition | null;
954
- declare function isLayeredSetting(setting: SettingDefinition): boolean;
955
- declare const SETTING_GROUPS: readonly string[];
956
- declare function expectedShape(type: SettingType): string;
957
- declare function acceptsShape(setting: SettingDefinition, value: unknown): boolean;
958
- declare function settingValueError(setting: SettingDefinition, value: unknown): string | null;
959
- declare function isSensitiveReference(value: unknown): boolean;
960
- /**
961
- * Coerces raw text (a CLI argument or an environment variable) through a setting's
962
- * registry type, the same way for both sources. Types whose values are not written
963
- * as bare text (boolean, number, strings, object) are read as JSON; a value that
964
- * fails to parse is left as the original string so `settingValueError` reports it. A boolean also reads `1` and `0`.
965
- */
966
- declare function coerceSettingText(setting: SettingDefinition, raw: string): unknown;
967
- //#endregion
968
- //#region state/settings-schema.d.ts
969
- declare const SETTINGS_SCHEMA_FILE = "settings.schema.json";
970
- declare const SETTINGS_SCHEMA_URL: string;
971
- type JsonSchema = Record<string, unknown>;
972
- /**
973
- * The published settings schema. The root describes a committed `.stim.json`;
974
- * `$defs.machine` describes the settings keys of `$STIM_HOME/config.json`.
975
- * Every leaf carries `x-stim` with its dotted key, kind, writable scopes,
976
- * environment override, and sensitivity.
977
- */
978
- declare function settingsJsonSchema(): JsonSchema;
979
- //#endregion
980
- //#region state/hosted-app.d.ts
981
- interface HostedAppFile {
982
- path: string;
983
- kind: "file" | "exec" | "link";
984
- size: number;
985
- sha256: string;
986
- }
987
- interface HostedAppOffer {
988
- session: string;
989
- attempt: string;
990
- bundleId: string;
991
- mode: "development" | "release" | "process";
992
- devClientScheme?: string;
993
- arguments?: string[];
994
- manifest: {
995
- sha256: string;
996
- size: number;
997
- };
998
- }
999
- interface HostedAppDelivery extends Omit<HostedAppOffer, "manifest"> {
1000
- state: "receiving" | "installing" | "installed" | "unknown";
1001
- launched: true | "unverified" | null;
1002
- notice?: string;
1003
- }
1004
- interface HostedAppRecord extends HostedAppDelivery {
1005
- manifest: HostedAppOffer["manifest"];
1006
- files: HostedAppFile[];
1007
- }
1008
- declare const HOSTED_APP_CHUNK_BYTES: number;
1009
- declare function hostedAppAttempt(value: unknown): value is string;
1010
- declare function hostedAppArea(session: string, attempt: string, workerHome?: string): string;
1011
- declare function hostedAppBlobs(session: string, workerHome?: string): string;
1012
- /** Transfers cached verification after a rename, only for the same inode, size, mtime and digest. */
1013
- declare function rememberHostedAppBlobRename(from: string, to: string, file: {
1014
- size: number;
1015
- sha256: string;
1016
- }): void;
1017
- /** Checks regular blob bytes, including their size, before content-addressed reuse. */
1018
- declare function validHostedAppBlob(path: string, file: {
1019
- size: number;
1020
- sha256: string;
1021
- }): boolean;
1022
- /** At most 32 plain arguments of 1024 characters (8192 in total), with no NUL, CR or LF. */
1023
- declare function validHostedAppArguments(value: unknown): value is string[];
1024
- declare function parseHostedAppOffer(value: unknown): HostedAppOffer | null;
1025
- /** Relative normalized paths, with no file or link as another entry's ancestor. */
1026
- declare function parseHostedAppManifest(value: unknown): HostedAppFile[] | null;
1027
- /** Reads a server-written receipt; malformed or missing established state remains unresolved. */
1028
- declare function readHostedAppMetadata(session: string, attempt: string, workerHome?: string): Omit<HostedAppRecord, "files">;
1029
- declare function readHostedApp(session: string, attempt: string, workerHome?: string): HostedAppRecord;
1030
- //#endregion
1031
- //#region state/hosted-macos.d.ts
1032
- /** App slots one host offers; a slot names the host-assigned bundle id `<bundleId>.hosted<slot>`. */
1033
- declare const HOSTED_MACOS_APP_SLOTS = 64;
1034
- /** The drivers `hosting.agentDriver` can name: `none` and the first-party driver packages stim-server ships. */
1035
- declare const HOSTED_AGENT_DRIVERS: readonly ["none", "agent-device"];
1036
- type HostedAgentDriverName = (typeof HOSTED_AGENT_DRIVERS)[number];
1037
- /** A driver name a grant or access record can carry; a client resolves only the drivers it knows. */
1038
- declare function hostedAgentDriverName(value: unknown): value is string;
1039
- /** What a host reports in a macOS offer: no simulator is created, so the choice is the host itself. */
1040
- interface HostedMacosChoice {
1041
- architecture: "arm64" | "x86_64";
1042
- macosVersion: string;
1043
- }
1044
- /** A ready hosted macOS session's identity: the host it runs on and the app slot reserved for it. */
1045
- interface HostedMacosDevice extends HostedMacosChoice {
1046
- appSlot: number;
1047
- }
1048
- /**
1049
- * The agent-device lease owner scope a client names on every request. `deviceKey` is `<bundleId>@<pid>`
1050
- * for a macOS app, or `ios:mobile:<udid>` with `backend: ios-instance` for a hosted simulator, or `android:mobile:<serial>` with `backend: android-instance` for a hosted emulator.
1051
- */
1052
- interface HostedAgentLease {
1053
- tenant: string;
1054
- runId: string;
1055
- clientId: string;
1056
- deviceKey: string;
1057
- backend?: "ios-instance" | "android-instance";
1058
- }
1059
- /**
1060
- * Agent control a driver issues for one installed hosted app. `path` is the session's base route on the host,
1061
- * `scope` the driver's handle that limits it to this one app, and `lease`, for a driver that allocates leases,
1062
- * the scope that pins the allocation.
1063
- */
1064
- interface HostedAgentIssuedGrant {
1065
- driver: string;
1066
- path: string;
1067
- token: string;
1068
- scope: string;
1069
- lease?: HostedAgentLease;
1070
- }
944
+ value: (old: unknown) => string | null;
945
+ }>>;
946
+ declare function replacementAdvice(key: string, old: unknown): {
947
+ key: string;
948
+ command: string | null;
949
+ } | null;
950
+ declare const WEB_VIEWPORTS: readonly ["desktop", "phone"];
951
+ type WebViewport = (typeof WEB_VIEWPORTS)[number];
952
+ declare const SETTINGS: readonly SettingDefinition[];
953
+ declare function settingDefinition(key: string): SettingDefinition | null;
954
+ declare function isLayeredSetting(setting: SettingDefinition): boolean;
955
+ declare const SETTING_GROUPS: readonly string[];
956
+ declare function expectedShape(type: SettingType): string;
957
+ declare function acceptsShape(setting: SettingDefinition, value: unknown): boolean;
958
+ declare function settingValueError(setting: SettingDefinition, value: unknown): string | null;
959
+ declare function isSensitiveReference(value: unknown): boolean;
1071
960
  /**
1072
- * Agent control the host grants a client for one installed hosted app, sent only over that client's approved
1073
- * device-host connection and never journaled. `none` means the host's `hosting.agentDriver` starts no driver.
961
+ * Coerces raw text (a CLI argument or an environment variable) through a setting's
962
+ * registry type, the same way for both sources. Types whose values are not written
963
+ * as bare text (boolean, number, strings, object) are read as JSON; a value that
964
+ * fails to parse is left as the original string so `settingValueError` reports it. A boolean also reads `1` and `0`.
1074
965
  */
1075
- type HostedAgentGrant = {
1076
- driver: "none";
1077
- } | HostedAgentIssuedGrant;
1078
- /** `app.launch` and `app.attach` results; a host includes `agent` once the app is installed. */
1079
- type HostedAppLaunch = HostedAppDelivery & {
1080
- agent?: HostedAgentGrant;
1081
- };
966
+ declare function coerceSettingText(setting: SettingDefinition, raw: string): unknown;
967
+ //#endregion
968
+ //#region state/status.d.ts
969
+ type StatsPlatform = "ios" | "android";
970
+ /** A platform whose build reports phases: the stats platforms and `macos`. */
971
+ type BuildPlatform = StatsPlatform | "macos";
972
+ type RunOutcomeKind = "hit" | "cold";
973
+ declare const BUILD_PHASES: readonly ["prepare", "cache-lookup", "wait", "prebuild", "pods", "compile", "device", "install", "launch"];
974
+ type BuildPhase = (typeof BUILD_PHASES)[number];
975
+ type ActiveBuildState = "running" | "stale" | "unknown";
1082
976
  /**
1083
- * Agent control as the client hands it to a coding agent: secrets stay in files Stim writes with mode
1084
- * 0600, and `command` shows how to address the hosted app. Never carries a token inline. `setting` names
1085
- * the hosting Mac's setting that turns a driver on.
977
+ * Where a running build compiles: `local`, or the remote machine it was offloaded to, with the phase it is in there
978
+ * (`sync`, `deps`, `prebuild`, `pods`, `build` or `fetch`) and when the offload and that phase started.
1086
979
  */
1087
- type HostedAgentAccess = {
1088
- driver: "none";
1089
- setting: "hosting.agentDriver";
1090
- } | {
1091
- driver: string;
1092
- remoteConfig: string;
1093
- command: string;
980
+ type BuildPlacement = "local" | {
981
+ host: string;
982
+ phase: string;
983
+ startedAt: string;
984
+ phaseStartedAt: string;
1094
985
  };
1095
- /** Where a workspace's macOS app runs when `stim macos --remote` placed it on another Mac. */
1096
- interface HostedMacosPlacement {
1097
- machine: string;
1098
- session: string;
1099
- appSlot: number;
1100
- appAttempt: string;
1101
- /** The host-assigned id the running app uses, `hostedMacosBundleId(<offered id>, appSlot)`. */
1102
- bundleId: string;
1103
- agent: HostedAgentAccess;
1104
- }
1105
- declare function hostedMacosAppSlot(value: unknown): value is number;
1106
- declare function hostedMacosBundleId(bundleId: string, appSlot: number): string;
1107
- declare function parseHostedMacosChoice(value: unknown): HostedMacosChoice | null;
1108
- declare function parseHostedMacosDevice(value: unknown): HostedMacosDevice | null;
1109
- /** A grant naming a driver this client does not know is not usable; it parses as null. */
1110
- declare function parseHostedAgentGrant(value: unknown): HostedAgentGrant | null;
1111
- declare function parseHostedAgentAccess(value: unknown): HostedAgentAccess | null;
1112
- declare function parseHostedMacosPlacement(value: unknown): HostedMacosPlacement | null;
1113
- //#endregion
1114
- //#region state/device-host.d.ts
1115
- type HostedDevicePlatform = "ios" | "android" | "macos";
1116
- /** Deadlines stim-server gives each hosted worker run. Clients derive their waits from these. */
1117
- interface DeviceHostLimits {
1118
- prepareMs: number;
1119
- offerMs: number;
1120
- stopMs: number;
1121
- logsMs: number;
1122
- killGraceMs: number;
1123
- }
1124
- declare const DEVICE_HOST_LIMITS: Readonly<DeviceHostLimits>;
1125
- /** How many parked sessions one reserve inspects, and possibly retires, before it creates a device. */
1126
- declare const HOSTED_PARKED_CANDIDATES: number;
1127
- /** How long a cancelled hosted worker run can take to settle: SIGTERM, SIGKILL after the grace, then it is abandoned. */
1128
- declare function hostedWorkerSettleMs(limits?: Pick<DeviceHostLimits, "killGraceMs">): number;
1129
- interface HostedIosChoice {
1130
- deviceTypeId: string;
1131
- runtimeId: string;
1132
- deviceType: string;
1133
- runtime: string;
1134
- architecture: "arm64" | "x86_64";
986
+ interface BuildWaitingFor {
987
+ kind: "build-slot" | "device-slot";
988
+ inUse: number;
989
+ max: number;
990
+ since: string;
1135
991
  }
1136
- interface HostedIosDevice extends HostedIosChoice {
1137
- udid: string;
1138
- name: string;
992
+ interface BuildReport {
993
+ platform: BuildPlatform;
994
+ slot: string;
995
+ state: ActiveBuildState;
996
+ phase: BuildPhase;
997
+ startedAt: string;
998
+ phaseStartedAt: string;
999
+ outcome: RunOutcomeKind | null;
1000
+ /** Whether `outcome` is this run's own; before its cache lookup resolves it is the project's latest outcome. */
1001
+ outcomeKnown: boolean;
1002
+ /** The resolved lookup result; absent before resolution and when the run skips cache lookup. */
1003
+ cacheLookupOutcome?: "hit" | "miss";
1004
+ expectedMs: number | null;
1005
+ expectedPhaseMs: number | null;
1006
+ /** Milliseconds spent in phases already left; sums repeat visits, excludes the current visit, and is omitted before any phase completes and by older Stim. */
1007
+ completedPhaseMs?: Partial<Record<BuildPhase, number>>;
1008
+ basis: number;
1009
+ /**
1010
+ * The phases a run like this one goes through, in order, with each one's median in milliseconds, from the same
1011
+ * runs as `expectedMs`; null without comparable runs. The current phase can be one the plan does not list.
1012
+ */
1013
+ plannedPhases: PlannedPhase[] | null;
1014
+ /** Why the run's cache lookup missed, once the run knows; `baseline` omits `cacheKey`. */
1015
+ missReason?: BuildMissReason;
1016
+ /**
1017
+ * True while `missReason` is the miss of the key the run looked up first and the run will prebuild or install pods,
1018
+ * then look the key up again. A hit on the second lookup removes `missReason` and sets `outcome` to `hit`.
1019
+ */
1020
+ missProvisional?: true;
1021
+ /** What the native build tool is doing now, once it printed a line Stim recognizes. */
1022
+ detail?: BuildDetail;
1023
+ /** What the `device` or `launch` phase is waiting on, once Stim observed it; absent otherwise and from older Stim. */
1024
+ activity?: BuildActivity;
1025
+ placement: BuildPlacement;
1026
+ /** While `phase` is `wait`: the workspace whose build of the same artifact this run waits on, when it is known. */
1027
+ waitingOn?: {
1028
+ path: string;
1029
+ };
1030
+ waitingFor?: BuildWaitingFor;
1139
1031
  }
1140
- interface HostedAndroidChoice {
1141
- systemImage: string;
1142
- deviceProfile: string;
1143
- architecture: "arm64-v8a" | "x86_64";
1032
+ /**
1033
+ * What a run's `device` or `launch` phase is waiting on. `booting` lasts while the run waits for the owned simulator or
1034
+ * emulator it started booting. `bundling` starts when the app's bundle request reaches this workspace's
1035
+ * Metro and ends when Metro delivered it; `percent` is Metro's own progress for that request, present only once Metro
1036
+ * reported one. `waiting-ready` lasts while the app reports its readiness pending. `startedAt` is when the evidence was
1037
+ * recorded.
1038
+ */
1039
+ interface BuildActivity {
1040
+ name: "booting" | "bundling" | "waiting-ready";
1041
+ startedAt: string;
1042
+ percent?: number;
1144
1043
  }
1145
- interface HostedAndroidDevice extends HostedAndroidChoice {
1146
- avdName: string;
1147
- serial: string;
1148
- consolePort: number;
1044
+ /** One phase a running build is expected to go through, and its median duration in comparable runs. */
1045
+ interface PlannedPhase {
1046
+ phase: BuildPhase;
1047
+ expectedMs: number;
1149
1048
  }
1150
- type HostedDevice = HostedIosDevice | HostedAndroidDevice | HostedMacosDevice;
1151
- interface HostedDeviceSelectors {
1152
- deviceType?: string;
1153
- runtime?: string;
1154
- systemImage?: string;
1155
- deviceProfile?: string;
1049
+ /** The native build tool's step inside a build's `compile` phase. */
1050
+ declare const NATIVE_BUILD_STEPS: readonly ["configure", "compile", "link", "resources", "script", "dex", "package", "sign"];
1051
+ type NativeBuildStep = (typeof NATIVE_BUILD_STEPS)[number];
1052
+ /**
1053
+ * A running build's progress as its tool reports it. `unit` is `targets` for xcodebuild, `tasks` for Gradle and
1054
+ * `steps` for SwiftPM. xcodebuild's `done` counts targets it finished and `total` the targets in its dependency graph,
1055
+ * which includes targets with nothing to do; Gradle's `done` counts the tasks it reported, with a null `total`;
1056
+ * SwiftPM's `done` and `total` are the `[done/total]` of its latest progress line, and its total can grow while it
1057
+ * builds. They are counts, not a completion fraction. `status` reports the detail only during the `compile` phase. `line` is
1058
+ * the latest compile, link or task line with paths shortened to file names.
1059
+ */
1060
+ interface BuildDetail {
1061
+ step: NativeBuildStep | null;
1062
+ unit: "targets" | "tasks" | "steps" | null;
1063
+ done: number | null;
1064
+ total: number | null;
1065
+ line: string | null;
1066
+ updatedAt: string;
1156
1067
  }
1157
- interface HostedDeviceOfferRequest extends HostedDeviceSelectors {
1158
- platform: HostedDevicePlatform;
1068
+ /** Where a build's app came from: a cache tier, or `false` when it compiled or failed before one was found. */
1069
+ type BuildCacheHit = "local" | "remote" | false;
1070
+ /** A platform's most recent `ios` or `android` run in one workspace. */
1071
+ interface DevicePlacement {
1072
+ decision: "local" | "hosted" | "waited-locally" | "eas";
1073
+ reason: string;
1074
+ machine?: string;
1159
1075
  }
1160
- type HostedNativeOffer = {
1161
- resources: {
1162
- cpus: number;
1163
- /** Five-minute system load divided by available CPU count. */
1164
- loadPerCore: number;
1165
- maxLoadPerCore?: number;
1166
- memoryFreeBytes: number;
1167
- memoryPressure: "normal" | "warning" | "critical" | null;
1168
- /** Available bytes on the Stim home volume, not necessarily the Android AVD volume. */
1169
- workerDiskFreeBytes: number | null;
1170
- localDevices?: number | {
1171
- unknown: string;
1172
- };
1173
- };
1174
- declined: string | null;
1175
- } & ({
1176
- platform: "ios";
1177
- choice: HostedIosChoice | null;
1178
- } | {
1179
- platform: "android";
1180
- choice: HostedAndroidChoice | null;
1181
- } | {
1182
- platform: "macos";
1183
- choice: HostedMacosChoice | null;
1184
- });
1185
- type HostedDeviceOffer = HostedNativeOffer & {
1186
- capacity: {
1187
- running: number;
1188
- max: number;
1189
- available: number | null;
1190
- local?: number | {
1191
- unknown: string;
1192
- };
1193
- };
1194
- };
1195
- interface HostedDeviceRequest extends HostedDeviceOfferRequest {
1196
- workspace: string;
1076
+ interface LastBuildReport {
1077
+ devicePlacement?: DevicePlacement;
1078
+ platform: BuildPlatform;
1079
+ status: "ok" | "failed";
1080
+ cacheHit: BuildCacheHit;
1081
+ cacheSkipped: boolean;
1082
+ durationMs: number | null;
1083
+ fingerprint: string | null;
1084
+ startedAt: string;
1085
+ finishedAt: string | null;
1086
+ errorCode?: string;
1087
+ missReason?: BuildMissReason;
1088
+ /** The selected auto, local, or configured machine entry; present on new build records, including cache hits. */
1089
+ buildMachine?: string;
1090
+ /** Where compilation ran: here or a machine name; absent when no build ran. */
1091
+ builtOn?: string;
1092
+ /** The remote machine that compiled the app when the build was offloaded. */
1093
+ offloadedTo?: string;
1094
+ /** Why the run built here after it considered offloading; absent when it offloaded or never considered it. */
1095
+ offloadFallback?: string;
1096
+ /** The first compiler diagnostics of a failed build, when the build tool reported any. */
1097
+ diagnostics?: BuildDiagnostic[];
1098
+ /** Present on a failed run: what failed, from `buildCause`. */
1099
+ cause?: BuildCause;
1100
+ }
1101
+ /**
1102
+ * A failed run's cause: the first diagnostic with a file and a line, keyed `file:line`, else the run's error code
1103
+ * (or `failed`), with null `file` and `line`. Consecutive failures with the same `key` failed the same way.
1104
+ */
1105
+ interface BuildCause {
1106
+ key: string;
1107
+ file: string | null;
1108
+ line: number | null;
1109
+ }
1110
+ /** How a recorded build ended. `interrupted` is a run whose process ended without recording a result. */
1111
+ declare const BUILD_RESULTS: readonly ["succeeded", "failed", "cancelled", "interrupted"];
1112
+ type BuildResult = (typeof BUILD_RESULTS)[number];
1113
+ /**
1114
+ * One run in a workspace's recent build history. `configuration` is the iOS configuration or the Android
1115
+ * variant the run built, `Debug` or `debug` by default, and null when the run ended before resolving it.
1116
+ * `phases` holds the milliseconds spent in each phase the run entered. An interrupted run has null
1117
+ * `durationMs` and `finishedAt`, no cache facts, and 0 for the phase it stopped in.
1118
+ */
1119
+ interface BuildHistoryEntry extends LastBuildReport {
1120
+ result: BuildResult;
1197
1121
  slot: string;
1198
- attempt: string;
1122
+ configuration: string | null;
1123
+ cacheKey: string | null;
1124
+ phases: Partial<Record<BuildPhase, number>>;
1125
+ /** A `stim macos` run's SwiftPM step total; absent for other runs and when the build reported none. */
1126
+ compileSteps?: number;
1199
1127
  }
1200
- type HostedDevicePhase = "preparing" | "ready" | "stopping" | "stopped" | "unknown";
1201
- interface HostedDeviceSession extends HostedDeviceRequest {
1202
- id: string;
1203
- client: string;
1204
- state: HostedDevicePhase;
1205
- device: HostedDevice | null;
1206
- consolePort?: number;
1207
- appSlot?: number;
1208
- createdAt: string;
1209
- parked?: {
1210
- at: string;
1128
+ /** One compiler error from a failed build: where it is, when the tool said, and its message. */
1129
+ interface BuildDiagnostic {
1130
+ file: string | null;
1131
+ line: number | null;
1132
+ column: number | null;
1133
+ message: string;
1134
+ }
1135
+ /** What kind of native input a changed fingerprint source is. */
1136
+ type BuildMissCategory = "native-dependency" | "config-plugin" | "app-config" | "app-asset" | "package" | "native-dir" | "autolinking" | "package-scripts" | "file" | "other";
1137
+ interface BuildMissChange {
1138
+ source: string;
1139
+ change: "added" | "removed" | "changed";
1140
+ category: BuildMissCategory;
1141
+ }
1142
+ /**
1143
+ * Why a run compiled instead of installing a cached app. `changes` holds at most 20 entries,
1144
+ * ordered by importance; `changeCount` is the full number of changed fingerprint sources.
1145
+ * Only a plan reports `prebuild-pending`: the run would prebuild before compiling, so the changes
1146
+ * compare the fingerprint before that prebuild.
1147
+ */
1148
+ interface BuildMissReason {
1149
+ kind: "changed" | "no-baseline" | "same-sources" | "cache-skipped" | "fingerprint-error" | "prebuild-pending";
1150
+ summary: string;
1151
+ changes: BuildMissChange[];
1152
+ changeCount: number;
1153
+ /** `cacheKey` is kept in workspace state so a later miss can find this baseline; status omits it. */
1154
+ baseline: {
1155
+ fingerprint: string;
1156
+ cacheKey?: string;
1157
+ from: "workspace" | "project";
1158
+ } | null;
1159
+ rekeyedBy: string[];
1160
+ }
1161
+ /** The payload `stim ios --plan --json` and `stim android --plan --json` print. */
1162
+ interface BuildPlanPayload {
1163
+ platform: StatsPlatform;
1164
+ slot?: string;
1165
+ fingerprint: string;
1166
+ cacheKey: string | null;
1167
+ cacheHit: BuildCacheHit;
1168
+ provider: string | null;
1169
+ cacheSkipped: boolean;
1170
+ prebuild: "none" | "generate" | "regenerate" | "refuse" | null;
1171
+ outcome: RunOutcomeKind | null;
1172
+ expectedMs: number | null;
1173
+ basis: number;
1174
+ /** On a predicted miss with cache reads on, why the cache has no app; `baseline` omits `cacheKey`. */
1175
+ missReason?: BuildMissReason;
1176
+ refusal?: {
1177
+ code: string;
1178
+ message: string;
1179
+ remedy: string;
1211
1180
  };
1212
- notice?: string;
1213
- appAttempt?: string;
1214
- agent?: HostedAgentGrant;
1215
- metroPort?: number;
1216
- clientMetroPort?: number;
1181
+ /** With ios.remote or android.remote set to auto or a Mac, where the plan assumes the device runs, such as `this machine; auto may use janics-mac-mini`. */
1182
+ placement?: string;
1217
1183
  }
1218
- declare function deviceHostRoot(): string;
1219
- declare function deviceHostArea(id: string): string;
1220
- declare function parseHostedChoice(value: unknown): HostedIosChoice | null;
1221
- declare function parseHostedDevice(value: unknown): HostedIosDevice | null;
1222
- declare function parseHostedAndroidDevice(value: unknown): HostedAndroidDevice | null;
1223
- /** Android Emulator -help-port defines the supported even console ports as 5554 through 5584. */
1224
- declare function hostedConsolePort(value: unknown): value is number;
1225
- declare function parseHostedPlatformDevice(value: unknown, platform: HostedDevicePlatform): HostedDevice | null;
1226
- declare function hostedDeviceId(device: HostedDevice): string;
1227
- declare function parseHostedRequest(value: unknown): HostedDeviceRequest | null;
1228
- declare function parseHostedOfferRequest(value: unknown): HostedDeviceOfferRequest | null;
1229
- declare function parseHostedLocalDeviceCount(value: unknown): number | {
1230
- unknown: string;
1231
- } | null;
1232
- declare function parseHostedNativeOffer(value: unknown): HostedNativeOffer | null;
1233
- /** A missing established journal or malformed record is a refusal, never an empty reservation set. */
1234
- declare function readHostedSessions(): HostedDeviceSession[];
1235
- /** The private worker's exact device record; an absent record is unresolved after creation started. */
1236
- declare function readHostedDevice(home: string): HostedIosDevice;
1237
- declare function readHostedDevice(home: string, platform: "android"): HostedAndroidDevice;
1238
- declare function readHostedDevice(home: string, platform: "macos"): HostedMacosDevice;
1239
- declare function readHostedDevice(home: string, platform: HostedDevicePlatform): HostedDevice;
1240
- declare function readHostedDeviceLedger(home: string, options?: {
1241
- required?: boolean;
1242
- }): {
1243
- ios: string[];
1244
- android: string[];
1245
- web: string[];
1246
- } | null;
1247
- /** Hosted sessions use isolated homes: only one exact device of their platform may appear in this ledger. */
1248
- declare function assertHostedDeviceLedger(home: string, id: string, platform?: HostedDevicePlatform, options?: {
1249
- allowEmpty?: boolean;
1250
- }): "listed" | "empty";
1251
- //#endregion
1252
- //#region state/workspace-state.d.ts
1253
- interface WorkspaceState {
1254
- supervisor?: Record<string, unknown>;
1255
- collectors?: Record<string, unknown>;
1256
- lastBuild?: Record<string, unknown>;
1257
- launches?: Record<string, unknown>;
1258
- remoteDevice?: Record<string, unknown>;
1259
- metroTunnel?: Record<string, unknown>;
1260
- [key: string]: unknown;
1184
+ /** The read-only `stim macos --plan --json` payload; SwiftPM work and worker availability remain unknown. */
1185
+ interface MacosBuildPlanPayload {
1186
+ platform: "macos";
1187
+ product: string;
1188
+ buildMachine: string;
1189
+ fingerprint: string | null;
1190
+ cacheKey: string | null;
1191
+ cacheHit: boolean;
1192
+ provider: null;
1193
+ cacheSkipped: boolean;
1194
+ prebuild: null;
1195
+ outcome: null;
1196
+ expectedMs: null;
1197
+ basis: number;
1261
1198
  }
1262
- declare const IDLE_STOP_KEY = "devServerStop";
1263
- /** Why the supervisor last stopped the dev server on its own: `metro.idleStopMinutes` with no use. */
1264
- interface IdleStopRecord {
1265
- reason: "idle";
1266
- at: string;
1267
- idleMinutes: number;
1199
+ type ActivityState = "driven" | "active" | "idle" | "unknown";
1200
+ interface ActivityDriver {
1201
+ tool: string;
1202
+ pid: number | null;
1203
+ since: string | null;
1268
1204
  }
1269
- declare function readIdleStop(state: WorkspaceState | null | undefined): IdleStopRecord | null;
1270
- declare const DEVICE_RECLAIM_REASON = "reclaimed for a waiting run";
1271
- declare const DEVICE_IDLE_SHUTDOWN_KEY = "deviceIdleShutdowns";
1272
- /** An owned simulator or emulator shut down after being idle. */
1273
- interface DeviceIdleShutdownRecord {
1274
- at: string;
1275
- idleMinutes: number;
1276
- reason?: string;
1205
+ /** The kinds of evidence that date a device's last activity, in the order `recent` lists them. */
1206
+ declare const ACTIVITY_RECENCY_BASES: readonly ["agent-action", "device-log", "metro-bundle", "workspace-use", "supervisor-start", "page-log", "viewer"];
1207
+ type ActivityRecencyBasis = (typeof ACTIVITY_RECENCY_BASES)[number];
1208
+ interface DeviceActivity {
1209
+ state: ActivityState;
1210
+ driver?: ActivityDriver;
1211
+ lastActivityAt?: string;
1212
+ /** The newest time of each kind of evidence behind `lastActivityAt`, so a reader can leave out app log chatter. */
1213
+ recent?: Partial<Record<ActivityRecencyBasis, string>>;
1214
+ basis: string[];
1215
+ /** For a driven device, the probes that could not be read, which `basis` leaves out. */
1216
+ unknown?: string[];
1277
1217
  }
1278
- /** The recorded idle shutdowns by device slot key (`ios`, `android`, `ios:<slot>`). */
1279
- declare function readDeviceIdleShutdowns(state: WorkspaceState | null | undefined): Record<string, DeviceIdleShutdownRecord>;
1280
- declare const DEV_SERVER_STOP_REQUEST_KEY = "devServerStopRequest";
1281
- /** A Stim component's intent to stop the supervisor with `processToken`, written before it signals. */
1282
- interface DevServerStopRequest {
1283
- processToken: string;
1284
- by: string;
1285
- pid: number;
1286
- at: string;
1287
- workspace?: string;
1218
+ /** Every state a device's app process can report. */
1219
+ declare const APP_PROCESS_STATES: readonly ["running", "stopped", "unknown"];
1220
+ /**
1221
+ * Whether the workspace's app process is alive on a device now. This is current process state, not launch evidence:
1222
+ * `launched` in an `ios` or `android` result stays the record of one launch. `id` is the bundle identifier or
1223
+ * package that was checked; `state` is "unknown" when the process listing could not be read.
1224
+ */
1225
+ interface DeviceAppProcess {
1226
+ id: string;
1227
+ state: (typeof APP_PROCESS_STATES)[number];
1288
1228
  }
1289
- declare function readDevServerStopRequest(state: WorkspaceState | null | undefined): DevServerStopRequest | null;
1290
1229
  /**
1291
- * Why the dev server last stopped, stored under `IDLE_STOP_KEY`. `requested` names the Stim component that
1292
- * asked; `signal` is a SIGTERM or SIGINT no Stim component asked for; `server-exited` is the dev server
1293
- * process exiting on its own.
1230
+ * A linked worktree's `git status`. `changed` counts tracked paths with staged or unstaged changes, including
1231
+ * conflicts; `untracked` counts untracked entries as `git status` lists them, so an untracked directory counts once.
1232
+ * `ahead` and `behind` compare HEAD with `upstream`, and are null when there is no upstream or it no longer exists.
1233
+ * `mergedInto` names the default branch (`origin/main`) when `gc` would consider the branch merged into it, judged
1234
+ * from local refs without fetching.
1294
1235
  */
1295
- type DevServerStopRecord = IdleStopRecord | {
1296
- reason: "requested";
1297
- at: string;
1298
- by: string;
1299
- byPid: number;
1300
- byWorkspace?: string;
1301
- } | {
1302
- reason: "signal";
1303
- at: string;
1304
- signal: string;
1305
- } | {
1306
- reason: "server-exited";
1307
- at: string;
1308
- mode: string;
1309
- code: number | null;
1310
- signal: string | null;
1311
- };
1312
- /** A stopped dev server's last cause in status; `vanished` is a supervisor proven gone that recorded none. */
1313
- type MetroLastStop = DevServerStopRecord | {
1314
- reason: "vanished";
1315
- pid: number;
1316
- startedAt: string | null;
1317
- };
1318
- declare function readDevServerStop(state: WorkspaceState | null | undefined): DevServerStopRecord | null;
1319
- /** Each platform's latest run; `lastBuild` holds whichever platform ran last. */
1320
- declare const LAST_BUILD_KEYS: Readonly<Record<StatsPlatform, string>>;
1321
- declare const BUILD_HISTORY_KEY = "buildHistory";
1322
- declare const BUILD_HISTORY_LIMIT = 10;
1323
- /** A recorded miss reason in the shape status reports, without `baseline.cacheKey`; null when malformed. */
1324
- declare function parseMissReason(value: unknown): BuildMissReason | null;
1236
+ interface WorktreeGit {
1237
+ changed: number;
1238
+ untracked: number;
1239
+ upstream: string | null;
1240
+ ahead: number | null;
1241
+ behind: number | null;
1242
+ mergedInto: string | null;
1243
+ }
1325
1244
  /**
1326
- * The first diagnostics of a failed build in the shape a last-build record stores and `status` reports, the
1327
- * ones with a source position first.
1245
+ * The GitHub pull request of a worktree's branch and HEAD, as `status --watch` last looked it up through `gh`.
1246
+ * `state` is `draft` for an open draft. `checks` counts the head commit's check runs and commit statuses, null when it
1247
+ * has none. `checkedAt` is when Stim last asked GitHub.
1328
1248
  */
1329
- declare function buildDiagnostics(value: unknown): BuildDiagnostic[];
1330
- declare function readDevicePlacement(state: WorkspaceState | null, platform: StatsPlatform, slot?: string): DevicePlacement | null;
1331
- declare function readLastBuilds(state: WorkspaceState | null | undefined): Partial<Record<StatsPlatform, LastBuildReport>>;
1332
- /** Each platform's recorded runs, newest first, at most `BUILD_HISTORY_LIMIT` each. */
1333
- declare function readBuildHistory(state: WorkspaceState | null | undefined): Partial<Record<BuildPlatform, BuildHistoryEntry[]>>;
1334
- declare function readWorkspaceState(root: string): WorkspaceState | null;
1335
- declare function lastUseFrom(state: WorkspaceState | null, logMtimes: readonly number[]): number;
1249
+ interface WorktreePullRequest {
1250
+ number: number;
1251
+ url: string;
1252
+ title: string;
1253
+ state: "open" | "draft" | "merged" | "closed";
1254
+ checks: {
1255
+ passing: number;
1256
+ failing: number;
1257
+ pending: number;
1258
+ } | null;
1259
+ reviewDecision: "approved" | "changes-requested" | "review-required" | null;
1260
+ checkedAt: string;
1261
+ }
1262
+ interface WorktreeFacts {
1263
+ path: string;
1264
+ branch?: string;
1265
+ repository?: string;
1266
+ /** Null when git could not be read in time, the worktree is in a folder status does not open, or one-shot status skipped an idle environment or an unprovisioned worktree. */
1267
+ git?: WorktreeGit | null;
1268
+ /** Null when GitHub has no pull request for the branch; absent when unknown, such as without `gh`. */
1269
+ pullRequest?: WorktreePullRequest | null;
1270
+ /** Present with `git`: what the worktree's git chip shows, from `gitChip`. */
1271
+ gitChip?: GitChip;
1272
+ }
1273
+ /** Every coding-agent tool whose sessions status can attribute to an environment. */
1274
+ declare const AGENT_TOOLS: readonly ["claude-code", "codex"];
1275
+ type AgentTool = (typeof AGENT_TOOLS)[number];
1336
1276
  /**
1337
- * When an ios, android or macos run last finished in the workspace, from its recorded builds (a cache hit counts),
1338
- * in epoch milliseconds; NaN when it records none. Dev server starts, reloads and logs do not count.
1277
+ * A coding-agent session working in an environment: one whose working directory `cwd` is the environment's path,
1278
+ * inside it, or its git worktree root, or one that ran a Stim command in it. `title` is the short name the tool keeps
1279
+ * for the session, never its conversation. `lastActiveAt` is the tool's own last update, or the Stim command's time
1280
+ * when that is newer. `openUrl` opens the session in the tool's desktop app when that app is installed on this machine.
1281
+ * `webUrl` opens a Claude Code session with Remote Control connected on claude.ai/code or in the Claude mobile app.
1339
1282
  */
1340
- declare function lastNativeUseFrom(state: WorkspaceState | null): number;
1341
- declare const MAINTENANCE_LOG_NAME = "maintenance.ndjson";
1283
+ interface AgentSession {
1284
+ tool: AgentTool;
1285
+ sessionId: string;
1286
+ title?: string;
1287
+ cwd: string;
1288
+ startedAt?: string;
1289
+ lastActiveAt?: string;
1290
+ pid?: number;
1291
+ openUrl?: string;
1292
+ webUrl?: string;
1293
+ }
1342
1294
  /**
1343
- * When the workspace was last used, in epoch milliseconds. A workspace that recorded no use falls back to the mtime
1344
- * of its workspace.json, which is written once when the workspace is created, so it is a lower bound on the last
1345
- * use. NaN when its state.json exists but does not parse, or when workspace.json cannot be read either.
1295
+ * An agent session that stopped running in an environment, with the details Stim last found while it ran. `endedAt`
1296
+ * is the last time a status watcher found it running.
1346
1297
  */
1347
- declare function workspaceLastUsed(root: string): number;
1348
- declare const WARM_KEY = "warm";
1349
- /** How long a successful warm keeps a workspace `ready` when nothing runs in it. */
1350
- declare const READY_PHASE_MS: number;
1298
+ interface EndedAgentSession extends Omit<AgentSession, "pid"> {
1299
+ endedAt: string;
1300
+ }
1301
+ interface AndroidRuntimeFacts {
1302
+ serial: string | null;
1303
+ state: "detected" | "not-detected" | "missing" | "unknown";
1304
+ error?: string;
1305
+ }
1306
+ /** Every connection state a physical device can report. */
1307
+ declare const PHYSICAL_DEVICE_CONNECTIONS: readonly ["connected", "disconnected", "unknown"];
1308
+ type PhysicalDeviceConnection = (typeof PHYSICAL_DEVICE_CONNECTIONS)[number];
1351
1309
  /**
1352
- * What `stim worktree warm` last recorded in a workspace: a warm in progress, held by the ownership claim
1353
- * `claim` names, or a successful warm that finished at `at`.
1310
+ * A physical iPhone, iPad or Android phone the workspace holds an unexpired lease on, from `ios --device`,
1311
+ * `android --device` or `device lock`. Stim uses it and never owns it, so `owned` is always false. `id` is the
1312
+ * UDID or adb serial. `name` is the device's own name, falling back to the name the lease recorded; `model` is the
1313
+ * marketing name devicectl reports, null when it could not be read, or the Android model the lease recorded.
1314
+ * `connection` is `connected` when devicectl can reach the phone or adb lists it as `device`, `disconnected` when
1315
+ * the tool answered without it, and `unknown` when the tool could not be read in time. `lease.holder` is the
1316
+ * workspace path that holds it.
1354
1317
  */
1355
- type WarmRecord = {
1356
- phase: "warming";
1357
- step: WarmStep;
1358
- startedAt: string;
1359
- claim: {
1360
- root: string;
1361
- claimId: string;
1318
+ interface PhysicalDeviceState {
1319
+ platform: StatsPlatform;
1320
+ slot: string;
1321
+ id: string;
1322
+ name: string | null;
1323
+ model: string | null;
1324
+ owned: false;
1325
+ physical: true;
1326
+ connection: PhysicalDeviceConnection;
1327
+ lease: {
1328
+ holder: string;
1329
+ kind: "declared" | "run";
1330
+ grantedAt: string | null;
1331
+ expiresAt: string;
1362
1332
  };
1363
- } | {
1364
- phase: "ready";
1365
- at: string;
1366
- };
1367
- declare function readWarmRecord(state: WorkspaceState | null | undefined): WarmRecord | null;
1368
- declare const WORKSPACE_AGENT_KEY = "agentSession";
1333
+ }
1334
+ interface RemoteDeviceState {
1335
+ platform: "ios" | "android" | null;
1336
+ backend: "eas";
1337
+ sessionId: string;
1338
+ state: "claimed" | "unclaimed" | "unknown";
1339
+ startedAt: string | null;
1340
+ webPreviewUrl: string | null;
1341
+ }
1369
1342
  /**
1370
- * The coding-agent session whose shell ran the Stim command that last recorded `lastUsedAt`, with that command's
1371
- * working directory as `cwd` and its time as `lastActiveAt`.
1343
+ * One part of a worktree's git chip, in display order: commits ahead of and behind the upstream, uncommitted files
1344
+ * (changed plus untracked), the base branch the branch is merged into (left out when its pull request is merged,
1345
+ * whose own mark says so), and a branch with no upstream that is not merged.
1372
1346
  */
1373
- declare function readWorkspaceAgent(state: WorkspaceState | null | undefined): AgentSession | null;
1374
- //#endregion
1375
- //#region state/hosted-native.d.ts
1376
- interface HostedNativePlacement<D> {
1377
- machine: string;
1378
- session: string;
1379
- appAttempt: string;
1380
- device: D | null;
1381
- selected: string;
1382
- reason?: string;
1383
- agent: HostedAgentAccess;
1384
- }
1385
- declare function parseHostedNativePlacement(value: unknown, platform: "ios"): HostedNativePlacement<HostedIosDevice> | null;
1386
- declare function parseHostedNativePlacement(value: unknown, platform: "android"): HostedNativePlacement<HostedAndroidDevice> | null;
1387
- declare function parseHostedNativePlacement(value: unknown, platform: "ios" | "android"): HostedNativePlacement<HostedIosDevice | HostedAndroidDevice> | null;
1388
- declare function hostedNativeRecords(state: WorkspaceState | null, platform: "ios" | "android"): Record<string, unknown>;
1389
- declare function unreadableHostedNative(slot: string, platform: "ios" | "android"): string;
1390
- //#endregion
1391
- //#region state/hosted-android.d.ts
1392
- /** A workspace slot's placement; null device means its reservation has not reached ready yet. */
1393
- interface HostedAndroidPlacement extends HostedNativePlacement<HostedAndroidDevice> {}
1394
- /** Public hosting facts; the host's serial, AVD name and private Metro gateway are excluded. */
1395
- type HostedAndroidStatus = {
1396
- machine: string;
1397
- session: string;
1398
- selected: string;
1399
- reason?: string;
1400
- agent: HostedAgentAccess;
1401
- device: {
1402
- name: string;
1403
- systemImage: string;
1404
- api: number;
1405
- } | null;
1406
- /** The latest session probe in status, independent of a conflicting local emulator's state. */
1407
- state?: string;
1408
- };
1409
- declare function parseHostedAndroidPlacement(value: unknown): HostedAndroidPlacement | null;
1410
- declare function hostedAndroidStatus(placement: HostedAndroidPlacement): HostedAndroidStatus;
1411
- declare function hostedAndroidRecords(state: WorkspaceState | null): Record<string, unknown>;
1412
- declare function unreadableHostedAndroid(slot: string): string;
1413
- declare function hostedAndroidPlacements(state: WorkspaceState | null): Record<string, HostedAndroidPlacement>;
1414
- declare function hostedAndroidDeviceName(device: HostedAndroidDevice): string;
1415
- //#endregion
1416
- //#region state/hosted-ios.d.ts
1417
- /** A workspace slot's placement; null device means its reservation has not reached ready yet. */
1418
- interface HostedIosPlacement extends HostedNativePlacement<HostedIosDevice> {}
1419
- /** Public hosting facts; the host's UDID and private Metro gateway are excluded. */
1420
- type HostedIosStatus = {
1421
- machine: string;
1422
- session: string;
1423
- selected: string;
1424
- reason?: string;
1425
- agent: HostedAgentAccess;
1426
- device: {
1427
- name: string;
1428
- runtime: string;
1429
- } | null;
1430
- /** The latest session probe in status, independent of a conflicting local simulator's state. */
1431
- state?: string;
1347
+ type GitChipPart = {
1348
+ kind: "arrows";
1349
+ ahead: number;
1350
+ behind: number;
1351
+ } | {
1352
+ kind: "changed";
1353
+ count: number;
1354
+ } | {
1355
+ kind: "merged";
1356
+ into: string;
1357
+ } | {
1358
+ kind: "no-upstream";
1432
1359
  };
1433
- declare function parseHostedIosPlacement(value: unknown): HostedIosPlacement | null;
1434
- declare function hostedIosStatus(placement: HostedIosPlacement): HostedIosStatus;
1435
- declare function hostedIosRecords(state: WorkspaceState | null): Record<string, unknown>;
1436
- declare function unreadableHostedIos(slot: string): string;
1437
- declare function hostedIosPlacements(state: WorkspaceState | null): Record<string, HostedIosPlacement>;
1438
- //#endregion
1439
- //#region state/maintenance.d.ts
1440
- type MaintenanceMode = "off" | "report" | "on";
1441
- type MaintenanceCheck = "pressure" | "size" | "worktree" | "sweep" | "idle";
1442
- type MaintenanceActionKind = "would-clear-outputs" | "would-trim-cache" | "would-empty-cache" | "would-shutdown-device" | "would-stop-workspace" | "would-remove-worktree" | "would-remove-orphan" | "would-unregister-cache" | "would-delete-parked-device" | "would-delete-devices" | "would-clear-build-dirs" | "would-delete-dependencies" | "would-stop-gradle-daemons" | "would-delete-gradle-version" | "would-delete-temp-leftovers";
1443
- /** Disk pressure: 0 above every threshold, 1 under the budget floor, 2 under the urgent threshold, 3 under the critical threshold or the hard floor. */
1444
- type DiskPressureLevel = 0 | 1 | 2 | 3;
1445
- interface MaintenanceAction {
1446
- kind: MaintenanceActionKind;
1447
- target: string;
1448
- bytes: number;
1449
- reason: string;
1450
- workspace?: string;
1451
- dir?: string;
1452
- olderThanDays?: number;
1453
- check?: "worktree" | "sweep" | "disk";
1454
- /** The disk pressure level that planned the action; it runs only while free disk stays under that level's floor. */
1455
- level?: 2 | 3;
1360
+ /** A pull request's checks at a glance: any failing, else any pending, else any passing; null without checks. */
1361
+ type ChecksState = "passing" | "failing" | "pending" | null;
1362
+ interface GitChip {
1363
+ parts: GitChipPart[];
1364
+ ci: ChecksState;
1456
1365
  }
1457
- interface MaintenanceSize {
1458
- name: string;
1459
- dir: string;
1460
- bytes: number;
1461
- measuredAt: number;
1462
- category: "workspace-outputs" | "build-cache" | "metro-cache" | "compilation-cache" | "ccache" | "owned-devices" | "other";
1463
- workspace?: string;
1464
- idleDays?: number | null;
1465
- blocked?: string;
1366
+ /**
1367
+ * Whether a running owned simulator or emulator lacks the workspace's app: `none` when its platform never built
1368
+ * successfully here and the latest build failed, `closed` when status saw no app process, else null.
1369
+ */
1370
+ type AppPresence = "none" | "closed" | null;
1371
+ declare const WORKSPACE_STAGE_KINDS: readonly ["building", "warming", "ready", "build-failed", "running", "stopped"];
1372
+ /**
1373
+ * Where a workspace is, first match wins: `building` while a build runs, `warming` and `ready` from `phase` when
1374
+ * nothing is live, `build-failed` when the newest run of either platform failed, `running` when it is live, its
1375
+ * macOS app is running or orphaned, or it holds a remote device, else `stopped`. `since` is when that began: the
1376
+ * build's start, the warm phase's time, the failed run's end (else start), the supervisor's start (else the running
1377
+ * macOS app's build end, else its start), or Metro's last stop. `platform` names the build for `building`
1378
+ * and `build-failed`. `closedApps` lists, for `running`, the devices whose `appPresence` is `closed`.
1379
+ */
1380
+ interface WorkspaceStage {
1381
+ kind: (typeof WORKSPACE_STAGE_KINDS)[number];
1382
+ since: string | null;
1383
+ platform: StatsPlatform | "macos" | null;
1384
+ closedApps: {
1385
+ platform: StatsPlatform;
1386
+ slot: string;
1387
+ }[];
1466
1388
  }
1389
+ /** Every code a status issue can carry. */
1390
+ declare const STATUS_ISSUE_CODES: readonly ["port-not-ours", "sim-missing", "sim-without-metro", "avd-serial-changed", "avd-missing", "avd-not-detected", "avd-unchecked", "android-reverse-missing", "supervisor-unverified", "browser-unverified", "browser-orphaned"];
1391
+ type StatusIssueCode = (typeof STATUS_ISSUE_CODES)[number];
1467
1392
  /**
1468
- * A process whose physical footprint, as `top` reports it, is abnormally large. `command` is the one command that
1469
- * safely restarts or stops it, null when Stim knows none; `note` says what that command does.
1393
+ * One thing in a workspace that needs the user, or with severity `info` a note that needs nothing. `remedy` is a
1394
+ * command to run from `workspace`; `slot` is absent for the default device slot. `warnings` carries the `error` and
1395
+ * `warning` issues as text.
1396
+ *
1397
+ * Severity:
1398
+ * - `error`: Stim cannot verify or safely act on something; a person resolves it, following the teardown guide.
1399
+ * - `warning`: something is broken now and needs someone to act, such as an app that cannot reach Metro, a held
1400
+ * port or a device that vanished while expected.
1401
+ * - `info`: a fact the next normal Stim command handles by itself, so it needs no action.
1402
+ *
1403
+ * The phone app and Stim Desktop show `error` and `warning` and hide `info`; plain `stim status` prints `info` as a
1404
+ * dim note with no remedy.
1470
1405
  */
1471
- interface MemoryCulprit {
1472
- pid: number;
1473
- name: string;
1474
- bytes: number;
1475
- command: string | null;
1476
- note: string | null;
1406
+ interface StatusIssue {
1407
+ code: StatusIssueCode;
1408
+ severity: "error" | "warning" | "info";
1409
+ message: string;
1410
+ remedy: string;
1411
+ workspace: string;
1412
+ slot?: string;
1477
1413
  }
1478
- interface MaintenancePressure {
1479
- disk: {
1480
- volume: string;
1481
- freeMb: number;
1482
- }[];
1483
- memory: {
1484
- level: "normal" | "warning" | "critical" | null;
1485
- availableBytes: number | null;
1486
- pressured: boolean;
1487
- /** Read only while the level is warning or critical; absent otherwise. */
1488
- culprits?: MemoryCulprit[];
1489
- };
1490
- warningSince: number | null;
1491
- diskLevel?: DiskPressureLevel;
1414
+ /** Every state the owned page's latest load can report. */
1415
+ declare const WEB_PAGE_STATES: readonly ["loading", "loaded", "failed"];
1416
+ /**
1417
+ * The owned page's latest load, from the page-load marker in the workspace's web log: `url` is the document it
1418
+ * loaded, `state` is `failed` when that document did not load or the page crashed, with the log message as
1419
+ * `error`. `route`, present only when an in-app route change (history API or fragment) moved the page off `url`
1420
+ * since the load, is the URL it shows now.
1421
+ */
1422
+ interface WebPageState {
1423
+ url: string;
1424
+ state: (typeof WEB_PAGE_STATES)[number];
1425
+ error?: string;
1426
+ route?: string;
1492
1427
  }
1493
- interface MaintenancePass {
1494
- startedAt: number;
1495
- durationMs: number;
1496
- trigger: string;
1497
- mode: "report" | "on";
1498
- freedBytes: number;
1499
- actions: number;
1500
- stopped: 0;
1501
- blocked: string[];
1428
+ /**
1429
+ * The workspace's Stim-owned Chrome. `pid` is Chrome's process and `supervisorPid` the Stim process that holds
1430
+ * its DevTools session. `cdpEndpoint` is the loopback DevTools HTTP endpoint agents can attach to, and `targetId`
1431
+ * the DevTools target of the owned page; both are null when the browser is not running. `profile` is the
1432
+ * Stim-owned user data directory. `page` is null before the page's first load. `activity` names DevTools clients
1433
+ * other than Stim's as drivers.
1434
+ */
1435
+ interface WebBrowserState {
1436
+ browser: "chrome";
1437
+ version: string | null;
1438
+ running: boolean;
1439
+ pid: number | null;
1440
+ supervisorPid: number | null;
1441
+ url: string;
1442
+ headless: boolean;
1443
+ viewport: WebViewport;
1444
+ profile: string;
1445
+ cdpEndpoint: string | null;
1446
+ targetId: string | null;
1447
+ page?: WebPageState | null;
1448
+ activity?: DeviceActivity;
1502
1449
  }
1503
- interface MaintenanceRecord extends NdjsonRecord {
1504
- ts: number;
1505
- src: "maintenance";
1506
- level: "debug" | "info" | "warn" | "error";
1507
- msg: string;
1508
- event: "maintenance_pass" | "maintenance_action" | "maintenance_skip" | "maintenance_failure" | "maintenance_check";
1509
- pass: string;
1510
- trigger: string;
1511
- mode: "report" | "on";
1450
+ /** The steps of `stim worktree warm`: `refresh` fast-forwards and installs in the source checkout, `copy` carries ignored entries. */
1451
+ type WarmStep = "refresh" | "copy";
1452
+ /**
1453
+ * Where a workspace is in its lifecycle: `warming` while `stim worktree warm` runs in it, `ready` after a warm
1454
+ * succeeded and before any `start`, `ios`, `android`, `web` or `reload` there, for at most two hours; `live` when
1455
+ * anything it owns runs, else `idle`.
1456
+ */
1457
+ declare const WORKSPACE_PHASES: readonly ["warming", "ready", "live", "idle"];
1458
+ type WorkspacePhase = (typeof WORKSPACE_PHASES)[number];
1459
+ /** A folder's size on disk as `status --watch` last measured it. */
1460
+ interface DiskMeasure {
1461
+ bytes: number;
1462
+ measuredAt: string;
1512
1463
  }
1513
- interface MaintenanceState {
1514
- version: 1;
1515
- lastAt: Partial<Record<MaintenanceCheck, number>>;
1516
- deferredAt?: Partial<Record<MaintenanceCheck, number>>;
1517
- pressure: MaintenancePressure | null;
1518
- sizes: MaintenanceSize[];
1519
- lastPass: MaintenancePass | null;
1520
- recent: MaintenanceRecord[];
1521
- plan: MaintenanceAction[];
1522
- skipKeys?: string[];
1464
+ /**
1465
+ * An environment's disk use as `status --watch` last measured it. `worktreeBytes` is the git worktree folder, or the
1466
+ * workspace folder outside one, node_modules included. `nodeModulesBytes` is node_modules at the worktree root and,
1467
+ * when different, at the workspace path, and is part of `worktreeBytes`. `buildBytes` is Stim's own folder for the
1468
+ * workspace: Xcode derived data, Gradle outputs and logs. A figure is null until it was measured; `measuredAt` is the
1469
+ * oldest measurement.
1470
+ */
1471
+ interface EnvironmentDisk {
1472
+ worktreeBytes: number | null;
1473
+ nodeModulesBytes: number | null;
1474
+ buildBytes: number | null;
1475
+ measuredAt: string;
1523
1476
  }
1524
- interface MaintenanceStatus {
1525
- mode: MaintenanceMode;
1526
- invalid?: string;
1527
- claim?: {
1528
- unresolved: string;
1529
- removeCommand: string;
1530
- };
1531
- lastChecks: {
1532
- pressure: number | null;
1533
- size: number | null;
1534
- worktree: number | null;
1535
- sweep: number | null;
1477
+ /**
1478
+ * A Metro bundle request: `bundling` while one is in flight on the workspace's Metro, with its `platform`,
1479
+ * `startedAt` and, when Metro reported progress for it, `percent` (0-100). `last` is the newest finished request,
1480
+ * `durationMs` from request to response end.
1481
+ */
1482
+ interface MetroBundleState {
1483
+ bundling: boolean;
1484
+ platform?: StatsPlatform;
1485
+ startedAt?: string;
1486
+ percent?: number;
1487
+ last?: {
1488
+ platform: StatsPlatform;
1489
+ status: "ok" | "failed";
1490
+ durationMs: number;
1491
+ finishedAt: string;
1536
1492
  };
1537
- pressure: MaintenancePressure | null;
1538
- sizes: MaintenanceSize[];
1539
- lastPass: MaintenancePass | null;
1540
- running: {
1541
- startedAt: string;
1542
- trigger: string;
1543
- } | null;
1544
- recent: MaintenanceRecord[];
1545
- plan: MaintenanceAction[];
1546
1493
  }
1547
- /** Rejects missing, corrupt or unsupported payloads without throwing. */
1548
- declare function parseMaintenanceState(value: unknown): MaintenanceState | null;
1549
- declare function readMaintenanceState(): MaintenanceState | null;
1550
- declare function readMaintenanceAttempt(): number | undefined;
1551
- //#endregion
1552
- //#region state/archive.d.ts
1553
- interface ArchivedBytes {
1554
- logs: number;
1555
- recordings: number;
1556
- agentActions: number;
1557
- record: number;
1558
- total: number;
1494
+ interface SimulatorState {
1495
+ devicePlacement?: DevicePlacement;
1496
+ host?: HostedIosStatus;
1497
+ name: string | null;
1498
+ udid: string;
1499
+ owned: boolean;
1500
+ state: string;
1501
+ activity?: DeviceActivity;
1502
+ app?: DeviceAppProcess;
1503
+ appPresence?: AppPresence;
1504
+ /** The simulator's data folder, for an owned simulator once measured. */
1505
+ disk?: DiskMeasure;
1506
+ /** Present while the device is not booted after idle shutdown or queue reclaim. */
1507
+ idleShutdown?: DeviceIdleShutdownRecord;
1559
1508
  }
1560
- interface ArchivedWorkspace {
1561
- id: string;
1562
- projectRoot: string;
1563
- project: string;
1564
- workspace: string;
1565
- worktree: {
1566
- repository: string | null;
1567
- branch: string | null;
1568
- head: string | null;
1569
- subject: string | null;
1570
- merged: boolean | null;
1571
- pullRequest: {
1572
- number: number;
1573
- state: string;
1574
- title: string;
1575
- url: string;
1576
- } | null;
1577
- };
1578
- removedAt: string;
1579
- removedBy: string;
1580
- lastUsedAt: string | null;
1581
- builds: {
1582
- count: number;
1583
- last: LastBuildReport | null;
1584
- lastErrorCount: number;
1509
+ interface AndroidDeviceState {
1510
+ devicePlacement?: DevicePlacement;
1511
+ host?: HostedAndroidStatus;
1512
+ name: string | undefined;
1513
+ owned: boolean;
1514
+ physical: boolean;
1515
+ serial?: string | null;
1516
+ state?: AndroidRuntimeFacts["state"] | "ready" | "stopped" | "unverified";
1517
+ deviceProfile?: string | null;
1518
+ activity?: DeviceActivity;
1519
+ app?: DeviceAppProcess;
1520
+ appPresence?: AppPresence;
1521
+ /** The AVD's folder, for an owned emulator once measured. */
1522
+ disk?: DiskMeasure;
1523
+ /** Present while the emulator is not running after idle shutdown or queue reclaim. */
1524
+ idleShutdown?: DeviceIdleShutdownRecord;
1525
+ }
1526
+ /** The tutorial format version the installed Stim writes into the tutorial app. */
1527
+ declare const TUTORIAL_VERSION = 2;
1528
+ interface EnvironmentState {
1529
+ slots?: {
1530
+ slot: string;
1531
+ ios: SimulatorState | null | undefined;
1532
+ android: AndroidDeviceState | null | undefined;
1533
+ }[];
1534
+ path: string;
1535
+ /** The 16-hex identity of the registered canonical project path, used by shared workspace links. */
1536
+ workspaceId?: string;
1537
+ /** Statically detected app platforms; always set by status, empty when none are detected. */
1538
+ platforms?: string[];
1539
+ /** Present when the app's app.json sets expo.extra.stimTutorial to a positive integer: that version. */
1540
+ tutorial?: {
1541
+ version: number;
1585
1542
  };
1586
- agents: EndedAgentSession[];
1587
- bytes: ArchivedBytes;
1588
- expires: {
1589
- logs: string | null;
1590
- recordings: string | null;
1591
- agentActions: string | null;
1592
- record: string | null;
1543
+ /** When `stim doctor` last ran in this workspace, per platform; absent until it has run. */
1544
+ doctorRuns?: Partial<Record<"ios" | "android", {
1545
+ at: string;
1546
+ }>>;
1547
+ live: boolean;
1548
+ phase?: WorkspacePhase;
1549
+ /** When the warm started (`warming`) or finished (`ready`); null for `live` and `idle`. */
1550
+ phaseSince?: string | null;
1551
+ /** Whether stim-server may record this workspace's device screens, from `recording.enabled`. */
1552
+ recording?: {
1553
+ enabled: boolean;
1593
1554
  };
1594
- version: number;
1595
- replacedBy?: string;
1596
- }
1597
- interface ArchivedUsage {
1598
- count: number;
1599
- bytes: number;
1600
- byKind: {
1601
- logs: number;
1602
- recordings: number;
1603
- agentActions: number;
1604
- record: number;
1555
+ /** Present when `workspace.hidden` is true: clients list the workspace under Hidden. */
1556
+ hidden?: true;
1557
+ /** Where the workspace is, from `workspaceStage`; `stim status --json` always sets it. */
1558
+ stage?: WorkspaceStage;
1559
+ /** The step a `warming` workspace's warm is in; absent in every other phase. */
1560
+ warmStep?: WarmStep;
1561
+ /**
1562
+ * The memory this workspace's processes use, as `memorySource` says: the sum of its `machine` owners' `memoryMb`
1563
+ * when status read their footprints, or else a fixed estimate per booted simulator, detected emulator, running
1564
+ * Metro and running Chrome. `capacity.committedMb` sums it.
1565
+ */
1566
+ memoryMb: number;
1567
+ /** How `memoryMb` was obtained; absent from payloads written before it existed, which carry the estimate. */
1568
+ memorySource?: Exclude<MemorySource, "rss">;
1569
+ warnings: string[];
1570
+ issues: StatusIssue[];
1571
+ ios?: SimulatorState | null;
1572
+ android?: AndroidDeviceState | null;
1573
+ metro?: {
1574
+ port: number;
1575
+ running: boolean;
1576
+ pid: number | null;
1577
+ tunnel?: {
1578
+ provider: Exclude<TunnelMode, "auto" | "off" | "expo">;
1579
+ url: string;
1580
+ };
1581
+ idleStop?: IdleStopRecord;
1582
+ lastStop?: MetroLastStop;
1583
+ /** The other process that answers Metro on `port`; `cwd` is null when its directory could not be read. */
1584
+ heldBy?: {
1585
+ pid: number;
1586
+ cwd: string | null;
1587
+ };
1588
+ /** Absent when the Metro log holds no bundle request. */
1589
+ bundle?: MetroBundleState;
1590
+ } | null;
1591
+ web?: WebBrowserState | null;
1592
+ macos?: MacosAppState | null;
1593
+ supervisor?: {
1594
+ pid: number | null;
1595
+ mode: string | null;
1596
+ startedAt: string | null;
1597
+ healthy: boolean;
1598
+ } | null;
1599
+ logs?: {
1600
+ dir: string;
1601
+ errorsSinceMarker: number;
1602
+ } | null;
1603
+ /** `installed` is whether the agent-device executable is on the PATH of the process that ran status. */
1604
+ agentDevice?: {
1605
+ stateDir: string;
1606
+ installed?: boolean;
1605
1607
  };
1608
+ worktree?: WorktreeFacts | null;
1609
+ remoteDevices?: RemoteDeviceState[];
1610
+ /** The physical devices this workspace leases, in every slot; absent when it leases none. */
1611
+ physicalDevices?: PhysicalDeviceState[];
1612
+ build?: BuildReport | null;
1613
+ lastBuilds?: Partial<Record<StatsPlatform, LastBuildReport>>;
1614
+ /** Each platform's recent runs, newest first, at most `BUILD_HISTORY_LIMIT` each. */
1615
+ builds?: Partial<Record<BuildPlatform, BuildHistoryEntry[]>>;
1616
+ disk?: EnvironmentDisk;
1617
+ /** The coding-agent sessions working in the environment, most recently active first; absent when none. */
1618
+ agents?: AgentSession[];
1619
+ /** The agent sessions that stopped running here in the last 3 days, most recently ended first; absent when none. */
1620
+ endedAgents?: EndedAgentSession[];
1606
1621
  }
1607
- declare function readArchives(): ArchivedWorkspace[];
1608
- /** Reads current archive files asynchronously, outside the synchronous state-read cache; cancellation propagates. */
1609
- declare function readArchivesAsync(signal?: AbortSignal): Promise<ArchivedWorkspace[]>;
1610
- declare function readArchive(id: string): ArchivedWorkspace | null;
1611
- /** Reads a current archive without the synchronous state-read cache; invalid or missing records return null. */
1612
- declare function readArchiveAsync(id: string, signal?: AbortSignal): Promise<ArchivedWorkspace | null>;
1613
- declare function readArchiveState(id: string): WorkspaceState | null;
1614
- declare function archivedUsage(archives: readonly ArchivedWorkspace[]): ArchivedUsage;
1615
- declare function linkReplacedArchives(archives: readonly ArchivedWorkspace[], paths: readonly string[]): ArchivedWorkspace[];
1616
- declare function archiveEnabled(env: NodeJS.ProcessEnv, layers: readonly unknown[]): boolean;
1617
- //#endregion
1618
- //#region state/macos.d.ts
1619
- interface MacosProcess {
1620
- pid: number;
1621
- processToken: string;
1622
- startedAtMicros: number;
1622
+ /** `committedMb` sums the environments' `memoryMb`; `overCapacity` is that sum over 60% of `totalMemoryMb`. */
1623
+ interface StatusCapacity {
1624
+ liveCount: number;
1625
+ committedMb: number;
1626
+ totalMemoryMb: number;
1627
+ overCapacity: boolean;
1628
+ /** `concurrency.maxBuilds` usage; present only while that cap is set. */
1629
+ builds?: StatusBuildSlots;
1630
+ /** `concurrency.maxDevices` usage; present only while that cap is set. */
1631
+ devices?: StatusDeviceSlots;
1632
+ /** This machine's load. Optional only so payloads from releases that predate it still decode. */
1633
+ load?: StatusCapacityLoad;
1623
1634
  }
1624
- interface MacosBuild {
1625
- state: "running" | "ok" | "failed";
1626
- startedAt: string;
1627
- finishedAt?: string;
1628
- durationMs?: number;
1629
- fingerprint?: string;
1630
- cacheKey?: string;
1631
- cacheHit?: boolean;
1632
- cacheSource?: "local" | "provider" | "peer";
1633
- error?: string;
1634
- errorCode?: string;
1635
- buildMachine?: string;
1636
- builtOn?: string;
1637
- offloadedTo?: string;
1638
- offloadFallback?: string;
1635
+ /**
1636
+ * `free` is under `busyAt` (`server.maxLoadPerCore`), where placement keeps work on this machine; `busy` is at or over
1637
+ * it, where placement prefers another machine; `holding` is at or over `limit`, where new local native work waits.
1638
+ */
1639
+ type LoadState = "free" | "busy" | "holding";
1640
+ /**
1641
+ * `loadPerCore` is the 5-minute load average divided by `cpus`, rounded to one decimal. `limit` is the load per core at
1642
+ * which new local native work waits, null while no limit is configured.
1643
+ */
1644
+ interface StatusCapacityLoad {
1645
+ cpus: number;
1646
+ loadPerCore: number;
1647
+ busyAt: number;
1648
+ limit: number | null;
1649
+ state: LoadState;
1639
1650
  }
1640
- interface MacosAppRecord {
1641
- launchId: string;
1642
- arguments: string[];
1643
- product: string;
1644
- /** The app's visible name: the product and the workspace label. Absent in older records. */
1645
- displayName?: string;
1646
- bundle: string;
1647
- bundleId: string;
1648
- executable: string;
1649
- build: MacosBuild;
1650
- supervisor?: MacosProcess;
1651
- app?: MacosProcess;
1652
- /** Set when `stim macos --remote` reserved a session on another Mac; the app then has no local process. */
1653
- host?: HostedMacosPlacement;
1654
- /** Host launch evidence: true for a live app; status can override it in memory after probing the session. */
1655
- hostLaunched?: boolean | "unverified";
1651
+ /**
1652
+ * A held build slot. `workspace` is the root of the local build holding it, `client` the device id of the client
1653
+ * whose offloaded build this machine runs in it; both are null for a slot whose holder Stim cannot read.
1654
+ */
1655
+ interface StatusBuildSlotHolder {
1656
+ slot: number;
1657
+ workspace: string | null;
1658
+ client: string | null;
1659
+ since: string | null;
1660
+ unresolved?: true;
1656
1661
  }
1657
- interface MacosAppState extends MacosAppRecord {
1658
- state: "running" | "orphaned" | "stopped" | "unverified";
1662
+ /** `inUse` counts the slots below `max` that a live or unresolved claim holds, which a new build cannot take. */
1663
+ interface StatusBuildSlots {
1664
+ max: number;
1665
+ inUse: number;
1666
+ holders: StatusBuildSlotHolder[];
1659
1667
  }
1660
- declare function parseMacosRecord(value: unknown): MacosAppRecord | null;
1661
- declare function readMacosRecord(root: string): MacosAppRecord | null;
1662
- declare function readHostedMacosApp(home: string): MacosAppRecord | null;
1663
- declare function macosAppState(record: MacosAppRecord | null): MacosAppState | null;
1664
- //#endregion
1665
- //#region state/status.d.ts
1666
- type StatsPlatform = "ios" | "android";
1667
- /** A platform whose build reports phases: the stats platforms and `macos`. */
1668
- type BuildPlatform = StatsPlatform | "macos";
1669
- type RunOutcomeKind = "hit" | "cold";
1670
- declare const BUILD_PHASES: readonly ["prepare", "cache-lookup", "wait", "prebuild", "pods", "compile", "device", "install", "launch"];
1671
- type BuildPhase = (typeof BUILD_PHASES)[number];
1672
- type ActiveBuildState = "running" | "stale" | "unknown";
1673
1668
  /**
1674
- * Where a running build compiles: `local`, or the remote machine it was offloaded to, with the phase it is in there
1675
- * (`sync`, `deps`, `prebuild`, `pods`, `build` or `fetch`) and when the offload and that phase started.
1669
+ * An owned device counted against `concurrency.maxDevices`. `label` is the label of the workspace that records it, or
1670
+ * the device's name when no workspace does; `workspace` and `slot` are null then.
1676
1671
  */
1677
- type BuildPlacement = "local" | {
1678
- host: string;
1679
- phase: string;
1680
- startedAt: string;
1681
- phaseStartedAt: string;
1682
- };
1683
- interface BuildWaitingFor {
1684
- kind: "build-slot" | "device-slot";
1685
- inUse: number;
1672
+ interface StatusDeviceSlotHolder {
1673
+ platform: "ios" | "android";
1674
+ label: string;
1675
+ workspace: string | null;
1676
+ slot: string | null;
1677
+ booting?: true;
1678
+ }
1679
+ /** `inUse` is null, with `unknown` saying why, when Stim cannot count the owned devices. */
1680
+ interface StatusDeviceSlots {
1686
1681
  max: number;
1687
- since: string;
1682
+ inUse: number | null;
1683
+ unknown?: string;
1684
+ holders: StatusDeviceSlotHolder[];
1688
1685
  }
1689
- interface BuildReport {
1690
- platform: BuildPlatform;
1691
- slot: string;
1692
- state: ActiveBuildState;
1693
- phase: BuildPhase;
1694
- startedAt: string;
1695
- phaseStartedAt: string;
1696
- outcome: RunOutcomeKind | null;
1697
- /** Whether `outcome` is this run's own; before its cache lookup resolves it is the project's latest outcome. */
1698
- outcomeKnown: boolean;
1699
- /** The resolved lookup result; absent before resolution and when the run skips cache lookup. */
1700
- cacheLookupOutcome?: "hit" | "miss";
1701
- expectedMs: number | null;
1702
- expectedPhaseMs: number | null;
1703
- /** Milliseconds spent in phases already left; sums repeat visits, excludes the current visit, and is omitted before any phase completes and by older Stim. */
1704
- completedPhaseMs?: Partial<Record<BuildPhase, number>>;
1705
- basis: number;
1706
- /**
1707
- * The phases a run like this one goes through, in order, with each one's median in milliseconds, from the same
1708
- * runs as `expectedMs`; null without comparable runs. The current phase can be one the plan does not list.
1709
- */
1710
- plannedPhases: PlannedPhase[] | null;
1711
- /** Why the run's cache lookup missed, once the run knows; `baseline` omits `cacheKey`. */
1712
- missReason?: BuildMissReason;
1713
- /**
1714
- * True while `missReason` is the miss of the key the run looked up first and the run will prebuild or install pods,
1715
- * then look the key up again. A hit on the second lookup removes `missReason` and sets `outcome` to `hit`.
1716
- */
1717
- missProvisional?: true;
1718
- /** What the native build tool is doing now, once it printed a line Stim recognizes. */
1719
- detail?: BuildDetail;
1720
- /** What the `device` or `launch` phase is waiting on, once Stim observed it; absent otherwise and from older Stim. */
1721
- activity?: BuildActivity;
1722
- placement: BuildPlacement;
1723
- /** While `phase` is `wait`: the workspace whose build of the same artifact this run waits on, when it is known. */
1724
- waitingOn?: {
1725
- path: string;
1726
- };
1727
- waitingFor?: BuildWaitingFor;
1686
+ interface DeviceLeaseState {
1687
+ slot?: string;
1688
+ path: string;
1689
+ platform: string;
1690
+ id: string | null;
1691
+ deviceName: string | null;
1692
+ holder: string | null;
1693
+ grantedAt: string | null;
1694
+ expiresAt: string | null;
1695
+ mine: boolean;
1696
+ expired: boolean;
1697
+ parsed: boolean;
1728
1698
  }
1729
1699
  /**
1730
- * What a run's `device` or `launch` phase is waiting on. `booting` lasts while the run waits for the owned simulator or
1731
- * emulator it started booting. `bundling` starts when the app's bundle request reaches this workspace's
1732
- * Metro and ends when Metro delivered it; `percent` is Metro's own progress for that request, present only once Metro
1733
- * reported one. `waiting-ready` lasts while the app reports its readiness pending. `startedAt` is when the evidence was
1734
- * recorded.
1700
+ * Where a memory figure comes from. `footprint` is each process's physical footprint, which Activity Monitor's Memory
1701
+ * column shows. `rss` sums resident set sizes, which count pages shared between processes once per process, so a
1702
+ * simulator reports well above its footprint; the machine owners fall back to it when the footprint helper cannot be
1703
+ * built. `estimate` is a fixed amount per running thing, which an environment reports whenever it has no footprint.
1735
1704
  */
1736
- interface BuildActivity {
1737
- name: "booting" | "bundling" | "waiting-ready";
1738
- startedAt: string;
1739
- percent?: number;
1740
- }
1741
- /** One phase a running build is expected to go through, and its median duration in comparable runs. */
1742
- interface PlannedPhase {
1743
- phase: BuildPhase;
1744
- expectedMs: number;
1745
- }
1746
- /** The native build tool's step inside a build's `compile` phase. */
1747
- declare const NATIVE_BUILD_STEPS: readonly ["configure", "compile", "link", "resources", "script", "dex", "package", "sign"];
1748
- type NativeBuildStep = (typeof NATIVE_BUILD_STEPS)[number];
1705
+ declare const MEMORY_SOURCES: readonly ["footprint", "rss", "estimate"];
1706
+ type MemorySource = (typeof MEMORY_SOURCES)[number];
1707
+ /** Every kind of process owner the status machine section reports. */
1708
+ declare const MACHINE_OWNER_KINDS: readonly ["simulator", "emulator", "metro", "build", "browser", "macos", "server", "shared"];
1709
+ type MachineOwnerKind = (typeof MACHINE_OWNER_KINDS)[number];
1749
1710
  /**
1750
- * A running build's progress as its tool reports it. `unit` is `targets` for xcodebuild, `tasks` for Gradle and
1751
- * `steps` for SwiftPM. xcodebuild's `done` counts targets it finished and `total` the targets in its dependency graph,
1752
- * which includes targets with nothing to do; Gradle's `done` counts the tasks it reported, with a null `total`;
1753
- * SwiftPM's `done` and `total` are the `[done/total]` of its latest progress line, and its total can grow while it
1754
- * builds. They are counts, not a completion fraction. `status` reports the detail only during the `compile` phase. `line` is
1755
- * the latest compile, link or task line with paths shortened to file names.
1711
+ * One thing using this machine's CPU and memory now. Each process is counted in exactly one owner: the one whose root
1712
+ * process is its nearest ancestor. `workspace` and `slot` (absent for the default slot) name the workspace a
1713
+ * simulator, emulator, Metro, build or Chrome belongs to; a device no workspace records has a null workspace, and
1714
+ * machine-wide processes are `shared`. `id` is the simulator's UDID, the emulator's AVD name, Metro's port as text,
1715
+ * the build's platform, or null. `owned` is true only for what Stim started and stops: a workspace's owned device,
1716
+ * Metro, build or Chrome. `cpuPercent` is `ps` %CPU summed over the owner's processes, where 100 is one core.
1717
+ * `residentMb` sums their resident set sizes; pages shared between processes count once per process, so a
1718
+ * simulator, whose processes all map the runtime's shared libraries, reports well above its physical footprint.
1719
+ * `memoryMb` sums their physical footprints when `MachineUsageState.memorySource` is `footprint`, taking the resident
1720
+ * size of a process the footprint helper could not read, and equals `residentMb` when it is `rss`.
1756
1721
  */
1757
- interface BuildDetail {
1758
- step: NativeBuildStep | null;
1759
- unit: "targets" | "tasks" | "steps" | null;
1760
- done: number | null;
1761
- total: number | null;
1762
- line: string | null;
1763
- updatedAt: string;
1722
+ interface MachineOwner {
1723
+ kind: MachineOwnerKind;
1724
+ name: string;
1725
+ workspace: string | null;
1726
+ slot?: string;
1727
+ id: string | null;
1728
+ owned: boolean;
1729
+ cpuPercent: number;
1730
+ residentMb: number;
1731
+ memoryMb: number;
1732
+ processes: number;
1764
1733
  }
1765
- /** Where a build's app came from: a cache tier, or `false` when it compiled or failed before one was found. */
1766
- type BuildCacheHit = "local" | "remote" | false;
1767
- /** A platform's most recent `ios` or `android` run in one workspace. */
1768
- interface DevicePlacement {
1769
- decision: "local" | "hosted" | "waited-locally" | "eas";
1770
- reason: string;
1771
- machine?: string;
1734
+ /** Where this machine's CPU and memory go, from one pass over the host process table. */
1735
+ interface MachineUsageState {
1736
+ memorySource: Exclude<MemorySource, "estimate">;
1737
+ owners: MachineOwner[];
1772
1738
  }
1773
- interface LastBuildReport {
1774
- devicePlacement?: DevicePlacement;
1775
- platform: BuildPlatform;
1776
- status: "ok" | "failed";
1777
- cacheHit: BuildCacheHit;
1778
- cacheSkipped: boolean;
1779
- durationMs: number | null;
1780
- fingerprint: string | null;
1781
- startedAt: string;
1782
- finishedAt: string | null;
1783
- errorCode?: string;
1784
- missReason?: BuildMissReason;
1785
- /** The selected auto, local, or configured machine entry; present on new build records, including cache hits. */
1786
- buildMachine?: string;
1787
- /** Where compilation ran: here or a machine name; absent when no build ran. */
1788
- builtOn?: string;
1789
- /** The remote machine that compiled the app when the build was offloaded. */
1790
- offloadedTo?: string;
1791
- /** Why the run built here after it considered offloading; absent when it offloaded or never considered it. */
1792
- offloadFallback?: string;
1793
- /** The first compiler diagnostics of a failed build, when the build tool reported any. */
1794
- diagnostics?: BuildDiagnostic[];
1795
- /** Present on a failed run: what failed, from `buildCause`. */
1796
- cause?: BuildCause;
1739
+ /** The payload `stim status --json` prints, and `status --watch --json` prints on each change. */
1740
+ interface StatusPayload {
1741
+ maintenance?: MaintenanceStatus;
1742
+ archived?: ArchivedWorkspace[];
1743
+ archivedUsage?: ArchivedUsage;
1744
+ environments: (EnvironmentState & {
1745
+ labelOnly?: true;
1746
+ })[];
1747
+ capacity: StatusCapacity;
1748
+ deviceLeases: DeviceLeaseState[];
1749
+ unprovisionedWorktrees: WorktreeFacts[];
1750
+ simctlAvailable: boolean;
1751
+ /** Null when nothing runs that status attributes, so it read no process table, or when the table was unreadable. */
1752
+ machine: MachineUsageState | null;
1797
1753
  }
1754
+ //#endregion
1755
+ //#region state/build-capacity.d.ts
1756
+ declare function buildSlotsDir(): string;
1757
+ declare function buildSlotPath(index: number): string;
1798
1758
  /**
1799
- * A failed run's cause: the first diagnostic with a file and a line, keyed `file:line`, else the run's error code
1800
- * (or `failed`), with null `file` and `line`. Consecutive failures with the same `key` failed the same way.
1759
+ * Takes the first free one of `max` `concurrency.maxBuilds` slots, recording `details` and the slot `index`. Returns
1760
+ * null while every slot is held; throws the claim refusal when no slot is busy but one cannot be resolved.
1801
1761
  */
1802
- interface BuildCause {
1803
- key: string;
1804
- file: string | null;
1805
- line: number | null;
1806
- }
1807
- /** How a recorded build ended. `interrupted` is a run whose process ended without recording a result. */
1808
- declare const BUILD_RESULTS: readonly ["succeeded", "failed", "cancelled", "interrupted"];
1809
- type BuildResult = (typeof BUILD_RESULTS)[number];
1762
+ declare function tryAcquireBuildSlotClaim({ max, details }: {
1763
+ max: number;
1764
+ details: Record<string, unknown>;
1765
+ }): {
1766
+ claim: ClaimHandle;
1767
+ index: number;
1768
+ path: string;
1769
+ } | null;
1810
1770
  /**
1811
- * One run in a workspace's recent build history. `configuration` is the iOS configuration or the Android
1812
- * variant the run built, `Debug` or `debug` by default, and null when the run ended before resolving it.
1813
- * `phases` holds the milliseconds spent in each phase the run entered. An interrupted run has null
1814
- * `durationMs` and `finishedAt`, no cache facts, and 0 for the phase it stopped in.
1771
+ * How busy this machine is for native builds. `loadPerCore` is the 5-minute load average divided by the CPU count,
1772
+ * rounded to one decimal. `builds` counts this Stim home's runs that hold a live native-run claim and are in
1773
+ * prebuild, pods or compile here, not on a remote machine, plus the build slots this machine holds for other machines'
1774
+ * offloaded builds. `maxBuilds` is `concurrency.maxBuilds`, 0 when unlimited.
1815
1775
  */
1816
- interface BuildHistoryEntry extends LastBuildReport {
1817
- result: BuildResult;
1818
- slot: string;
1819
- configuration: string | null;
1820
- cacheKey: string | null;
1821
- phases: Partial<Record<BuildPhase, number>>;
1822
- /** A `stim macos` run's SwiftPM step total; absent for other runs and when the build reported none. */
1823
- compileSteps?: number;
1824
- }
1825
- /** One compiler error from a failed build: where it is, when the tool said, and its message. */
1826
- interface BuildDiagnostic {
1827
- file: string | null;
1828
- line: number | null;
1829
- column: number | null;
1830
- message: string;
1831
- }
1832
- /** What kind of native input a changed fingerprint source is. */
1833
- type BuildMissCategory = "native-dependency" | "config-plugin" | "app-config" | "app-asset" | "package" | "native-dir" | "autolinking" | "package-scripts" | "file" | "other";
1834
- interface BuildMissChange {
1835
- source: string;
1836
- change: "added" | "removed" | "changed";
1837
- category: BuildMissCategory;
1776
+ interface MachineCapacity {
1777
+ cpus: number;
1778
+ loadPerCore: number;
1779
+ builds: number;
1780
+ maxBuilds: number;
1781
+ maxLoadPerCore: number;
1838
1782
  }
1783
+ declare function maxLoadPerCore(): number;
1784
+ /** The 5-minute load average per CPU, rounded to one decimal, and the CPU count it divides by. */
1785
+ declare function loadPerCore(): {
1786
+ cpus: number;
1787
+ loadPerCore: number;
1788
+ };
1789
+ declare function loadState(load: number, busyAt: number, limit: number | null): LoadState;
1790
+ declare function machineCapacity(): MachineCapacity;
1791
+ /** Why a machine with this capacity should not take another native build; null when it can. */
1792
+ declare function saturation(capacity: MachineCapacity): string | null;
1793
+ //#endregion
1794
+ //#region state/build-machines.d.ts
1839
1795
  /**
1840
- * Why a run compiled instead of installing a cached app. `changes` holds at most 20 entries,
1841
- * ordered by importance; `changeCount` is the full number of changed fingerprint sources.
1842
- * Only a plan reports `prebuild-pending`: the run would prebuild before compiling, so the changes
1843
- * compare the fingerprint before that prebuild.
1796
+ * What this machine keeps about a remote machine it asked for access. `machine` is the `remote.machines` entry,
1797
+ * `transport` how it is reached, and `address` the tailnet name or local network host it resolved to. Once approved, `serverKey` is the worker's pinned stim-server key: every later
1798
+ * connection must prove it holds that key. While pending, `ticket` is the setup ticket this machine generated or was
1799
+ * given, and `expiresAt` its expiry when known. Records of protocol 1, which pinned a tailnet node, are not read.
1844
1800
  */
1845
- interface BuildMissReason {
1846
- kind: "changed" | "no-baseline" | "same-sources" | "cache-skipped" | "fingerprint-error" | "prebuild-pending";
1847
- summary: string;
1848
- changes: BuildMissChange[];
1849
- changeCount: number;
1850
- /** `cacheKey` is kept in workspace state so a later miss can find this baseline; status omits it. */
1851
- baseline: {
1852
- fingerprint: string;
1853
- cacheKey?: string;
1854
- from: "workspace" | "project";
1855
- } | null;
1856
- rekeyedBy: string[];
1801
+ interface MachineCredential {
1802
+ machine: string;
1803
+ transport: "tailscale" | "local";
1804
+ address: string;
1805
+ state: "pending" | "approved";
1806
+ requestedAt: string;
1807
+ serverKey?: string;
1808
+ deviceId?: string;
1809
+ ticket?: string;
1810
+ expiresAt?: string;
1857
1811
  }
1858
- /** The payload `stim ios --plan --json` and `stim android --plan --json` print. */
1859
- interface BuildPlanPayload {
1860
- platform: StatsPlatform;
1861
- slot?: string;
1862
- fingerprint: string;
1863
- cacheKey: string | null;
1864
- cacheHit: BuildCacheHit;
1865
- provider: string | null;
1866
- cacheSkipped: boolean;
1867
- prebuild: "none" | "generate" | "regenerate" | "refuse" | null;
1868
- outcome: RunOutcomeKind | null;
1869
- expectedMs: number | null;
1870
- basis: number;
1871
- /** On a predicted miss with cache reads on, why the cache has no app; `baseline` omits `cacheKey`. */
1872
- missReason?: BuildMissReason;
1873
- refusal?: {
1874
- code: string;
1875
- message: string;
1876
- remedy: string;
1812
+ type BuildMachineCredential = MachineCredential;
1813
+ declare const MACHINE_CREDENTIALS_VERSION = 2;
1814
+ /** A stored credential, or null when it is malformed or from an older version. */
1815
+ declare function parseMachineCredential(value: unknown): MachineCredential | null;
1816
+ declare function readBuildMachines(): BuildMachineCredential[];
1817
+ //#endregion
1818
+ //#region state/client-identity.d.ts
1819
+ /** This machine's long-term X25519 key as a client of other machines, in `$STIM_HOME/client-identity.key`. */
1820
+ declare function clientIdentity(): NoiseKeyPair;
1821
+ //#endregion
1822
+ //#region state/config-types.d.ts
1823
+ type SettingsObject = Record<string, unknown>;
1824
+ interface SupervisorRecord {
1825
+ processToken?: string;
1826
+ pid?: number;
1827
+ port?: number;
1828
+ startedAt?: string;
1829
+ serverPid?: number;
1830
+ mode?: string;
1831
+ [key: string]: unknown;
1832
+ }
1833
+ interface IosDeviceRecord {
1834
+ deviceUdid?: string;
1835
+ deviceName?: string | null;
1836
+ owned?: boolean;
1837
+ serial?: string;
1838
+ simslimManaged?: boolean;
1839
+ simslimApplied?: {
1840
+ profile: string;
1841
+ springBoardPid: number;
1877
1842
  };
1878
- /** With ios.remote or android.remote set to auto or a Mac, where the plan assumes the device runs, such as `this machine; auto may use janics-mac-mini`. */
1879
- placement?: string;
1843
+ schemeApprovals?: string[];
1844
+ [key: string]: unknown;
1845
+ }
1846
+ interface AndroidDeviceRecord {
1847
+ avdName?: string;
1848
+ consolePort?: number;
1849
+ serial?: string;
1850
+ kind?: string;
1851
+ deviceName?: string | null;
1852
+ owned?: boolean;
1853
+ setupIncomplete?: boolean;
1854
+ bootPending?: boolean;
1855
+ [key: string]: unknown;
1856
+ }
1857
+ type DeviceRecord = IosDeviceRecord | AndroidDeviceRecord;
1858
+ interface PlatformRecords {
1859
+ ios?: IosDeviceRecord;
1860
+ android?: AndroidDeviceRecord;
1861
+ [platform: string]: DeviceRecord | undefined;
1862
+ }
1863
+ interface ProjectRecord {
1864
+ ports?: Record<string, number>;
1865
+ metroPort?: number | null;
1866
+ platforms?: PlatformRecords;
1867
+ deviceSlots?: Record<string, PlatformRecords>;
1868
+ supervisor?: SupervisorRecord;
1869
+ settings?: SettingsObject;
1870
+ worktreeRoot?: boolean;
1871
+ worktreeBranch?: string;
1872
+ worktreeBranchOwned?: boolean;
1873
+ worktreeMainRoot?: string;
1874
+ worktreeRemovalComplete?: boolean;
1875
+ worktreePendingBranchSha?: string;
1876
+ label?: string;
1877
+ bundleId?: string;
1878
+ androidPackage?: string;
1879
+ isExpo?: boolean;
1880
+ lastBuild?: Record<string, unknown>;
1881
+ doctorRuns?: Partial<Record<"ios" | "android", DoctorRunRecord>>;
1882
+ [key: string]: unknown;
1880
1883
  }
1881
- /** The read-only `stim macos --plan --json` payload; SwiftPM work and worker availability remain unknown. */
1882
- interface MacosBuildPlanPayload {
1883
- platform: "macos";
1884
- product: string;
1885
- buildMachine: string;
1886
- fingerprint: string | null;
1887
- cacheKey: string | null;
1888
- cacheHit: boolean;
1889
- provider: null;
1890
- cacheSkipped: boolean;
1891
- prebuild: null;
1892
- outcome: null;
1893
- expectedMs: null;
1894
- basis: number;
1884
+ interface DoctorRunRecord {
1885
+ at: string;
1886
+ version: string;
1895
1887
  }
1896
- type ActivityState = "driven" | "active" | "idle" | "unknown";
1897
- interface ActivityDriver {
1898
- tool: string;
1899
- pid: number | null;
1900
- since: string | null;
1888
+ interface RepoRecord {
1889
+ settings?: SettingsObject;
1890
+ [key: string]: unknown;
1901
1891
  }
1902
- /** The kinds of evidence that date a device's last activity, in the order `recent` lists them. */
1903
- declare const ACTIVITY_RECENCY_BASES: readonly ["agent-action", "device-log", "metro-bundle", "workspace-use", "supervisor-start", "page-log", "viewer"];
1904
- type ActivityRecencyBasis = (typeof ACTIVITY_RECENCY_BASES)[number];
1905
- interface DeviceActivity {
1906
- state: ActivityState;
1907
- driver?: ActivityDriver;
1908
- lastActivityAt?: string;
1909
- /** The newest time of each kind of evidence behind `lastActivityAt`, so a reader can leave out app log chatter. */
1910
- recent?: Partial<Record<ActivityRecencyBasis, string>>;
1911
- basis: string[];
1892
+ interface ConcurrencyLimits {
1893
+ maxBuilds: number;
1894
+ maxDevices: number;
1912
1895
  }
1913
- /** Every state a device's app process can report. */
1914
- declare const APP_PROCESS_STATES: readonly ["running", "stopped", "unknown"];
1915
- /**
1916
- * Whether the workspace's app process is alive on a device now. This is current process state, not launch evidence:
1917
- * `launched` in an `ios` or `android` result stays the record of one launch. `id` is the bundle identifier or
1918
- * package that was checked; `state` is "unknown" when the process listing could not be read.
1919
- */
1920
- interface DeviceAppProcess {
1921
- id: string;
1922
- state: (typeof APP_PROCESS_STATES)[number];
1896
+ interface StimConfig {
1897
+ version?: number;
1898
+ homeId?: string;
1899
+ projects: Record<string, ProjectRecord>;
1900
+ repos: Record<string, RepoRecord>;
1901
+ tempDir?: unknown;
1902
+ iosSimulatorApp?: unknown;
1903
+ androidEmulatorApp?: unknown;
1904
+ optimizations?: unknown;
1905
+ concurrency?: {
1906
+ maxBuilds?: unknown;
1907
+ maxDevices?: unknown;
1908
+ };
1909
+ pool?: {
1910
+ iosParkedMax?: unknown;
1911
+ androidParkedMax?: unknown;
1912
+ };
1913
+ parked?: {
1914
+ ios?: unknown;
1915
+ android?: unknown;
1916
+ };
1917
+ caches?: {
1918
+ buildCache?: unknown;
1919
+ metroCache?: unknown;
1920
+ };
1921
+ hosting?: {
1922
+ agentDriver?: unknown;
1923
+ };
1924
+ debug?: {
1925
+ logs?: unknown;
1926
+ };
1927
+ server?: {
1928
+ buildsPaused?: unknown;
1929
+ devicesPaused?: unknown;
1930
+ acceptClientBuilds?: unknown;
1931
+ lanAddresses?: unknown;
1932
+ maxLoadPerCore?: unknown;
1933
+ workerRoot?: unknown;
1934
+ gradleDaemonIdleMinutes?: unknown;
1935
+ };
1936
+ remote?: {
1937
+ machines?: unknown;
1938
+ easMaxDurationMinutes?: unknown;
1939
+ build?: unknown;
1940
+ buildPoolDisabled?: unknown;
1941
+ devicePoolDisabled?: unknown;
1942
+ };
1943
+ [key: string]: unknown;
1923
1944
  }
1945
+ type Config = StimConfig;
1946
+ //#endregion
1947
+ //#region state/automatic-pools.d.ts
1948
+ type AutomaticPoolRole = "build" | "device";
1949
+ declare function automaticMachineEnabled(role: AutomaticPoolRole, machine: string, config?: StimConfig | null): boolean;
1950
+ declare function requireAutomaticMachine(role: AutomaticPoolRole, machine: string): void;
1924
1951
  /**
1925
- * A linked worktree's `git status`. `changed` counts tracked paths with staged or unstaged changes, including
1926
- * conflicts; `untracked` counts untracked entries as `git status` lists them, so an untracked directory counts once.
1927
- * `ahead` and `behind` compare HEAD with `upstream`, and are null when there is no upstream or it no longer exists.
1928
- * `mergedInto` names the default branch (`origin/main`) when `gc` would consider the branch merged into it, judged
1929
- * from local refs without fetching.
1952
+ * Refuses a pool with no enabled member: local, or a configured remote approved for the role. `canHost` excludes
1953
+ * device-pool remotes that can never host, such as a Linux machine.
1930
1954
  */
1931
- interface WorktreeGit {
1932
- changed: number;
1933
- untracked: number;
1934
- upstream: string | null;
1935
- ahead: number | null;
1936
- behind: number | null;
1937
- mergedInto: string | null;
1938
- }
1955
+ declare function validateAutomaticPool(role: AutomaticPoolRole, config: StimConfig, canHost?: (machine: string) => boolean): void;
1956
+ //#endregion
1957
+ //#region state/config.d.ts
1958
+ declare function withConfigLock<T>(fn: () => T): T;
1959
+ /** Waits for the config lock without blocking the event loop. The signal cancels acquisition; `fn` runs synchronously. */
1960
+ declare function withConfigLockAsync<T>(fn: () => T, signal?: AbortSignal): Promise<T>;
1961
+ declare function configCorruptRepair(path?: string): string;
1962
+ /** The build worker root: `server.workerRoot` when it is absolute, else `$STIM_HOME/build-worker`. */
1963
+ declare function buildWorkerRoot(config?: Config | null): string;
1939
1964
  /**
1940
- * The GitHub pull request of a worktree's branch and HEAD, as `status --watch` last looked it up through `gh`.
1941
- * `state` is `draft` for an open draft. `checks` counts the head commit's check runs and commit statuses, null when it
1942
- * has none. `checkedAt` is when Stim last asked GitHub.
1965
+ * A short directory at the drive root that stands in for the build worker directory `path` on Windows. The NDK's
1966
+ * ninja is not long-path aware (STIM_PATH_TOO_LONG), and it opens paths under the checkout and under the Gradle user
1967
+ * home's transforms cache, both of which are too long under the worker root. A subst drive does not help: Node's
1968
+ * native realpath, which expo-modules-autolinking uses, resolves it back to the long path.
1943
1969
  */
1944
- interface WorktreePullRequest {
1945
- number: number;
1946
- url: string;
1947
- title: string;
1948
- state: "open" | "draft" | "merged" | "closed";
1949
- checks: {
1950
- passing: number;
1951
- failing: number;
1952
- pending: number;
1953
- } | null;
1954
- reviewDecision: "approved" | "changes-requested" | "review-required" | null;
1955
- checkedAt: string;
1970
+ declare function windowsShortDir(path: string, systemDrive?: string): string;
1971
+ /** The ASCII key a Windows build worker records beside a `windowsShortDir` to tell which `kind` and `path` it serves. */
1972
+ declare function windowsShortDirKey(kind: "checkout" | "gradle", path: string): string;
1973
+ declare function loadConfig(): Config | null;
1974
+ declare function getProject(projectPath: string): ProjectRecord | null;
1975
+ declare function getConcurrencyLimits({ env }?: {
1976
+ env?: NodeJS.ProcessEnv;
1977
+ }): ConcurrencyLimits;
1978
+ interface ParkedMax {
1979
+ max: number;
1980
+ error: string | null;
1956
1981
  }
1957
- interface WorktreeFacts {
1958
- path: string;
1959
- branch?: string;
1960
- repository?: string;
1961
- /** Null when git could not be read in time, the worktree is in a folder status does not open, or one-shot status skipped an idle environment or an unprovisioned worktree. */
1962
- git?: WorktreeGit | null;
1963
- /** Null when GitHub has no pull request for the branch; absent when unknown, such as without `gh`. */
1964
- pullRequest?: WorktreePullRequest | null;
1965
- /** Present with `git`: what the worktree's git chip shows, from `gitChip`. */
1966
- gitChip?: GitChip;
1982
+ declare function getParkedMax(platform: "ios" | "android", { config, env }?: {
1983
+ config?: Config | null;
1984
+ env?: NodeJS.ProcessEnv;
1985
+ }): ParkedMax;
1986
+ declare function getProjectSettings(projectPath: string): Record<string, unknown>;
1987
+ declare function getRepoSettings(gitCommonDir: string): Record<string, unknown>;
1988
+ declare function isPathPrefix(prefix: string, path: string): boolean;
1989
+ declare function findEnclosingWorktreeRoot(projectPath: string): string | null;
1990
+ declare function allMetroPorts(): number[];
1991
+ //#endregion
1992
+ //#region state/debug-log.d.ts
1993
+ declare const DEBUG_ENV = "STIM_DEBUG";
1994
+ declare function debugLogDir(): string;
1995
+ /** The `STIM_DEBUG` override (1/true on, 0/false off, anything else off), else the machine setting `debug.logs`. Never throws. */
1996
+ declare function debugLoggingEnabled(env?: NodeJS.ProcessEnv): boolean;
1997
+ interface DebugLog {
1998
+ /** Whether debug logging is on right now; re-read every few seconds so a long-running process follows the setting. */
1999
+ enabled(): boolean;
2000
+ /** Appends one record to `$STIM_HOME/logs/debug/<component>.ndjson` when enabled. Never throws; keys that look like secrets are redacted. */
2001
+ log(event: string, fields?: Record<string, unknown>): void;
1967
2002
  }
1968
- /** Every coding-agent tool whose sessions status can attribute to an environment. */
1969
- declare const AGENT_TOOLS: readonly ["claude-code", "codex"];
1970
- type AgentTool = (typeof AGENT_TOOLS)[number];
1971
2003
  /**
1972
- * A coding-agent session working in an environment: one whose working directory `cwd` is the environment's path,
1973
- * inside it, or its git worktree root, or one that ran a Stim command in it. `title` is the short name the tool keeps
1974
- * for the session, never its conversation. `lastActiveAt` is the tool's own last update, or the Stim command's time
1975
- * when that is newer. `openUrl` opens the session in the tool's desktop app when that app is installed on this machine.
1976
- * `webUrl` opens a Claude Code session with Remote Control connected on claude.ai/code or in the Claude mobile app.
2004
+ * Debug-level records for one component (`cli` or `server`). Off by default; `STIM_DEBUG=1` or `debug.logs` turns it
2005
+ * on. Callers pass names, durations and codes, never arguments, environment values, tokens or tickets.
1977
2006
  */
1978
- interface AgentSession {
1979
- tool: AgentTool;
1980
- sessionId: string;
1981
- title?: string;
1982
- cwd: string;
1983
- startedAt?: string;
1984
- lastActiveAt?: string;
1985
- pid?: number;
1986
- openUrl?: string;
1987
- webUrl?: string;
2007
+ declare function createDebugLog(component: "cli" | "server", { env, now, base }?: {
2008
+ env?: NodeJS.ProcessEnv;
2009
+ now?: () => number;
2010
+ base?: () => Record<string, unknown>;
2011
+ }): DebugLog;
2012
+ //#endregion
2013
+ //#region state/trace.d.ts
2014
+ declare const TRACE_ENV = "STIM_TRACE";
2015
+ /**
2016
+ * `args` with the value of any flag or assignment that looks like a secret, credentials in URLs, token shapes and
2017
+ * the `exact` values replaced. `program` selects the flags that carry a credential without a secret-looking name,
2018
+ * such as `curl -u`.
2019
+ */
2020
+ declare function redactArgv(args: readonly string[], exact?: readonly string[], program?: string): string[];
2021
+ /** A shell command line with the same redactions as `redactArgv`, clipped to a fixed length. */
2022
+ declare function redactCommandLine(command: string, exact?: readonly string[]): string;
2023
+ type TraceArgs = Record<string, unknown>;
2024
+ type EndSpan = (args?: TraceArgs) => void;
2025
+ type Args = TraceArgs;
2026
+ interface TracePhases {
2027
+ /** Ends the phase in progress and starts `name`; entering the phase already in progress does nothing. */
2028
+ enter(name: string, args?: Args): void;
2029
+ end(args?: Args): void;
2030
+ }
2031
+ interface Trace {
2032
+ enabled(): boolean;
2033
+ /** Starts a span; the returned function ends it, with optional extra args. */
2034
+ begin(name: string, args?: TraceArgs): EndSpan;
2035
+ /** `begin` for the outermost span of a process: it ends at exit with the exit code. */
2036
+ command(name: string, args?: TraceArgs): void;
2037
+ /** `begin` for a child process: same event with the `exec` category. */
2038
+ beginExec(name: string, args?: TraceArgs): EndSpan;
2039
+ /** Runs `fn` inside a span and ends it when `fn` returns or throws. */
2040
+ span<T>(name: string, fn: () => T, args?: TraceArgs): T;
2041
+ /** `span` for an async `fn`: ends when its promise settles. */
2042
+ spanAsync<T>(name: string, fn: () => Promise<T>, args?: TraceArgs): Promise<T>;
2043
+ mark(name: string, args?: TraceArgs): void;
2044
+ /** A strip of consecutive, non-overlapping phases on its own track. */
2045
+ phases(track: string): TracePhases;
1988
2046
  }
1989
2047
  /**
1990
- * An agent session that stopped running in an environment, with the details Stim last found while it ran. `endedAt`
1991
- * is the last time a status watcher found it running.
2048
+ * Timing spans for one stim process tree, written as Chrome trace events (open the file in Perfetto or
2049
+ * chrome://tracing) when `STIM_TRACE=<path>` is set. Unset, every call is one environment check. A stim command
2050
+ * (`trace.command`) truncates the file and writes `[`; every other process that inherits the variable appends one
2051
+ * event per line, creating the file if it is missing, so the file has no closing bracket. Child process arguments are written through `redactArgv`.
1992
2052
  */
1993
- interface EndedAgentSession extends Omit<AgentSession, "pid"> {
1994
- endedAt: string;
1995
- }
1996
- interface AndroidRuntimeFacts {
1997
- serial: string | null;
1998
- state: "detected" | "not-detected" | "missing" | "unknown";
1999
- error?: string;
2000
- }
2001
- /** Every connection state a physical device can report. */
2002
- declare const PHYSICAL_DEVICE_CONNECTIONS: readonly ["connected", "disconnected", "unknown"];
2003
- type PhysicalDeviceConnection = (typeof PHYSICAL_DEVICE_CONNECTIONS)[number];
2053
+ declare const trace: Trace;
2054
+ //#endregion
2055
+ //#region state/home-identity.d.ts
2056
+ /** The persistent, nonsecret identity of this Stim home; null until the CLI initializes it. */
2057
+ declare function readHomeIdentity(config?: Config | null): string | null;
2058
+ //#endregion
2059
+ //#region state/json-file.d.ts
2004
2060
  /**
2005
- * A physical iPhone, iPad or Android phone the workspace holds an unexpired lease on, from `ios --device`,
2006
- * `android --device` or `device lock`. Stim uses it and never owns it, so `owned` is always false. `id` is the
2007
- * UDID or adb serial. `name` is the device's own name, falling back to the name the lease recorded; `model` is the
2008
- * marketing name devicectl reports, null when it could not be read, or the Android model the lease recorded.
2009
- * `connection` is `connected` when devicectl can reach the phone or adb lists it as `device`, `disconnected` when
2010
- * the tool answered without it, and `unknown` when the tool could not be read in time. `lease.holder` is the
2011
- * workspace path that holds it.
2061
+ * Runs `fn` with each state file read from disk at most once: inside it, including after an `await`, `readStateFile`
2062
+ * returns the text of the first read of that path, and a path found missing stays missing. Callers still parse every time, so no two
2063
+ * share a mutable object. Only for computations that write none of the files they read.
2012
2064
  */
2013
- interface PhysicalDeviceState {
2014
- platform: StatsPlatform;
2015
- slot: string;
2016
- id: string;
2017
- name: string | null;
2018
- model: string | null;
2019
- owned: false;
2020
- physical: true;
2021
- connection: PhysicalDeviceConnection;
2022
- lease: {
2023
- holder: string;
2024
- kind: "declared" | "run";
2025
- grantedAt: string | null;
2026
- expiresAt: string;
2027
- };
2065
+ declare function withStateReadCache<T>(fn: () => T): T;
2066
+ declare function readStateFile(path: string): string;
2067
+ declare function readJsonFile(path: string): unknown;
2068
+ declare function isJsonObject(value: unknown): value is Record<string, unknown>;
2069
+ declare function readJsonObject(path: string): Record<string, unknown> | null;
2070
+ //#endregion
2071
+ //#region state/ledgers.d.ts
2072
+ type CreatedDevicePlatform = "ios" | "android" | "web";
2073
+ /** `web` lists the absolute paths of browser profile directories Stim created. */
2074
+ interface CreatedDevices {
2075
+ ios: ReadonlySet<string>;
2076
+ android: ReadonlySet<string>;
2077
+ web: ReadonlySet<string>;
2028
2078
  }
2029
- interface RemoteDeviceState {
2030
- platform: "ios" | "android" | null;
2031
- backend: "eas";
2079
+ declare function readCreatedDevices(): CreatedDevices;
2080
+ interface EasSessionClaim {
2032
2081
  sessionId: string;
2033
- state: "claimed" | "unclaimed" | "unknown";
2034
- startedAt: string | null;
2035
- webPreviewUrl: string | null;
2082
+ name: string;
2083
+ platform: "ios" | "android";
2084
+ workspaceRoot: string;
2085
+ workspaceHome: string;
2086
+ stateFile: string;
2036
2087
  }
2037
- /**
2038
- * One part of a worktree's git chip, in display order: commits ahead of and behind the upstream, uncommitted files
2039
- * (changed plus untracked), the base branch the branch is merged into (left out when its pull request is merged,
2040
- * whose own mark says so), and a branch with no upstream that is not merged.
2041
- */
2042
- type GitChipPart = {
2043
- kind: "arrows";
2044
- ahead: number;
2045
- behind: number;
2046
- } | {
2047
- kind: "changed";
2048
- count: number;
2049
- } | {
2050
- kind: "merged";
2051
- into: string;
2052
- } | {
2053
- kind: "no-upstream";
2054
- };
2055
- /** A pull request's checks at a glance: any failing, else any pending, else any passing; null without checks. */
2056
- type ChecksState = "passing" | "failing" | "pending" | null;
2057
- interface GitChip {
2058
- parts: GitChipPart[];
2059
- ci: ChecksState;
2088
+ interface EasSessionLedgerRead {
2089
+ claims: Map<string, EasSessionClaim>;
2090
+ notice: string | null;
2091
+ safe: boolean;
2060
2092
  }
2093
+ /** Returns the claim normalized to canonical paths, or null when it is not a valid claim for `id`. */
2094
+ declare function validEasSessionClaim(id: string, value: unknown): EasSessionClaim | null;
2095
+ declare function readEasSessionLedger(root?: string): EasSessionLedgerRead;
2096
+ //#endregion
2097
+ //#region state/error-entries.d.ts
2061
2098
  /**
2062
- * Whether a running owned simulator or emulator lacks the workspace's app: `none` when its platform never built
2063
- * successfully here and the latest build failed, `closed` when status saw no app process, else null.
2064
- */
2065
- type AppPresence = "none" | "closed" | null;
2066
- declare const WORKSPACE_STAGE_KINDS: readonly ["building", "warming", "ready", "build-failed", "running", "stopped"];
2067
- /**
2068
- * Where a workspace is, first match wins: `building` while a build runs, `warming` and `ready` from `phase` when
2069
- * nothing is live, `build-failed` when the newest run of either platform failed, `running` when it is live, its
2070
- * macOS app is running or orphaned, or it holds a remote device, else `stopped`. `since` is when that began: the
2071
- * build's start, the warm phase's time, the failed run's end (else start), the supervisor's start (else the running
2072
- * macOS app's build end, else its start), or Metro's last stop. `platform` names the build for `building`
2073
- * and `build-failed`. `closedApps` lists, for `running`, the devices whose `appPresence` is `closed`.
2099
+ * The number of rows a log list shows for `records`, an error query's result in log order. Stim Desktop's
2100
+ * `LogEntryList` and the phone's `groupRecords` group the same way and replay the same vectors: in Expo's dev
2101
+ * server one failed bundle is a `Bundling failed` marker line, the error line and a failed bundle response,
2102
+ * which the apps list as one row, and every other record is its own row.
2074
2103
  */
2075
- interface WorkspaceStage {
2076
- kind: (typeof WORKSPACE_STAGE_KINDS)[number];
2077
- since: string | null;
2078
- platform: StatsPlatform | "macos" | null;
2079
- closedApps: {
2080
- platform: StatsPlatform;
2081
- slot: string;
2082
- }[];
2104
+ declare function countErrorEntries(records: readonly NdjsonRecord[]): number;
2105
+ //#endregion
2106
+ //#region state/fingerprint-memo.d.ts
2107
+ declare const FINGERPRINT_MEMO_SCHEMA = 1;
2108
+ /** Full native fingerprints shared by every workspace of this Stim home, one file per platform and static digest. */
2109
+ declare function fingerprintMemoDir(platform?: string): string;
2110
+ declare function fingerprintMemoFile(platform: string, digest: string): string;
2111
+ declare function fingerprintMemoLock(): string;
2112
+ /** The full fingerprint a tree with this static digest produced, written by the CLI. */
2113
+ interface FingerprintMemoRecord {
2114
+ schema: typeof FINGERPRINT_MEMO_SCHEMA;
2115
+ platform: string;
2116
+ digest: string;
2117
+ recordedAt: number;
2118
+ hash: string;
2119
+ sources: unknown[];
2120
+ /** The packages the static check counted as native, to name the ones a disagreeing check found it missed. */
2121
+ nativePackages: string[];
2083
2122
  }
2084
- /** Every code a status issue can carry. */
2085
- declare const STATUS_ISSUE_CODES: readonly ["port-not-ours", "sim-missing", "sim-without-metro", "avd-serial-changed", "avd-missing", "avd-not-detected", "avd-unchecked", "android-reverse-missing", "supervisor-unverified", "browser-unverified", "browser-orphaned"];
2086
- type StatusIssueCode = (typeof STATUS_ISSUE_CODES)[number];
2087
- /**
2088
- * One thing in a workspace that needs the user, or with severity `info` a note that needs nothing. `remedy` is a
2089
- * command to run from `workspace`; `slot` is absent for the default device slot. `warnings` carries the `error` and
2090
- * `warning` issues as text.
2091
- *
2092
- * Severity:
2093
- * - `error`: Stim cannot verify or safely act on something; a person resolves it, following the teardown guide.
2094
- * - `warning`: something is broken now and needs someone to act, such as an app that cannot reach Metro, a held
2095
- * port or a device that vanished while expected.
2096
- * - `info`: a fact the next normal Stim command handles by itself, so it needs no action.
2097
- *
2098
- * The phone app and Stim Desktop show `error` and `warning` and hide `info`; plain `stim status` prints `info` as a
2099
- * dim note with no remedy.
2100
- */
2101
- interface StatusIssue {
2102
- code: StatusIssueCode;
2103
- severity: "error" | "warning" | "info";
2104
- message: string;
2105
- remedy: string;
2106
- workspace: string;
2107
- slot?: string;
2123
+ declare function readFingerprintMemo(platform: string, digest: string): FingerprintMemoRecord | null;
2124
+ //#endregion
2125
+ //#region state/logs-query.d.ts
2126
+ interface SinceResult {
2127
+ ms?: number;
2128
+ error?: string;
2108
2129
  }
2109
- /** Every state the owned page's latest load can report. */
2110
- declare const WEB_PAGE_STATES: readonly ["loading", "loaded", "failed"];
2111
- /**
2112
- * The owned page's latest load, from the page-load marker in the workspace's web log: `url` is the document it
2113
- * loaded, `state` is `failed` when that document did not load or the page crashed, with the log message as
2114
- * `error`. `route`, present only when an in-app route change (history API or fragment) moved the page off `url`
2115
- * since the load, is the URL it shows now.
2116
- */
2117
- interface WebPageState {
2118
- url: string;
2119
- state: (typeof WEB_PAGE_STATES)[number];
2130
+ interface GrepResult {
2131
+ re?: RegExp;
2120
2132
  error?: string;
2121
- route?: string;
2122
2133
  }
2123
- /**
2124
- * The workspace's Stim-owned Chrome. `pid` is Chrome's process and `supervisorPid` the Stim process that holds
2125
- * its DevTools session. `cdpEndpoint` is the loopback DevTools HTTP endpoint agents can attach to, and `targetId`
2126
- * the DevTools target of the owned page; both are null when the browser is not running. `profile` is the
2127
- * Stim-owned user data directory. `page` is null before the page's first load. `activity` names DevTools clients
2128
- * other than Stim's as drivers.
2129
- */
2130
- interface WebBrowserState {
2131
- browser: "chrome";
2132
- version: string | null;
2133
- running: boolean;
2134
- pid: number | null;
2135
- supervisorPid: number | null;
2136
- url: string;
2137
- headless: boolean;
2138
- viewport: WebViewport;
2139
- profile: string;
2140
- cdpEndpoint: string | null;
2141
- targetId: string | null;
2142
- page?: WebPageState | null;
2143
- activity?: DeviceActivity;
2134
+ interface MarkerWindow {
2135
+ launchTs: number | null;
2136
+ bundleTs: number | null;
2137
+ pageLoadTs: number | null;
2144
2138
  }
2145
- /** The steps of `stim worktree warm`: `refresh` fast-forwards and installs in the source checkout, `copy` carries ignored entries. */
2146
- type WarmStep = "refresh" | "copy";
2147
- /**
2148
- * Where a workspace is in its lifecycle: `warming` while `stim worktree warm` runs in it, `ready` after a warm
2149
- * succeeded and before any `start`, `ios`, `android`, `web` or `reload` there, for at most two hours; `live` when
2150
- * anything it owns runs, else `idle`.
2151
- */
2152
- declare const WORKSPACE_PHASES: readonly ["warming", "ready", "live", "idle"];
2153
- type WorkspacePhase = (typeof WORKSPACE_PHASES)[number];
2154
- /** A folder's size on disk as `status --watch` last measured it. */
2155
- interface DiskMeasure {
2156
- bytes: number;
2157
- measuredAt: string;
2139
+ interface QueryCriteria {
2140
+ slot?: string;
2141
+ includeAppDeviceErrors?: boolean;
2142
+ sources?: string[];
2143
+ minLevel?: string;
2144
+ grep?: RegExp;
2145
+ sinceTs?: number;
2146
+ errorsOnly?: boolean;
2147
+ markerTs?: number;
2148
+ bundleMarkerTs?: number;
2158
2149
  }
2150
+ interface TailState {
2151
+ offset: number;
2152
+ partial: string;
2153
+ }
2154
+ declare function parseSince(text: unknown): SinceResult;
2155
+ declare function compileGrep(pattern: unknown): GrepResult;
2156
+ declare const ERROR_SOURCES: string[];
2157
+ declare function markerWindow(records: NdjsonRecord[]): MarkerWindow;
2159
2158
  /**
2160
- * An environment's disk use as `status --watch` last measured it. `worktreeBytes` is the git worktree folder, or the
2161
- * workspace folder outside one, node_modules included. `nodeModulesBytes` is node_modules at the worktree root and,
2162
- * when different, at the workspace path, and is part of `worktreeBytes`. `buildBytes` is Stim's own folder for the
2163
- * workspace: Xcode derived data, Gradle outputs and logs. A figure is null until it was measured; `measuredAt` is the
2164
- * oldest measurement.
2159
+ * The platform a bundle marker speaks for, lowercased: its `platform` field, or the tag @expo/cli prints before
2160
+ * "Bundled" ("iOS", "Android", "Web", "RSC(iOS)" as ios, or an environment tag such as "DOM"). Null when unknown.
2165
2161
  */
2166
- interface EnvironmentDisk {
2167
- worktreeBytes: number | null;
2168
- nodeModulesBytes: number | null;
2169
- buildBytes: number | null;
2170
- measuredAt: string;
2171
- }
2162
+ declare function bundleMarkerPlatform(record: NdjsonRecord): string | null;
2163
+ declare function recordMatches(record: NdjsonRecord | null | undefined, criteria?: QueryCriteria): boolean;
2164
+ declare function buildCriteria({ slot, sources, minLevel, since, grep, errorsOnly, markerTs, bundleMarkerTs, now }?: {
2165
+ slot?: string;
2166
+ sources?: string[];
2167
+ minLevel?: string;
2168
+ since?: string | null;
2169
+ grep?: string | RegExp | null;
2170
+ errorsOnly?: boolean;
2171
+ markerTs?: number;
2172
+ bundleMarkerTs?: number;
2173
+ now?: number;
2174
+ }): QueryCriteria;
2175
+ declare function logFiles(dir: string): string[];
2176
+ declare function fileSizes(dir: string): Record<string, number>;
2177
+ declare function readLogRecords(dir: string, include?: (name: string) => boolean): NdjsonRecord[];
2178
+ declare const NATIVE_CRASH_LOG_PREFIX = "native-crash-";
2179
+ /** Reads the CLI timeline with retained native crash records after other records at the same timestamp. */
2180
+ declare function readLogTimeline(dir: string, records?: NdjsonRecord[]): NdjsonRecord[];
2181
+ declare function isExpoErrorContext(record: NdjsonRecord, event: unknown): boolean;
2182
+ type ExpoContextTarget = "msg" | "field";
2172
2183
  /**
2173
- * A Metro bundle request: `bundling` while one is in flight on the workspace's Metro, with its `platform`,
2174
- * `startedAt` and, when Metro reported progress for it, `percent` (0-100). `last` is the newest finished request,
2175
- * `durationMs` from request to response end.
2184
+ * Adds the code frame and stack lines Expo printed after each Expo error record, either joined into
2185
+ * `msg` or as a `context` string array.
2176
2186
  */
2177
- interface MetroBundleState {
2178
- bundling: boolean;
2179
- platform?: StatsPlatform;
2180
- startedAt?: string;
2181
- percent?: number;
2182
- last?: {
2183
- platform: StatsPlatform;
2184
- status: "ok" | "failed";
2185
- durationMs: number;
2186
- finishedAt: string;
2187
- };
2188
- }
2189
- interface SimulatorState {
2190
- devicePlacement?: DevicePlacement;
2191
- host?: HostedIosStatus;
2192
- name: string | null;
2193
- udid: string;
2194
- owned: boolean;
2195
- state: string;
2196
- activity?: DeviceActivity;
2197
- app?: DeviceAppProcess;
2198
- appPresence?: AppPresence;
2199
- /** The simulator's data folder, for an owned simulator once measured. */
2200
- disk?: DiskMeasure;
2201
- /** Present while the device is not booted after idle shutdown or queue reclaim. */
2202
- idleShutdown?: DeviceIdleShutdownRecord;
2203
- }
2204
- interface AndroidDeviceState {
2205
- devicePlacement?: DevicePlacement;
2206
- host?: HostedAndroidStatus;
2207
- name: string | undefined;
2208
- owned: boolean;
2209
- physical: boolean;
2210
- serial?: string | null;
2211
- state?: AndroidRuntimeFacts["state"] | "ready" | "stopped" | "unverified";
2212
- deviceProfile?: string | null;
2213
- activity?: DeviceActivity;
2214
- app?: DeviceAppProcess;
2215
- appPresence?: AppPresence;
2216
- /** The AVD's folder, for an owned emulator once measured. */
2217
- disk?: DiskMeasure;
2218
- /** Present while the emulator is not running after idle shutdown or queue reclaim. */
2219
- idleShutdown?: DeviceIdleShutdownRecord;
2187
+ declare function attachExpoErrorContext(all: NdjsonRecord[], matched: NdjsonRecord[], into?: ExpoContextTarget): NdjsonRecord[];
2188
+ interface LogQueryOptions {
2189
+ slot?: string;
2190
+ sources?: string[];
2191
+ minLevel?: string;
2192
+ since?: string;
2193
+ grep?: string | RegExp;
2194
+ errorsOnly?: boolean;
2195
+ now?: number;
2220
2196
  }
2221
- /** The tutorial format version the installed Stim writes into the tutorial app. */
2222
- declare const TUTORIAL_VERSION = 2;
2223
- interface EnvironmentState {
2224
- slots?: {
2225
- slot: string;
2226
- ios: SimulatorState | null | undefined;
2227
- android: AndroidDeviceState | null | undefined;
2228
- }[];
2229
- path: string;
2230
- /** The 16-hex identity of the registered canonical project path, used by shared workspace links. */
2231
- workspaceId?: string;
2232
- /** Statically detected app platforms; always set by status, empty when none are detected. */
2233
- platforms?: string[];
2234
- /** Present when the app's app.json sets expo.extra.stimTutorial to a positive integer: that version. */
2235
- tutorial?: {
2236
- version: number;
2237
- };
2238
- /** When `stim doctor` last ran in this workspace, per platform; absent until it has run. */
2239
- doctorRuns?: Partial<Record<"ios" | "android", {
2240
- at: string;
2241
- }>>;
2242
- live: boolean;
2243
- phase?: WorkspacePhase;
2244
- /** When the warm started (`warming`) or finished (`ready`); null for `live` and `idle`. */
2245
- phaseSince?: string | null;
2246
- /** Whether stim-server may record this workspace's device screens, from `recording.enabled`. */
2247
- recording?: {
2248
- enabled: boolean;
2249
- };
2250
- /** Present when `workspace.hidden` is true: clients list the workspace under Hidden. */
2251
- hidden?: true;
2252
- /** Where the workspace is, from `workspaceStage`; `stim status --json` always sets it. */
2253
- stage?: WorkspaceStage;
2254
- /** The step a `warming` workspace's warm is in; absent in every other phase. */
2255
- warmStep?: WarmStep;
2197
+ interface LogQueryPlan {
2198
+ /** Whether a record belongs to the queried slot's timeline, before any other filter. */
2199
+ inTimeline: (record: NdjsonRecord) => boolean;
2200
+ matches: (record: NdjsonRecord) => boolean;
2201
+ launchTs: number | null;
2202
+ sinceTs: number | undefined;
2256
2203
  /**
2257
- * The memory this workspace's processes use, as `memorySource` says: the sum of its `machine` owners' `memoryMb`
2258
- * when status read their footprints, or else a fixed estimate per booted simulator, detected emulator, running
2259
- * Metro and running Chrome. `capacity.committedMb` sums it.
2204
+ * Whether no record with a timestamp at most `maxTs`, or without one when `maxTs` is Infinity, can match.
2205
+ * `slots` lists every `slot ?? 'default'` value the records can carry; without it, a query that depends on
2206
+ * per-slot launch markers cannot rule out any timestamp.
2260
2207
  */
2261
- memoryMb: number;
2262
- /** How `memoryMb` was obtained; absent from payloads written before it existed, which carry the estimate. */
2263
- memorySource?: Exclude<MemorySource, "rss">;
2264
- warnings: string[];
2265
- issues: StatusIssue[];
2266
- ios?: SimulatorState | null;
2267
- android?: AndroidDeviceState | null;
2268
- metro?: {
2269
- port: number;
2270
- running: boolean;
2271
- pid: number | null;
2272
- tunnel?: {
2273
- provider: Exclude<TunnelMode, "auto" | "off" | "expo">;
2274
- url: string;
2275
- };
2276
- idleStop?: IdleStopRecord;
2277
- lastStop?: MetroLastStop;
2278
- /** The other process that answers Metro on `port`; `cwd` is null when its directory could not be read. */
2279
- heldBy?: {
2280
- pid: number;
2281
- cwd: string | null;
2282
- };
2283
- /** Absent when the Metro log holds no bundle request. */
2284
- bundle?: MetroBundleState;
2285
- } | null;
2286
- web?: WebBrowserState | null;
2287
- macos?: MacosAppState | null;
2288
- supervisor?: {
2289
- pid: number | null;
2290
- mode: string | null;
2291
- startedAt: string | null;
2292
- healthy: boolean;
2293
- } | null;
2294
- logs?: {
2295
- dir: string;
2296
- errorsSinceMarker: number;
2297
- } | null;
2298
- /** `installed` is whether the agent-device executable is on the PATH of the process that ran status. */
2299
- agentDevice?: {
2300
- stateDir: string;
2301
- installed?: boolean;
2302
- };
2303
- worktree?: WorktreeFacts | null;
2304
- remoteDevices?: RemoteDeviceState[];
2305
- /** The physical devices this workspace leases, in every slot; absent when it leases none. */
2306
- physicalDevices?: PhysicalDeviceState[];
2307
- build?: BuildReport | null;
2308
- lastBuilds?: Partial<Record<StatsPlatform, LastBuildReport>>;
2309
- /** Each platform's recent runs, newest first, at most `BUILD_HISTORY_LIMIT` each. */
2310
- builds?: Partial<Record<BuildPlatform, BuildHistoryEntry[]>>;
2311
- disk?: EnvironmentDisk;
2312
- /** The coding-agent sessions working in the environment, most recently active first; absent when none. */
2313
- agents?: AgentSession[];
2314
- /** The agent sessions that stopped running here in the last 3 days, most recently ended first; absent when none. */
2315
- endedAgents?: EndedAgentSession[];
2208
+ excludesUpTo: (maxTs: number, slots?: ReadonlySet<unknown>) => boolean;
2316
2209
  }
2317
- /** `committedMb` sums the environments' `memoryMb`; `overCapacity` is that sum over 60% of `totalMemoryMb`. */
2318
- interface StatusCapacity {
2319
- liveCount: number;
2320
- committedMb: number;
2321
- totalMemoryMb: number;
2322
- overCapacity: boolean;
2210
+ /** Decides which records a query matches from the timeline's marker records alone. */
2211
+ declare function planLogQuery(markers: readonly NdjsonRecord[], { slot, sources, minLevel, since, grep, errorsOnly, now }?: LogQueryOptions): LogQueryPlan;
2212
+ declare function queryLogs({ slot, dir, records, sources, minLevel, since, grep, tail, errorsOnly, errorContext, now }?: LogQueryOptions & {
2213
+ dir?: string;
2214
+ records?: NdjsonRecord[];
2215
+ tail?: number;
2216
+ errorContext?: boolean;
2217
+ }): NdjsonRecord[];
2218
+ /** Queries the JSON timeline, including Expo context fields on errors as `stim logs --json` does. */
2219
+ declare function queryJsonLogs(options?: Parameters<typeof queryLogs>[0]): NdjsonRecord[];
2220
+ declare function tailRead(prev: TailState | null | undefined, size: number): {
2221
+ start: number;
2222
+ prev: TailState;
2223
+ };
2224
+ declare function advanceTail(prev: TailState | null | undefined, chunk: string | null | undefined, size: number): {
2225
+ state: TailState;
2226
+ records: NdjsonRecord[];
2227
+ };
2228
+ declare function followLogs({ dir, onRecord, criteria, intervalMs, offsets, identities: seededIdentities, errorContext }: {
2229
+ dir: string;
2230
+ onRecord: (record: NdjsonRecord) => void;
2231
+ criteria?: QueryCriteria;
2232
+ /** An explicit polling interval. Omit for filesystem hints with a ten-second repair scan. */
2233
+ intervalMs?: number;
2234
+ offsets?: Record<string, number> | null;
2235
+ /** `fileIdentity` of each file `offsets` was read from, so a replacement made since is recognized. */
2236
+ identities?: Record<string, string> | null;
2237
+ /** Holds each Expo error until its code frame and stack stop growing, then attaches them. */
2238
+ errorContext?: ExpoContextTarget;
2239
+ }): () => void;
2240
+ declare function fileIdentity(stat: BigIntStats): string | undefined;
2241
+ declare function sortByTs(records: NdjsonRecord[]): NdjsonRecord[];
2242
+ //#endregion
2243
+ //#region state/log-reader.d.ts
2244
+ interface LogDirQuery extends LogQueryOptions {
2245
+ tail?: number;
2246
+ /** Records from outside the directory, such as agent-device actions; they follow its records at equal timestamps. */
2247
+ extra?: readonly NdjsonRecord[];
2323
2248
  }
2324
- interface DeviceLeaseState {
2325
- slot?: string;
2326
- path: string;
2327
- platform: string;
2328
- id: string | null;
2329
- deviceName: string | null;
2330
- holder: string | null;
2331
- grantedAt: string | null;
2332
- expiresAt: string | null;
2333
- mine: boolean;
2334
- expired: boolean;
2335
- parsed: boolean;
2249
+ /**
2250
+ * An indexed view of a workspace log directory, read in timestamp order without loading whole files. Each file
2251
+ * generation is indexed once per process (block timestamp bounds, markers, slots and launch attempts) and later
2252
+ * views read only what was appended since. Close it to release the open files.
2253
+ */
2254
+ interface LogDir {
2255
+ /** Bytes read from each current `.ndjson` file, through its last complete line. */
2256
+ readonly sizes: Record<string, number>;
2257
+ /** `fileIdentity` of each current `.ndjson` file that `sizes` was read from. */
2258
+ readonly identities: Record<string, string>;
2259
+ /** The oldest timestamp in the directory. */
2260
+ readonly firstTs: number | undefined;
2261
+ /** Launch attempt records outside native crash files, in timeline order. */
2262
+ launchAttempts(): NdjsonRecord[];
2263
+ /** The records `queryJsonLogs` returns for this directory's timeline followed by `extra`. */
2264
+ query(query?: LogDirQuery): Promise<NdjsonRecord[]>;
2265
+ /** Every matching record in timestamp order, in batches of at most 500, with the same filters and context as query. */
2266
+ batches(query?: Omit<LogDirQuery, "tail">): AsyncIterable<NdjsonRecord[]>;
2267
+ /** Prepares the directory marker predicate for records without markers, such as attributed agent actions. */
2268
+ filter(query?: LogQueryOptions): (record: NdjsonRecord) => boolean;
2269
+ close(): Promise<void>;
2336
2270
  }
2337
2271
  /**
2338
- * Where a memory figure comes from. `footprint` is each process's physical footprint, which Activity Monitor's Memory
2339
- * column shows. `rss` sums resident set sizes, which count pages shared between processes once per process, so a
2340
- * simulator reports well above its footprint; the machine owners fall back to it when the footprint helper cannot be
2341
- * built. `estimate` is a fixed amount per running thing, which an environment reports whenever it has no footprint.
2272
+ * Opens `dir` for reading. With `order: 'timeline'`, native crash files follow every other file at equal
2273
+ * timestamps, as `readLogTimeline` orders them; with `'name'`, every file is ranked by name, as `readLogRecords`.
2342
2274
  */
2343
- declare const MEMORY_SOURCES: readonly ["footprint", "rss", "estimate"];
2344
- type MemorySource = (typeof MEMORY_SOURCES)[number];
2345
- /** Every kind of process owner the status machine section reports. */
2346
- declare const MACHINE_OWNER_KINDS: readonly ["simulator", "emulator", "metro", "build", "browser", "macos", "server", "shared"];
2347
- type MachineOwnerKind = (typeof MACHINE_OWNER_KINDS)[number];
2275
+ declare function openLogDir(dir: string, { order, signal }?: {
2276
+ order?: "timeline" | "name";
2277
+ signal?: AbortSignal;
2278
+ }): Promise<LogDir>;
2279
+ //#endregion
2280
+ //#region state/ndjson-tail.d.ts
2348
2281
  /**
2349
- * One thing using this machine's CPU and memory now. Each process is counted in exactly one owner: the one whose root
2350
- * process is its nearest ancestor. `workspace` and `slot` (absent for the default slot) name the workspace a
2351
- * simulator, emulator, Metro, build or Chrome belongs to; a device no workspace records has a null workspace, and
2352
- * machine-wide processes are `shared`. `id` is the simulator's UDID, the emulator's AVD name, Metro's port as text,
2353
- * the build's platform, or null. `owned` is true only for what Stim started and stops: a workspace's owned device,
2354
- * Metro, build or Chrome. `cpuPercent` is `ps` %CPU summed over the owner's processes, where 100 is one core.
2355
- * `residentMb` sums their resident set sizes; pages shared between processes count once per process, so a
2356
- * simulator, whose processes all map the runtime's shared libraries, reports well above its physical footprint.
2357
- * `memoryMb` sums their physical footprints when `MachineUsageState.memorySource` is `footprint`, taking the resident
2358
- * size of a process the footprint helper could not read, and equals `residentMb` when it is `rss`.
2282
+ * Returns a reader of `file` and its rotated generation that yields, on every call, the records a full
2283
+ * `readNdjsonGenerations(file)` would, while reading only the bytes appended since the previous call. A file renamed
2284
+ * to its rotated generation between two calls is finished from there; any other replacement or a shrunk file is
2285
+ * read again from its start. A file rewritten in place to at least its previous length is not detected.
2359
2286
  */
2360
- interface MachineOwner {
2361
- kind: MachineOwnerKind;
2362
- name: string;
2363
- workspace: string | null;
2364
- slot?: string;
2365
- id: string | null;
2366
- owned: boolean;
2367
- cpuPercent: number;
2368
- residentMb: number;
2369
- memoryMb: number;
2370
- processes: number;
2371
- }
2372
- /** Where this machine's CPU and memory go, from one pass over the host process table. */
2373
- interface MachineUsageState {
2374
- memorySource: Exclude<MemorySource, "estimate">;
2375
- owners: MachineOwner[];
2287
+ declare function createNdjsonTail(file: string): () => NdjsonRecord[];
2288
+ //#endregion
2289
+ //#region state/paths.d.ts
2290
+ declare function getConfigPath(): string;
2291
+ declare function configLockPath(): string;
2292
+ declare function workspaceMetadataFile(projectRoot: string): string;
2293
+ declare function workspaceAgentDeviceDir(projectRoot: string): string;
2294
+ declare function workspaceLogsDir(projectRoot: string): string;
2295
+ /** Device screen footage stim-server records for the workspace, one directory per platform and slot. */
2296
+ declare function workspaceRecordingsDir(projectRoot: string): string;
2297
+ declare function workspaceLogErrorIndex(projectRoot: string): string;
2298
+ declare function workspaceDerivedData(projectRoot: string): string;
2299
+ declare function workspaceGradleBuild(projectRoot: string): string;
2300
+ declare function workspaceBuildDetailFile(projectRoot: string): string;
2301
+ /** The agent sessions that stopped running in the workspace in the last `ENDED_AGENT_RETENTION_MS`. */
2302
+ declare function workspaceEndedAgentsFile(projectRoot: string): string;
2303
+ declare function workspaceEndedAgentsLock(projectRoot: string): string;
2304
+ declare function supervisorPidFile(projectRoot: string): string;
2305
+ declare function workspaceStateFile(projectRoot: string): string;
2306
+ /** The dependency directories `gc --delete` deleted from the workspace's worktree, apart from state.json so writing it is not workspace activity. */
2307
+ declare function workspaceLightenedFile(projectRoot: string): string;
2308
+ declare function workspaceStateLock(projectRoot: string): string;
2309
+ declare function supervisorLogFile(projectRoot: string): string;
2310
+ declare function emulatorLogFile(projectRoot: string): string;
2311
+ declare function sharedCompilationCache(): string;
2312
+ declare function sharedCcache(): string;
2313
+ declare function sharedGradle(): string;
2314
+ declare function sharedPods(): string;
2315
+ declare function createdDevicesFile(): string;
2316
+ declare function gitMergeCacheDir(): string;
2317
+ declare function pullRequestCacheDir(): string;
2318
+ declare function agentSessionsCacheFile(): string;
2319
+ declare function diskUsageCacheDir(): string;
2320
+ /** The ownership claim root that admits one disk-usage walk at a time on this machine. */
2321
+ declare function diskUsageWalkClaims(): string;
2322
+ /** The remote machines this machine asked for build access, each with its pinned stim-server key once approved. */
2323
+ declare function buildMachinesFile(): string;
2324
+ declare function buildMachinesLock(): string;
2325
+ /** The remote machines this machine asked for access, separate from build credentials. */
2326
+ declare function deviceHostMachinesFile(): string;
2327
+ declare function deviceHostMachinesLock(): string;
2328
+ declare function deviceHostMachinesClaims(): string;
2329
+ declare function createdDevicesLock(): string;
2330
+ /** The EAS session ledger lives under the real home directory, never under STIM_HOME. */
2331
+ declare function easMachineStateRoot(): string;
2332
+ declare function easSessionLedgerFile(root?: string): string;
2333
+ declare function easSessionLedgerLock(root?: string): string;
2334
+ /** Where stim-server records, one file per server process, the devices whose frames a client subscribes to. */
2335
+ declare function deviceViewersDir(): string;
2336
+ declare function budgetSweepFile(): string;
2337
+ declare function budgetSweepLock(): string;
2338
+ declare function statsFile(): string;
2339
+ declare function maintenanceDir(): string;
2340
+ declare function maintenanceStateFile(): string;
2341
+ declare function maintenanceAttemptFile(): string;
2342
+ declare function maintenanceNdjsonFile(): string;
2343
+ declare function gcLogDir(): string;
2344
+ declare function gcLogFile(): string;
2345
+ declare function buildLedgerDir(): string;
2346
+ declare function buildLedgerFile(): string;
2347
+ declare function buildLedgerLock(): string;
2348
+ declare function maintenanceChildLogFile(): string;
2349
+ declare function maintenanceRunClaims(): string;
2350
+ /** Dependency trees `worktree remove` moved out of a worktree before git removed it; gc deletes what is left. */
2351
+ declare function trashRoot(): string;
2352
+ declare function archiveRoot(): string;
2353
+ declare function archiveDir(id: string): string;
2354
+ declare function archiveLock(): string;
2355
+ /** Uncommitted changes of removed worktrees: `<repository>/<branch-or-dir>-<timestamp>/` holding `manifest.json`. */
2356
+ declare function worktreeArchiveRoot(): string;
2357
+ /** This machine's own long-term X25519 key as a client of other machines' stim-servers. */
2358
+ declare function clientIdentityKeyFile(): string;
2359
+ /**
2360
+ * The local Unix socket. sun_path permits 103 UTF-8 bytes on macOS and 107 on Linux. Longer paths use
2361
+ * /tmp/stim-sockets-<uid>/<SHA-256 of the normalized absolute socket path>.sock.
2362
+ */
2363
+ declare function serverLocalSocket(): string;
2364
+ declare function serverSetupDir(): string;
2365
+ declare function setupJournalFile(hash: string): string | null;
2366
+ //#endregion
2367
+ //#region state/placement.d.ts
2368
+ /**
2369
+ * The placement words every workload shares (`remote.build`, `ios.remote`, `android.remote`, `macos.remote` and their
2370
+ * flags). Anything else a setting holds is a machine name, or for devices `eas` or `proxy`.
2371
+ *
2372
+ * - `local`: this machine only.
2373
+ * - `auto`: this machine first, a remote machine when this one is full or busy.
2374
+ * - `remote`: remote machines first, in `remote.machines` order; this machine only when none takes the run.
2375
+ * - `remote-only`: remote machines in `remote.machines` order, never this machine.
2376
+ */
2377
+ declare const PLACEMENT_WORDS: readonly ["local", "auto", "remote", "remote-only"];
2378
+ type PlacementWord = (typeof PLACEMENT_WORDS)[number];
2379
+ type AutomaticPlacement = Exclude<PlacementWord, "local">;
2380
+ declare function placementWord(value: string): PlacementWord | null;
2381
+ declare function prefersRemote(word: PlacementWord): boolean;
2382
+ declare function mayRunHere(word: PlacementWord): boolean;
2383
+ //#endregion
2384
+ //#region state/recordings.d.ts
2385
+ /** The footage stim-server keeps per device: the last 15 minutes recorded, however long ago. */
2386
+ declare const RECORDING_FOOTAGE_MS: number;
2387
+ /** The recordings of every workspace of one Stim home together; past it the oldest footage goes first. */
2388
+ declare const RECORDING_CAP_BYTES: number;
2389
+ /** The platforms stim-server records: owned simulators and emulators, and the Stim-owned Chrome page. */
2390
+ declare const RECORDING_PLATFORMS: readonly ["ios", "android", "web"];
2391
+ type RecordingPlatform = (typeof RECORDING_PLATFORMS)[number];
2392
+ /**
2393
+ * One file of H.264 footage. A closed segment is named `<start>-<end>.seg`, in epoch milliseconds of its first and
2394
+ * last access unit; the segment being written is `<start>.part`, and its end is its modification time.
2395
+ */
2396
+ interface RecordedSegment {
2397
+ file: string;
2398
+ start: number;
2399
+ end: number;
2400
+ bytes: number;
2401
+ open: boolean;
2376
2402
  }
2377
- /** The payload `stim status --json` prints, and `status --watch --json` prints on each change. */
2378
- interface StatusPayload {
2379
- maintenance?: MaintenanceStatus;
2380
- archived?: ArchivedWorkspace[];
2381
- archivedUsage?: ArchivedUsage;
2382
- environments: (EnvironmentState & {
2383
- labelOnly?: true;
2384
- })[];
2385
- capacity: StatusCapacity;
2386
- deviceLeases: DeviceLeaseState[];
2387
- unprovisionedWorktrees: WorktreeFacts[];
2388
- simctlAvailable: boolean;
2389
- /** Null when nothing runs that status attributes, so it read no process table, or when the table was unreadable. */
2390
- machine: MachineUsageState | null;
2403
+ interface RecordedDevice {
2404
+ platform: RecordingPlatform;
2405
+ slot: string;
2406
+ dir: string;
2407
+ segments: RecordedSegment[];
2391
2408
  }
2409
+ /** The directory under a workspace's recordings that holds one device slot's footage. */
2410
+ declare function recordingDeviceName(platform: RecordingPlatform, slot: string): string;
2411
+ /** Segment names hold whole milliseconds: the start rounded down and the end rounded up. */
2412
+ declare function closedSegmentName(start: number, end: number): string;
2413
+ declare function openSegmentName(start: number): string;
2414
+ /** The segments in one device directory, oldest first. Files that are not segments are left out; `closedOnly` excludes files still being written. */
2415
+ declare function listSegments(dir: string, closedOnly?: boolean): RecordedSegment[];
2416
+ /** Reads current segment files asynchronously, oldest first; cancellation propagates. */
2417
+ declare function listSegmentsAsync(dir: string, closedOnly?: boolean, signal?: AbortSignal): Promise<RecordedSegment[]>;
2418
+ /** Every device slot recorded under a workspace's recordings directory. */
2419
+ declare function listRecordedDevices(recordingsDir: string, closedOnly?: boolean): RecordedDevice[];
2420
+ /** Reads current device slots and their segment files asynchronously; cancellation propagates. */
2421
+ declare function listRecordedDevicesAsync(recordingsDir: string, closedOnly?: boolean, signal?: AbortSignal): Promise<RecordedDevice[]>;
2422
+ declare function recordedBytes(devices: readonly RecordedDevice[]): number;
2423
+ /**
2424
+ * The oldest closed segments to delete so that one device keeps at most `footageMs` of footage. Time between
2425
+ * segments, while nothing was recorded, does not count. The open segment is never chosen.
2426
+ */
2427
+ declare function footageDrops(segments: readonly RecordedSegment[], footageMs: number): RecordedSegment[];
2428
+ /** The oldest closed segments across all devices to delete so that together they hold at most `capBytes`. */
2429
+ declare function capDrops(devices: readonly RecordedDevice[], capBytes: number): RecordedSegment[];
2430
+ /**
2431
+ * Whether stim-server records a workspace's devices: `STIM_RECORDING`, then `recording.enabled` in the workspace,
2432
+ * repo and machine layers, in that order, and true by default.
2433
+ */
2434
+ declare function recordingEnabled(env: NodeJS.ProcessEnv, layers: readonly unknown[]): boolean;
2435
+ //#endregion
2436
+ //#region state/run-id.d.ts
2437
+ declare const RUN_ID_ENV = "STIM_RUN_ID";
2438
+ /** A run id as the CLI and stim-server accept it: letters, digits, `.`, `_` and `-`, at most 64. Null otherwise. */
2439
+ declare function validRunId(value: unknown): string | null;
2440
+ /**
2441
+ * The run id of the ambient operation context, else of this CLI invocation: `STIM_RUN_ID` when it holds a valid id
2442
+ * (Stim Desktop passes one), else a random one. The CLI entry exports it back to `STIM_RUN_ID` so processes the run
2443
+ * starts inherit it; this function does not.
2444
+ */
2445
+ declare function runId(env?: NodeJS.ProcessEnv): string;
2446
+ //#endregion
2447
+ //#region state/settings-schema.d.ts
2448
+ declare const SETTINGS_SCHEMA_FILE = "settings.schema.json";
2449
+ declare const SETTINGS_SCHEMA_URL: string;
2450
+ type JsonSchema = Record<string, unknown>;
2451
+ /**
2452
+ * The published settings schema. The root describes a committed `.stim.json`;
2453
+ * `$defs.machine` describes the settings keys of `$STIM_HOME/config.json`.
2454
+ * Every leaf carries `x-stim` with its dotted key, kind, writable scopes,
2455
+ * environment override, and sensitivity.
2456
+ */
2457
+ declare function settingsJsonSchema(): JsonSchema;
2392
2458
  //#endregion
2393
2459
  //#region state/status-derived.d.ts
2394
2460
  declare function withGitChip(worktree: WorktreeFacts): WorktreeFacts;
@@ -2649,25 +2715,29 @@ declare function readDeviceHostMachines(): DeviceHostMachineCredential[];
2649
2715
  //#endregion
2650
2716
  //#region state/hosted-metro.d.ts
2651
2717
  declare const HOSTED_METRO_REQUESTS_KEY = "hostedMetroRequests";
2652
- declare const HOSTED_METRO_GATEWAYS_KEY = "hostedMetroGateways";
2718
+ declare const HOSTED_METRO_LINKS_KEY = "hostedMetroLinks";
2653
2719
  /** Private supervisor request, keyed by hosted session; never part of a public payload. */
2654
2720
  interface HostedMetroRequest {
2655
2721
  id: string;
2656
2722
  machine: string;
2657
- address: string;
2658
- peer: string;
2659
- secret: string;
2660
2723
  }
2661
- interface HostedMetroGateway {
2724
+ /** The supervisor's answer to a request: `ready` while the host holds one of its links, `error` when it cannot link. */
2725
+ interface HostedMetroLinks {
2662
2726
  id: string;
2663
2727
  processToken: string;
2664
- port?: number;
2728
+ ready?: true;
2665
2729
  error?: string;
2666
2730
  }
2667
2731
  declare function hostedMetroRequests(state: WorkspaceState | null): Record<string, HostedMetroRequest>;
2668
- declare function hostedMetroGateways(state: WorkspaceState | null): Record<string, HostedMetroGateway>;
2732
+ declare function hostedMetroLinks(state: WorkspaceState | null): Record<string, HostedMetroLinks>;
2669
2733
  //#endregion
2670
2734
  //#region state/tailnet.d.ts
2735
+ /** stim-server's own port, which a machine on the local network connects to directly. */
2736
+ declare const DEFAULT_SERVER_PORT = 7787;
2737
+ /**
2738
+ * A `remote.machines` entry: a tailnet name, or `local:` and a host name or IPv4 address on the local network, each
2739
+ * optionally with `:<port>`.
2740
+ */
2671
2741
  declare const TAILNET_MACHINE_PATTERN: string;
2672
2742
  interface TailnetPeer {
2673
2743
  nodeId: string;
@@ -2681,7 +2751,13 @@ interface TailnetPeer {
2681
2751
  /** When Tailscale last saw the node, as an ISO timestamp; absent when it never did or the status omits it. */
2682
2752
  lastSeen?: string;
2683
2753
  }
2754
+ type MachineTransport = "tailscale" | "local";
2755
+ /**
2756
+ * A `remote.machines` entry's parts. A `tailscale` entry names a tailnet peer and the port of its `tailscale serve`
2757
+ * route; a `local` entry names a host on the local network and stim-server's own port there.
2758
+ */
2684
2759
  declare function parseMachine(entry: string): {
2760
+ transport: MachineTransport;
2685
2761
  name: string;
2686
2762
  port: number;
2687
2763
  } | null;
@@ -2706,9 +2782,14 @@ interface Endpoint {
2706
2782
  */
2707
2783
  declare function endpoint(peer: TailnetPeer, port: number): Endpoint;
2708
2784
  /**
2709
- * Where to reach a `remote.machines` entry now, or why Stim cannot. The tailnet only carries the bytes: the Noise
2710
- * handshake, pinned to the worker's server key, decides whether the machine answering is the one this machine paired
2711
- * with.
2785
+ * A `local` entry's stim-server, reached directly with plain WebSocket: protocol 2's Noise session authenticates and
2786
+ * encrypts it, so it needs no TLS.
2787
+ */
2788
+ declare function localEndpoint(name: string, port: number): Endpoint;
2789
+ /**
2790
+ * Where to reach a `remote.machines` entry now, or why Stim cannot. The tailnet or local network only carries the
2791
+ * bytes: the Noise handshake, pinned to the worker's server key, decides whether the machine answering is the one this
2792
+ * machine paired with. `current` is `tailscale status --json`, which a `local` entry does not need.
2712
2793
  */
2713
2794
  declare function machineEndpoint(machine: string, current: unknown): Endpoint | string;
2714
2795
  //#endregion
@@ -2836,8 +2917,16 @@ interface BuildLedgerRecord extends NdjsonRecord {
2836
2917
  outcome: BuildLedgerOutcome;
2837
2918
  failure?: BuildLedgerFailure;
2838
2919
  device?: BuildLedgerDevice;
2920
+ /** This machine's 5-minute load per core when the run started. Absent in older records. */
2921
+ loadPerCore?: number;
2922
+ /** The run's first owned simulator or emulator boot: when it started, in epoch milliseconds, and the load per core then. */
2923
+ boot?: BuildLedgerBoot;
2839
2924
  stimVersion: string;
2840
2925
  }
2926
+ interface BuildLedgerBoot {
2927
+ startedAt: number;
2928
+ loadPerCore: number;
2929
+ }
2841
2930
  declare function isBuildLedgerRecord(record: NdjsonRecord): record is BuildLedgerRecord;
2842
2931
  /** The ledger's records, oldest first. A missing or unreadable ledger is empty. */
2843
2932
  declare function readBuildLedger(file?: string): BuildLedgerRecord[];
@@ -2956,4 +3045,4 @@ declare class KeyedEntities<T> {
2956
3045
  /** Applies `delta` to `entities` keyed by `key`, keeping the order of `entities` and appending new keys. */
2957
3046
  declare function applyKeyedDelta<T>(entities: readonly T[], delta: KeyedDelta<T>, key: (entity: T) => string): T[];
2958
3047
  //#endregion
2959
- export { findPeer as $, hostedMacosAppSlot as $a, queryJsonLogs as $c, readDevServerStopRequest as $i, withConfigLock as $l, MachineUsageState as $n, runId as $o, MaintenanceMode as $r, maintenanceAttemptFile as $s, pullRequestCacheFile as $t, BuildLedgerRecord as A, parseHostedAndroidDevice as Aa, worktreeArchiveRoot as Ac, BUILD_HISTORY_LIMIT as Ai, TracePhases as Al, BuildPhase as An, RemoteDeviceBackend as Ao, MacosAppRecord as Ar, buildLedgerDir as As, offloadSummary as At, machineCacheUsageFile as Au, HOSTED_LOGS_PAGE_BYTES as B, readHostedSessions as Ba, advanceTail as Bc, MAINTENANCE_LOG_NAME as Bi, allMetroPorts as Bl, DeviceLeaseState as Bn, TUNNEL_MODES as Bo, ArchivedWorkspace as Br, deviceHostMachinesFile as Bs, DeviceViewersRecord as Bt, AgentActionReaderOptions as Bu, BuildLedgerCache as C, HostedNativeOffer as Ca, workspaceLightenedFile as Cc, parseHostedAndroidPlacement as Ci, readStateFile as Cl, BuildCause as Cn, settingsJsonSchema as Co, WebBrowserState as Cr, prefersRemote as Cs, StatsCapacityWait as Ct, buildSlotsDir as Cu, BuildLedgerOutcome as D, hostedConsolePort as Da, workspaceRecordingsDir as Dc, parseHostedNativePlacement as Di, TRACE_ENV as Dl, BuildMissCategory as Dn, IOS_SIMULATOR_APPS as Do, WorktreeFacts as Dr, archiveRoot as Ds, StatsScope as Dt, tryAcquireBuildSlotClaim as Du, BuildLedgerFailure as E, deviceHostRoot as Ea, workspaceMetadataFile as Ec, hostedNativeRecords as Ei, EndSpan as El, BuildHistoryEntry as En, ANDROID_PCH_CHOICES as Eo, WorkspaceStage as Er, archiveLock as Es, StatsReport as Et, saturation as Eu, GcLogStatus as F, parseHostedOfferRequest as Fa, ERROR_SOURCES as Fc, DevServerStopRequest as Fi, DebugLog as Fl, BuildResult as Fn, SettingEntry as Fo, parseMacosRecord as Fr, clientIdentityKeyFile as Fs, statsProjectKey as Ft, AgentDeviceUsage as Fu, parseHostedLogsCursor as G, HostedAgentGrant as Ga, fileIdentity as Gc, WarmRecord as Gi, getParkedMax as Gl, EnvironmentState as Gn, coerceSettingText as Go, readArchiveAsync as Gr, easMachineStateRoot as Gs, ENDED_AGENT_RETENTION_MS as Gt, LEVELS as Gu, HostedLogsPage as H, HOSTED_MACOS_APP_SLOTS as Ha, buildCriteria as Hc, READY_PHASE_MS as Hi, configCorruptRepair as Hl, DiskMeasure as Hn, WEB_VIEWPORTS as Ho, archivedUsage as Hr, deviceViewersDir as Hs, readViewedDevices as Ht, AgentTarget as Hu, GcLogTrigger as I, parseHostedPlatformDevice as Ia, LogQueryOptions as Ic, DeviceIdleShutdownRecord as Ii, createDebugLog as Il, BuildWaitingFor as In, SettingScope as Io, readHostedMacosApp as Ir, configLockPath as Is, trimCapacityRefusals as It, AgentDeviceUsageRoots as Iu, Endpoint as J, HostedAppLaunch as Ja, isExpoErrorContext as Jc, lastNativeUseFrom as Ji, getRepoSettings as Jl, LastBuildReport as Jn, isSensitiveReference as Jo, readArchivesAsync as Jr, emulatorLogFile as Js, agentSessionOf as Jt, levelRank as Ju, readHostedNativeLogsCheckpoint as K, HostedAgentIssuedGrant as Ka, fileSizes as Kc, WorkspaceState as Ki, getProject as Kl, GitChip as Kn, expectedShape as Ko, readArchiveState as Kr, easSessionLedgerFile as Ks, PullRequestCacheEntry as Kt, NdjsonRecord as Ku, isGcLogRecord as L, parseHostedRequest as La, LogQueryPlan as Lc, IDLE_STOP_KEY as Li, debugLogDir as Ll, ChecksState as Ln, SettingType as Lo, readMacosRecord as Lr, createdDevicesFile as Ls, trimCapacityWaits as Lt, agentDeviceUsageFile as Lu, readBuildLedger as M, parseHostedDevice as Ma, LogDir as Mc, DEVICE_RECLAIM_REASON as Mi, redactCommandLine as Ml, BuildPlanPayload as Mn, SETTING_GROUPS as Mo, MacosBuild as Mr, buildLedgerLock as Ms, readRunEstimates as Mt, SwiftpmCacheUsage as Mu, GcLogOptions as N, parseHostedLocalDeviceCount as Na, LogDirQuery as Nc, DEV_SERVER_STOP_REQUEST_KEY as Ni, trace as Nl, BuildPlatform as Nn, SETTING_SCOPES as No, MacosProcess as Nr, buildMachinesFile as Ns, readStats as Nt, readSwiftpmCacheUsage as Nu, BuildLedgerPlacement as O, hostedDeviceId as Oa, workspaceStateFile as Oc, unreadableHostedNative as Oi, Trace as Ol, BuildMissChange as On, REMOTE_DEVICE_BACKENDS as Oo, WorktreeGit as Or, budgetSweepFile as Os, decodeStats as Ot, MachineCacheUsage as Ou, GcLogRecord as P, parseHostedNativeOffer as Pa, openLogDir as Pc, DevServerStopRecord as Pi, DEBUG_ENV as Pl, BuildReport as Pn, SettingDefinition as Po, macosAppState as Pr, buildMachinesLock as Ps, readStatsReport as Pt, swiftpmCacheUsageFile as Pu, endpoint as Q, hostedAgentDriverName as Qa, planLogQuery as Qc, readDevServerStop as Qi, windowsShortDirKey as Ql, MachineOwnerKind as Qn, RUN_ID_ENV as Qo, MaintenanceCheck as Qr, gitMergeCacheDir as Qs, listStatusCacheEntries as Qt, readGcLog as R, readHostedDevice as Ra, NATIVE_CRASH_LOG_PREFIX as Rc, IdleStopRecord as Ri, debugLoggingEnabled as Rl, DeviceActivity as Rn, SettingWriteResult as Ro, ArchivedBytes as Rr, createdDevicesLock as Rs, trimSamples as Rt, readAgentDeviceUsage as Ru, BUILD_LEDGER_CAP as S, HostedIosDevice as Sa, workspaceGradleBuild as Sc, hostedAndroidStatus as Si, readJsonObject as Sl, BuildCacheHit as Sn, SETTINGS_SCHEMA_URL as So, WarmStep as Sr, placementWord as Ss, StatsCapacityRefusal as St, buildSlotPath as Su, BuildLedgerDevice as T, deviceHostArea as Ta, workspaceLogsDir as Tc, HostedNativePlacement as Ti, readHomeIdentity as Tl, BuildDiagnostic as Tn, ANDROID_EMULATOR_APPS as To, WorkspacePhase as Tr, archiveDir as Ts, StatsRecord as Tt, maxLoadPerCore as Tu, hostedMacosLogsDir as U, HostedAgentAccess as Ua, bundleMarkerPlatform as Uc, WARM_KEY as Ui, findEnclosingWorktreeRoot as Ul, EndedAgentSession as Un, WebViewport as Uo, linkReplacedArchives as Ur, diskUsageCacheDir as Us, stimBuildDigest as Ut, createAgentActionReader as Uu, HostedLogsCursor as V, HOSTED_AGENT_DRIVERS as Va, attachExpoErrorContext as Vc, MetroLastStop as Vi, buildWorkerRoot as Vl, DevicePlacement as Vn, TunnelMode as Vo, archiveEnabled as Vr, deviceHostMachinesLock as Vs, ViewedDevice as Vt, AgentRecordSelection as Vu, hostedNativeLogsDir as W, HostedAgentDriverName as Wa, compileGrep as Wc, WORKSPACE_AGENT_KEY as Wi, getConcurrencyLimits as Wl, EnvironmentDisk as Wn, acceptsShape as Wo, readArchive as Wr, diskUsageWalkClaims as Ws, AgentSessionsCache as Wt, createAsyncAgentActionReader as Wu, TailnetPeer as X, HostedMacosDevice as Xa, markerWindow as Xc, parseMissReason as Xi, loadConfig as Xl, MEMORY_SOURCES as Xn, settingDefinition as Xo, MaintenanceAction as Xr, gcLogFile as Xs, diskUsageFailureFile as Xt, parseNdjsonText as Xu, TAILNET_MACHINE_PATTERN as Y, HostedMacosChoice as Ya, logFiles as Yc, lastUseFrom as Yi, isPathPrefix as Yl, MACHINE_OWNER_KINDS as Yn, replacementAdvice as Yo, DiskPressureLevel as Yr, gcLogDir as Ys, diskUsageCacheFile as Yt, parseNdjsonLine as Yu, cannotHost as Z, HostedMacosPlacement as Za, parseSince as Zc, readBuildHistory as Zi, windowsShortDir as Zl, MachineOwner as Zn, settingValueError as Zo, MaintenanceActionKind as Zr, getConfigPath as Zs, endedAgentSessionOf as Zt, readNdjsonGenerations as Zu, isSetupJournalPrunable as _, HostedDevicePlatform as _a, workspaceAgentDeviceDir as _c, HostedAndroidPlacement as _i, readCreatedDevices as _l, AndroidRuntimeFacts as _n, readHostedAppMetadata as _o, StatusPayload as _r, recordingEnabled as _s, RunEstimates as _t, MACHINE_CREDENTIALS_VERSION as _u, MachineView as a, readWorkspaceAgent as aa, pullRequestCacheDir as ac, MaintenanceStatus as ai, tailRead as al, readPullRequestCache as an, parseHostedMacosPlacement as ao, PHYSICAL_DEVICE_CONNECTIONS as ar, RecordedSegment as as, HostedMetroRequest as at, ConcurrencyLimits as au, agentDeviceStateDirs as b, HostedDeviceSession as ba, workspaceEndedAgentsFile as bc, hostedAndroidPlacements as bi, isJsonObject as bl, BUILD_RESULTS as bn, validHostedAppBlob as bo, WORKSPACE_PHASES as br, PlacementWord as bs, STATS_VERSION as bt, readBuildMachines as bu, SummaryBuild as c, DEVICE_HOST_LIMITS as ca, setupJournalFile as cc, readMaintenanceAttempt as ci, fingerprintMemoDir as cl, ACTIVITY_RECENCY_BASES as cn, HostedAppFile as co, PlannedPhase as cr, closedSegmentName as cs, DeviceHostMachineCredential as ct, DoctorRunRecord as cu, WorktreeRow as d, HostedAndroidChoice as da, sharedGradle as dc, HostedIosStatus as di, readFingerprintMemo as dl, ActiveBuildState as dn, hostedAppArea as do, STATUS_ISSUE_CODES as dr, listRecordedDevicesAsync as ds, CAPACITY_REFUSAL_LIMIT as dt, RepoRecord as du, readDeviceIdleShutdowns as ea, maintenanceChildLogFile as ec, MaintenancePass as ei, queryLogs as el, readAgentSessionsCache as en, hostedMacosBundleId as eo, MacosBuildPlanPayload as er, validRunId as es, machineEndpoint as et, withConfigLockAsync as eu, statusViews as f, HostedAndroidDevice as fa, sharedPods as fc, hostedIosPlacements as fi, countErrorEntries as fl, ActivityDriver as fn, hostedAppAttempt as fo, SimulatorState as fr, listSegments as fs, CAPACITY_REFUSAL_MAX_AGE_MS as ft, SettingsObject as fu, isJournalExpired as g, HostedDevicePhase as ga, trashRoot as gc, unreadableHostedIos as gi, EasSessionLedgerRead as gl, AndroidDeviceState as gn, readHostedApp as go, StatusIssueCode as gr, recordingDeviceName as gs, ReadStatsResult as gt, BuildMachineCredential as gu, SetupJournal as h, HostedDeviceOfferRequest as ha, supervisorPidFile as hc, parseHostedIosPlacement as hi, EasSessionClaim as hl, AgentTool as hn, parseHostedAppOffer as ho, StatusIssue as hr, recordedBytes as hs, PLACEMENT_DECISIONS as ht, clientIdentity as hu, ArchivedSummary as i, readWarmRecord as ia, maintenanceStateFile as ic, MaintenanceState as ii, sortByTs as il, readEndedAgentSessions as in, parseHostedMacosDevice as io, NativeBuildStep as ir, RecordedDevice as is, HostedMetroGateway as it, validateAutomaticPool as iu, isBuildLedgerRecord as j, parseHostedChoice as ja, createNdjsonTail as jc, DEVICE_IDLE_SHUTDOWN_KEY as ji, redactArgv as jl, BuildPlacement as jn, SETTINGS as jo, MacosAppState as jr, buildLedgerFile as js, projectRootDirectories as jt, readMachineCacheUsage as ju, BuildLedgerPlatform as k, hostedWorkerSettleMs as ka, workspaceStateLock as kc, BUILD_HISTORY_KEY as ki, TraceArgs as kl, BuildMissReason as kn, REPLACED_SETTINGS as ko, WorktreePullRequest as kr, budgetSweepLock as ks, findProjectRoot as kt, SimRuntimeUsage as ku, SummaryHistoryEntry as l, DeviceHostLimits as la, sharedCcache as lc, readMaintenanceState as li, fingerprintMemoFile as ll, AGENT_TOOLS as ln, HostedAppOffer as lo, RemoteDeviceState as lr, footageDrops as ls, readDeviceHostMachines as lt, PlatformRecords as lu, SetupCapability as m, HostedDeviceOffer as ma, supervisorLogFile as mc, hostedIosStatus as mi, CreatedDevices as ml, AgentSession as mn, parseHostedAppManifest as mo, StatusCapacity as mr, openSegmentName as ms, OffloadSummary as mt, SupervisorRecord as mu, KeyedEntities as n, readIdleStop as na, maintenanceNdjsonFile as nc, MaintenanceRecord as ni, readLogTimeline as nl, readDiskUsage as nn, parseHostedAgentGrant as no, MetroBundleState as nr, RECORDING_FOOTAGE_MS as ns, HOSTED_METRO_GATEWAYS_KEY as nt, automaticMachineEnabled as nu, SUMMARY_ARCHIVES as o, readWorkspaceState as oa, serverLocalSocket as oc, MemoryCulprit as oi, FINGERPRINT_MEMO_SCHEMA as ol, withDerivedFacts as on, HOSTED_APP_CHUNK_BYTES as oo, PhysicalDeviceConnection as or, RecordingPlatform as os, hostedMetroGateways as ot, Config as ou, workspaceSummary as p, HostedDevice as pa, statsFile as pc, hostedIosRecords as pi, CreatedDevicePlatform as pl, ActivityRecencyBasis as pn, hostedAppBlobs as po, StatsPlatform as pr, listSegmentsAsync as ps, HISTORY_LIMIT as pt, StimConfig as pu, readLogsSince as q, HostedAgentLease as qa, followLogs as qc, buildDiagnostics as qi, getProjectSettings as ql, GitChipPart as qn, isLayeredSetting as qo, readArchives as qr, easSessionLedgerLock as qs, StatusCacheEntry as qt, SOURCES as qu, applyKeyedDelta as r, readLastBuilds as ra, maintenanceRunClaims as rc, MaintenanceSize as ri, recordMatches as rl, readDiskUsageFailure as rn, parseHostedMacosChoice as ro, NATIVE_BUILD_STEPS as rr, RECORDING_PLATFORMS as rs, HOSTED_METRO_REQUESTS_KEY as rt, requireAutomaticMachine as ru, StatusViews as s, workspaceLastUsed as sa, serverSetupDir as sc, parseMaintenanceState as si, FingerprintMemoRecord as sl, withGitChip as sn, HostedAppDelivery as so, PhysicalDeviceState as sr, capDrops as ss, hostedMetroRequests as st, DeviceRecord as su, KeyedDelta as t, readDevicePlacement as ta, maintenanceDir as tc, MaintenancePressure as ti, readLogRecords as tl, readBuildDetail as tn, parseHostedAgentAccess as to, MemorySource as tr, RECORDING_CAP_BYTES as ts, parseMachine as tt, AutomaticPoolRole as tu, WorkspaceSummary as u, HOSTED_PARKED_CANDIDATES as ua, sharedCompilationCache as uc, HostedIosPlacement as ui, fingerprintMemoLock as ul, APP_PROCESS_STATES as un, HostedAppRecord as uo, RunOutcomeKind as ur, listRecordedDevices as us, BuildMachineTotals as ut, ProjectRecord as uu, parseSetupJournal as v, HostedDeviceRequest as va, workspaceBuildDetailFile as vc, HostedAndroidStatus as vi, readEasSessionLedger as vl, AppPresence as vn, rememberHostedAppBlobRename as vo, TUTORIAL_VERSION as vr, AutomaticPlacement as vs, RunHistory as vt, MachineCredential as vu, BuildLedgerCacheResult as w, assertHostedDeviceLedger as wa, workspaceLogErrorIndex as wc, unreadableHostedAndroid as wi, withStateReadCache as wl, BuildDetail as wn, ANDROID_COMPILER_CACHE_CHOICES as wo, WebPageState as wr, agentSessionsCacheFile as ws, StatsPlacement as wt, machineCapacity as wu, agentDeviceStateDirsAsync as x, HostedIosChoice as xa, workspaceEndedAgentsLock as xc, hostedAndroidRecords as xi, readJsonFile as xl, BuildActivity as xn, SETTINGS_SCHEMA_FILE as xo, WORKSPACE_STAGE_KINDS as xr, mayRunHere as xs, StatsBucket as xt, MachineCapacity as xu, readSetupJournal as y, HostedDeviceSelectors as ya, workspaceDerivedData as yc, hostedAndroidDeviceName as yi, validEasSessionClaim as yl, BUILD_PHASES as yn, validHostedAppArguments as yo, WEB_PAGE_STATES as yr, PLACEMENT_WORDS as ys, RunSample as yt, parseMachineCredential as yu, HOSTED_LOGS_FIRST_BYTES as z, readHostedDeviceLedger as za, QueryCriteria as zc, LAST_BUILD_KEYS as zi, ParkedMax as zl, DeviceAppProcess as zn, SettingsPayload as zo, ArchivedUsage as zr, deviceHostMachinesClaims as zs, wholePhases as zt, readAgentDeviceUsageCache as zu };
3048
+ export { TailnetPeer as $, AgentSession as $a, DevServerStopRecord as $c, createDebugLog as $i, parseHostedNativeOffer as $l, configLockPath as $n, StatsPlatform as $o, LogQueryOptions as $r, macosAppState as $s, diskUsageCacheFile as $t, AgentRecordSelection as $u, BuildLedgerPlatform as A, StimConfig as Aa, HostedIosStatus as Ac, CreatedDevicePlatform as Ai, HostedAndroidChoice as Al, listSegmentsAsync as An, EnvironmentState as Ao, statsFile as Ar, SETTING_SCOPES as As, StatsReport as At, hostedAppArea as Au, HOSTED_LOGS_FIRST_BYTES as B, buildSlotsDir as Ba, hostedAndroidRecords as Bc, readStateFile as Bi, HostedIosChoice as Bl, prefersRemote as Bn, MacosBuildPlanPayload as Bo, workspaceLightenedFile as Br, WebViewport as Bs, trimCapacityRefusals as Bt, MachineCacheUsage as Bu, BuildLedgerBoot as C, Config as Ca, MaintenanceState as Cc, FINGERPRINT_MEMO_SCHEMA as Ci, readWarmRecord as Cl, RecordingPlatform as Cn, DeviceActivity as Co, serverLocalSocket as Cr, ANDROID_PCH_CHOICES as Cs, RunSample as Ct, parseHostedMacosDevice as Cu, BuildLedgerFailure as D, ProjectRecord as Da, readMaintenanceAttempt as Dc, fingerprintMemoLock as Di, DEVICE_HOST_LIMITS as Dl, listRecordedDevices as Dn, DiskMeasure as Do, sharedCompilationCache as Dr, RemoteDeviceBackend as Ds, StatsCapacityWait as Dt, HostedAppFile as Du, BuildLedgerDevice as E, PlatformRecords as Ea, parseMaintenanceState as Ec, fingerprintMemoFile as Ei, workspaceLastUsed as El, footageDrops as En, DevicePlacement as Eo, sharedCcache as Er, REPLACED_SETTINGS as Es, StatsCapacityRefusal as Et, HostedAppDelivery as Eu, GcLogRecord as F, MachineCredential as Fa, unreadableHostedIos as Fc, readEasSessionLedger as Fi, HostedDevicePhase as Fl, AutomaticPlacement as Fn, MACHINE_OWNER_KINDS as Fo, workspaceBuildDetailFile as Fr, SettingWriteResult as Fs, projectRootDirectories as Ft, readHostedApp as Fu, hostedNativeLogsDir as G, saturation as Ga, hostedNativeRecords as Gc, Trace as Gi, deviceHostRoot as Gl, budgetSweepFile as Gn, PHYSICAL_DEVICE_CONNECTIONS as Go, workspaceStateFile as Gr, isSensitiveReference as Gs, ViewedDevice as Gt, readSwiftpmCacheUsage as Gu, HostedLogsCursor as H, loadState as Ha, parseHostedAndroidPlacement as Hc, readHomeIdentity as Hi, HostedNativeOffer as Hl, archiveDir as Hn, MetroBundleState as Ho, workspaceLogsDir as Hr, coerceSettingText as Hs, trimSamples as Ht, machineCacheUsageFile as Hu, GcLogStatus as I, parseMachineCredential as Ia, HostedAndroidPlacement as Ic, validEasSessionClaim as Ii, HostedDevicePlatform as Il, PLACEMENT_WORDS as In, MEMORY_SOURCES as Io, workspaceDerivedData as Ir, SettingsPayload as Is, readRunEstimates as It, readHostedAppMetadata as Iu, readLogsSince as J, AGENT_TOOLS as Ja, BUILD_HISTORY_KEY as Jc, redactArgv as Ji, hostedWorkerSettleMs as Jl, buildLedgerFile as Jn, PlannedPhase as Jo, createNdjsonTail as Jr, settingValueError as Js, AgentSessionsCache as Jt, AgentDeviceUsageRoots as Ju, parseHostedLogsCursor as K, tryAcquireBuildSlotClaim as Ka, parseHostedNativePlacement as Kc, TraceArgs as Ki, hostedConsolePort as Kl, budgetSweepLock as Kn, PhysicalDeviceConnection as Ko, workspaceStateLock as Kr, replacementAdvice as Ks, readViewedDevices as Kt, swiftpmCacheUsageFile as Ku, GcLogTrigger as L, readBuildMachines as La, HostedAndroidStatus as Lc, isJsonObject as Li, HostedDeviceRequest as Ll, PlacementWord as Ln, MachineOwner as Lo, workspaceEndedAgentsFile as Lr, TUNNEL_MODES as Ls, readStats as Lt, rememberHostedAppBlobRename as Lu, isBuildLedgerRecord as M, clientIdentity as Ma, hostedIosRecords as Mc, EasSessionClaim as Mi, HostedDevice as Ml, recordedBytes as Mn, GitChipPart as Mo, supervisorPidFile as Mr, SettingEntry as Ms, decodeStats as Mt, hostedAppBlobs as Mu, readBuildLedger as N, BuildMachineCredential as Na, hostedIosStatus as Nc, EasSessionLedgerRead as Ni, HostedDeviceOffer as Nl, recordingDeviceName as Nn, LastBuildReport as No, trashRoot as Nr, SettingScope as Ns, findProjectRoot as Nt, parseHostedAppManifest as Nu, BuildLedgerOutcome as O, RepoRecord as Oa, readMaintenanceState as Oc, readFingerprintMemo as Oi, DeviceHostLimits as Ol, listRecordedDevicesAsync as On, EndedAgentSession as Oo, sharedGradle as Or, SETTINGS as Os, StatsPlacement as Ot, HostedAppOffer as Ou, GcLogOptions as P, MACHINE_CREDENTIALS_VERSION as Pa, parseHostedIosPlacement as Pc, readCreatedDevices as Pi, HostedDeviceOfferRequest as Pl, recordingEnabled as Pn, LoadState as Po, workspaceAgentDeviceDir as Pr, SettingType as Ps, offloadSummary as Pt, parseHostedAppOffer as Pu, TAILNET_MACHINE_PATTERN as Q, ActivityRecencyBasis as Qa, DEV_SERVER_STOP_REQUEST_KEY as Qc, DebugLog as Qi, parseHostedLocalDeviceCount as Ql, clientIdentityKeyFile as Qn, SimulatorState as Qo, ERROR_SOURCES as Qr, MacosProcess as Qs, agentSessionOf as Qt, AgentActionReaderOptions as Qu, isGcLogRecord as R, MachineCapacity as Ra, hostedAndroidDeviceName as Rc, readJsonFile as Ri, HostedDeviceSelectors as Rl, mayRunHere as Rn, MachineOwnerKind as Ro, workspaceEndedAgentsLock as Rr, TunnelMode as Rs, readStatsReport as Rt, validHostedAppArguments as Ru, BUILD_LEDGER_CAP as S, ConcurrencyLimits as Sa, MaintenanceSize as Sc, tailRead as Si, readLastBuilds as Sl, RecordedSegment as Sn, ChecksState as So, pullRequestCacheDir as Sr, ANDROID_EMULATOR_APPS as Ss, RunHistory as St, parseHostedMacosChoice as Su, BuildLedgerCacheResult as T, DoctorRunRecord as Ta, MemoryCulprit as Tc, fingerprintMemoDir as Ti, readWorkspaceState as Tl, closedSegmentName as Tn, DeviceLeaseState as To, setupJournalFile as Tr, REMOTE_DEVICE_BACKENDS as Ts, StatsBucket as Tt, HOSTED_APP_CHUNK_BYTES as Tu, HostedLogsPage as U, machineCapacity as Ua, unreadableHostedAndroid as Uc, EndSpan as Ui, assertHostedDeviceLedger as Ul, archiveLock as Un, NATIVE_BUILD_STEPS as Uo, workspaceMetadataFile as Ur, expectedShape as Us, wholePhases as Ut, readMachineCacheUsage as Uu, HOSTED_LOGS_PAGE_BYTES as V, loadPerCore as Va, hostedAndroidStatus as Vc, withStateReadCache as Vi, HostedIosDevice as Vl, agentSessionsCacheFile as Vn, MemorySource as Vo, workspaceLogErrorIndex as Vr, acceptsShape as Vs, trimCapacityWaits as Vt, SimRuntimeUsage as Vu, hostedMacosLogsDir as W, maxLoadPerCore as Wa, HostedNativePlacement as Wc, TRACE_ENV as Wi, deviceHostArea as Wl, archiveRoot as Wn, NativeBuildStep as Wo, workspaceRecordingsDir as Wr, isLayeredSetting as Ws, DeviceViewersRecord as Wt, SwiftpmCacheUsage as Wu, Endpoint as X, ActiveBuildState as Xa, DEVICE_IDLE_SHUTDOWN_KEY as Xc, trace as Xi, parseHostedChoice as Xl, buildMachinesFile as Xn, RunOutcomeKind as Xo, LogDirQuery as Xr, MacosAppState as Xs, PullRequestCacheEntry as Xt, readAgentDeviceUsage as Xu, DEFAULT_SERVER_PORT as Y, APP_PROCESS_STATES as Ya, BUILD_HISTORY_LIMIT as Yc, redactCommandLine as Yi, parseHostedAndroidDevice as Yl, buildLedgerLock as Yn, RemoteDeviceState as Yo, LogDir as Yr, MacosAppRecord as Ys, ENDED_AGENT_RETENTION_MS as Yt, agentDeviceUsageFile as Yu, MachineTransport as Z, ActivityDriver as Za, DEVICE_RECLAIM_REASON as Zc, DEBUG_ENV as Zi, parseHostedDevice as Zl, buildMachinesLock as Zn, STATUS_ISSUE_CODES as Zo, openLogDir as Zr, MacosBuild as Zs, StatusCacheEntry as Zt, readAgentDeviceUsageCache as Zu, isSetupJournalPrunable as _, withConfigLockAsync as _a, MaintenanceCheck as _c, queryLogs as _i, readDevServerStop as _l, validRunId as _n, BuildPlanPayload as _o, maintenanceChildLogFile as _r, WorkspaceStage as _s, HISTORY_LIMIT as _t, hostedAgentDriverName as _u, MachineView as a, configCorruptRepair as aa, ArchivedWorkspace as ac, SOURCES as ad, buildCriteria as ai, MAINTENANCE_LOG_NAME as al, readBuildDetail as an, BUILD_RESULTS as ao, deviceViewersDir as ar, StatusDeviceSlots as as, parseMachine as at, readHostedSessions as au, agentDeviceStateDirs as b, requireAutomaticMachine as ba, MaintenancePressure as bc, recordMatches as bi, readDevicePlacement as bl, RECORDING_PLATFORMS as bn, BuildResult as bo, maintenanceRunClaims as br, WorktreePullRequest as bs, ReadStatsResult as bt, parseHostedAgentAccess as bu, SummaryBuild as c, getParkedMax as ca, linkReplacedArchives as cc, parseNdjsonText as cd, fileIdentity as ci, WARM_KEY as cl, readEndedAgentSessions as cn, BuildCause as co, easMachineStateRoot as cr, StatusPayload as cs, HostedMetroLinks as ct, HostedAgentAccess as cu, WorktreeRow as d, getRepoSettings as da, readArchiveState as dc, isExpoErrorContext as di, WorkspaceState as dl, withGitChip as dn, BuildHistoryEntry as do, emulatorLogFile as dr, WORKSPACE_PHASES as ds, hostedMetroRequests as dt, HostedAgentIssuedGrant as du, debugLogDir as ea, parseMacosRecord as ec, AgentTarget as ed, LogQueryPlan as ei, DevServerStopRequest as el, diskUsageFailureFile as en, AgentTool as eo, createdDevicesFile as er, StatusBuildSlotHolder as es, cannotHost as et, parseHostedOfferRequest as eu, statusViews as f, isPathPrefix as fa, readArchives as fc, logFiles as fi, buildDiagnostics as fl, SETTINGS_SCHEMA_FILE as fn, BuildMissCategory as fo, gcLogDir as fr, WORKSPACE_STAGE_KINDS as fs, DeviceHostMachineCredential as ft, HostedAgentLease as fu, isJournalExpired as g, withConfigLock as ga, MaintenanceActionKind as gc, queryJsonLogs as gi, readBuildHistory as gl, runId as gn, BuildPlacement as go, maintenanceAttemptFile as gr, WorkspacePhase as gs, CAPACITY_REFUSAL_MAX_AGE_MS as gt, HostedMacosPlacement as gu, SetupJournal as h, windowsShortDirKey as ha, MaintenanceAction as hc, planLogQuery as hi, parseMissReason as hl, RUN_ID_ENV as hn, BuildPhase as ho, gitMergeCacheDir as hr, WebPageState as hs, CAPACITY_REFUSAL_LIMIT as ht, HostedMacosDevice as hu, ArchivedSummary as i, buildWorkerRoot as ia, ArchivedUsage as ic, NdjsonRecord as id, attachExpoErrorContext as ii, LAST_BUILD_KEYS as il, readAgentSessionsCache as in, BUILD_PHASES as io, deviceHostMachinesLock as ir, StatusDeviceSlotHolder as is, machineEndpoint as it, readHostedDeviceLedger as iu, BuildLedgerRecord as j, SupervisorRecord as ja, hostedIosPlacements as jc, CreatedDevices as ji, HostedAndroidDevice as jl, openSegmentName as jn, GitChip as jo, supervisorLogFile as jr, SettingDefinition as js, StatsScope as jt, hostedAppAttempt as ju, BuildLedgerPlacement as k, SettingsObject as ka, HostedIosPlacement as kc, countErrorEntries as ki, HOSTED_PARKED_CANDIDATES as kl, listSegments as kn, EnvironmentDisk as ko, sharedPods as kr, SETTING_GROUPS as ks, StatsRecord as kt, HostedAppRecord as ku, SummaryHistoryEntry as l, getProject as la, readArchive as lc, readNdjsonGenerations as ld, fileSizes as li, WORKSPACE_AGENT_KEY as ll, readPullRequestCache as ln, BuildDetail as lo, easSessionLedgerFile as lr, TUTORIAL_VERSION as ls, HostedMetroRequest as lt, HostedAgentDriverName as lu, SetupCapability as m, windowsShortDir as ma, DiskPressureLevel as mc, parseSince as mi, lastUseFrom as ml, settingsJsonSchema as mn, BuildMissReason as mo, getConfigPath as mr, WebBrowserState as ms, BuildMachineTotals as mt, HostedMacosChoice as mu, KeyedEntities as n, ParkedMax as na, readMacosRecord as nc, createAsyncAgentActionReader as nd, QueryCriteria as ni, IDLE_STOP_KEY as nl, listStatusCacheEntries as nn, AndroidRuntimeFacts as no, deviceHostMachinesClaims as nr, StatusCapacity as ns, findPeer as nt, parseHostedRequest as nu, SUMMARY_ARCHIVES as o, findEnclosingWorktreeRoot as oa, archiveEnabled as oc, levelRank as od, bundleMarkerPlatform as oi, MetroLastStop as ol, readDiskUsage as on, BuildActivity as oo, diskUsageCacheDir as or, StatusIssue as os, HOSTED_METRO_LINKS_KEY as ot, HOSTED_AGENT_DRIVERS as ou, workspaceSummary as p, loadConfig as pa, readArchivesAsync as pc, markerWindow as pi, lastNativeUseFrom as pl, SETTINGS_SCHEMA_URL as pn, BuildMissChange as po, gcLogFile as pr, WarmStep as ps, readDeviceHostMachines as pt, HostedAppLaunch as pu, readHostedNativeLogsCheckpoint as q, ACTIVITY_RECENCY_BASES as qa, unreadableHostedNative as qc, TracePhases as qi, hostedDeviceId as ql, buildLedgerDir as qn, PhysicalDeviceState as qo, worktreeArchiveRoot as qr, settingDefinition as qs, stimBuildDigest as qt, AgentDeviceUsage as qu, applyKeyedDelta as r, allMetroPorts as ra, ArchivedBytes as rc, LEVELS as rd, advanceTail as ri, IdleStopRecord as rl, pullRequestCacheFile as rn, AppPresence as ro, deviceHostMachinesFile as rr, StatusCapacityLoad as rs, localEndpoint as rt, readHostedDevice as ru, StatusViews as s, getConcurrencyLimits as sa, archivedUsage as sc, parseNdjsonLine as sd, compileGrep as si, READY_PHASE_MS as sl, readDiskUsageFailure as sn, BuildCacheHit as so, diskUsageWalkClaims as sr, StatusIssueCode as ss, HOSTED_METRO_REQUESTS_KEY as st, HOSTED_MACOS_APP_SLOTS as su, KeyedDelta as t, debugLoggingEnabled as ta, readHostedMacosApp as tc, createAgentActionReader as td, NATIVE_CRASH_LOG_PREFIX as ti, DeviceIdleShutdownRecord as tl, endedAgentSessionOf as tn, AndroidDeviceState as to, createdDevicesLock as tr, StatusBuildSlots as ts, endpoint as tt, parseHostedPlatformDevice as tu, WorkspaceSummary as u, getProjectSettings as ua, readArchiveAsync as uc, followLogs as ui, WarmRecord as ul, withDerivedFacts as un, BuildDiagnostic as uo, easSessionLedgerLock as ur, WEB_PAGE_STATES as us, hostedMetroLinks as ut, HostedAgentGrant as uu, parseSetupJournal as v, AutomaticPoolRole as va, MaintenanceMode as vc, readLogRecords as vi, readDevServerStopRequest as vl, RECORDING_CAP_BYTES as vn, BuildPlatform as vo, maintenanceDir as vr, WorktreeFacts as vs, OffloadSummary as vt, hostedMacosAppSlot as vu, BuildLedgerCache as w, DeviceRecord as wa, MaintenanceStatus as wc, FingerprintMemoRecord as wi, readWorkspaceAgent as wl, capDrops as wn, DeviceAppProcess as wo, serverSetupDir as wr, IOS_SIMULATOR_APPS as ws, STATS_VERSION as wt, parseHostedMacosPlacement as wu, agentDeviceStateDirsAsync as x, validateAutomaticPool as xa, MaintenanceRecord as xc, sortByTs as xi, readIdleStop as xl, RecordedDevice as xn, BuildWaitingFor as xo, maintenanceStateFile as xr, ANDROID_COMPILER_CACHE_CHOICES as xs, RunEstimates as xt, parseHostedAgentGrant as xu, readSetupJournal as y, automaticMachineEnabled as ya, MaintenancePass as yc, readLogTimeline as yi, readDeviceIdleShutdowns as yl, RECORDING_FOOTAGE_MS as yn, BuildReport as yo, maintenanceNdjsonFile as yr, WorktreeGit as ys, PLACEMENT_DECISIONS as yt, hostedMacosBundleId as yu, readGcLog as z, buildSlotPath as za, hostedAndroidPlacements as zc, readJsonObject as zi, HostedDeviceSession as zl, placementWord as zn, MachineUsageState as zo, workspaceGradleBuild as zr, WEB_VIEWPORTS as zs, statsProjectKey as zt, validHostedAppBlob as zu };