Skip to content

refactor(platform-apple): derive the runner host port instead of mirroring host-kit #2658

Description

@thymikee

Purpose

packages/platform-apple/src/runner/host.ts is the Apple runner host port: R77 keeps runner/** from value-importing @agent-device/host-kit/* so the seven Apple façade eager closures stay fixed. The port is right. Its shape is not: it is a hand-maintained mirror of host-kit, so every host-kit change is re-typed by hand.

Measured over the last 300 commits (main 91652a8fc5): 403 LOC, 63 exports, 28 importers inside the package, 11 touches. Nine of the eleven were not moves; six of those were a host-kit signature change or one new capability, each costing 3 to 16 lines here plus the binding in core/runner-host.ts (#2598, #2599, #2595, #2470, #2423, #2160).

Four hand-kept layers, and each host-kit change touches two or three of them:

  1. ~100 lines of copied type declarations (ExecResult, RetryPolicy, ProcessLockOwner, DiagnosticEventInput, …). Seven of the names do not exist in host-kit under that name (ExecStreamOptions, RetryPolicy, RetryOptions, RetryAttemptContext, RetryTelemetryEvent, DiagnosticEventInput, DefinedEnvMap) and are re-typed from scratch.
  2. A 50-member AppleRunnerHost interface that re-declares every function signature.
  3. Fifty one-line delegators (export const runCmdSync: AppleRunnerHost['runCmdSync'] = (…) => requireHost().runCmdSync(…)).
  4. core/runner-host.ts, which lists every name a third time.

Required behavior

Derive the port from the modules it fronts; keep the seam and the rule.

  1. Types. Delete the copied type block. The 15 runner files that import those types take them from the owning package with import type (R77 exempts type-only imports; typecheck erases them). Export the seven missing names from the host-kit subpath that owns the function (command, retry, diagnostics) — a host-kit fix, not a platform-apple one.
  2. Interface. Replace the re-declared signatures with a name list over the real modules:
    import type * as HostCommand from '@agent-device/host-kit/command';
    import type * as HostProcess from '@agent-device/host-kit/process';
    // one per subpath; ../core/tool-provider.ts, simctl.ts, physical-device-control.ts,
    // plist-xml.ts, runner-owner-state.ts for the Apple-owned members
    export type AppleRunnerHost =
      Pick<typeof HostCommand, 'runCmdStreaming' | 'runCmdSync' | 'runCmdBackground' | 'requireExecSuccess' | 'isCommandTimeoutError' | 'shellQuote'>
      & Pick<typeof HostProcess, 'isProcessAlive' | 'isProcessGroupAlive' | 'readProcessStartTime' | 'readProcessCommand' | 'signalPidsBestEffort' | 'signalProcessGroupBestEffort' | 'classifyOwnerLiveness'>
      & /* … */
      & { deadlineFromTimeoutMs: typeof HostRetry.Deadline.fromTimeoutMs };
    import type * as is a type-only declaration: R77 and the eager-closure gate see nothing. A signature change in host-kit reaches the runner with zero edits here.
  3. Delegators. Keep the named exports so the 28 importers do not change, typed by derivation:
    function delegate<K extends keyof AppleRunnerHost>(name: K): AppleRunnerHost[K] {
      return ((...args: unknown[]) =>
        (requireHost()[name] as (...a: unknown[]) => unknown)(...args)) as AppleRunnerHost[K];
    }
    export const runCmdSync = delegate('runCmdSync');
    Keep the Deadline object shim as is (seven files call Deadline.fromTimeoutMs).
  4. Binding. core/runner-host.ts spreads the real modules instead of listing names ({ ...HostCommand, ...HostProcess, …, deadlineFromTimeoutMs: Deadline.fromTimeoutMs, ...appleCore }). The Pick list becomes the single place that says what the runner may reach. test-host.ts is a Proxy and needs no change.

After this, adding a capability is one name in the Pick and one delegate line.

Completion conditions

Caveat

Spreading whole modules into the binding puts every export of those modules on the host object at runtime; the Pick type hides them. If check:production-exports or fallow objects, list the names explicitly in the binding instead. That keeps two lists, still with no hand-written signatures.

Out of scope

  • Retiring the port or R77. The eager-closure property still binds the runner subtree.
  • Changing any host-kit behavior. Only type exports are added there.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions