@@ -3,10 +3,7 @@ import { AppError } from '@agent-device/kernel/errors';
33import { execFailureDetails } from '@agent-device/host-kit/command' ;
44import { runAppleToolCommand } from '../core/tool-provider.ts' ;
55import { COLD_TOOLCHAIN_PROBE_TIMEOUT_MS } from '../runner/apple-runner-platform.ts' ;
6- import {
7- readHostToolchainIdentity ,
8- type HostToolchainIdentity ,
9- } from '../snapshot-source/cache-identity.ts' ;
6+ import { readHostToolchainIdentity } from '../snapshot-source/cache-identity.ts' ;
107import {
118 createSnapshotSourceDeadline ,
129 type SnapshotSourceDeadline ,
@@ -17,43 +14,15 @@ import {
1714 ensureNativeBuildCacheEntry ,
1815 execNativeBuildClang ,
1916 fingerprintNativeBuildSource ,
20- nativeBuildCacheKey ,
21- nativeBuildManifestFieldsMatch ,
2217} from '../snapshot-source/native-build-cache.ts' ;
2318import type { SnapshotSourceHost } from '../snapshot-source/types.ts' ;
2419
2520const FOLD_HELPER_SOURCE_FILENAME = 'Fold.m' ;
2621const FOLD_HELPER_BINARY_FILENAME = 'fold-helper' ;
2722const FOLD_HELPER_SCHEMA_VERSION = 1 as const ;
2823const FOLD_HELPER_LOCK_DESCRIPTION = 'iOS Simulator fold helper cache' ;
29- const MANIFEST_FIELDS = [
30- 'schemaVersion' ,
31- 'sourceHash' ,
32- 'cacheKey' ,
33- 'toolchain' ,
34- 'compileArgv' ,
35- ] as const ;
36-
37- /**
38- * The fold helper cache key, folding in the compile argv (fingerprinted with a placeholder
39- * `sourceRoot` and `outputPath`) alongside `sourceHash` and `toolchain`, so a change to a compiler
40- * flag or framework list cannot serve a binary built from a different command line (#2796
41- * follow-up).
42- */
43- export function foldHelperCacheKey (
44- input : Readonly < {
45- sourceHash : string ;
46- toolchain : HostToolchainIdentity ;
47- compileArgv : readonly string [ ] ;
48- } > ,
49- ) : string {
50- return nativeBuildCacheKey ( {
51- schemaVersion : FOLD_HELPER_SCHEMA_VERSION ,
52- sourceHash : input . sourceHash ,
53- toolchain : input . toolchain ,
54- compileArgv : input . compileArgv ,
55- } ) ;
56- }
24+ const FOLD_HELPER_BUILD_HINT =
25+ 'Select an Xcode with the iOS simulator SDK and foldable HID support using DEVELOPER_DIR.' ;
5726
5827/** Upper bound on a single fold-helper clang invocation; the same budget the prior per-call build used. */
5928export const FOLD_HELPER_BUILD_TIMEOUT_MS = 30_000 ;
@@ -68,8 +37,9 @@ const FOLD_HELPER_PREPARATION_DEADLINE_MS =
6837 * call after the first serves a cached binary instead of recompiling `Fold.m`, and a `DEVELOPER_DIR`
6938 * switch busts the cache instead of serving a binary built against a different SDK (#2796).
7039 *
71- * Failures surface as `AppError('COMMAND_FAILED', ..., {reason: 'fold-helper-build-failed'})`, the
72- * error shape `sendSimulatorFoldPose` reported before this cache existed.
40+ * Build and cache failures surface as `AppError('COMMAND_FAILED', ..., {reason:
41+ * 'fold-helper-build-failed'})`, the error shape `sendSimulatorFoldPose` reported before this cache
42+ * existed, carrying the underlying failure's hint and details.
7343 */
7444export async function ensureFoldHelperBinary (
7545 input : Readonly < {
@@ -90,27 +60,21 @@ export async function ensureFoldHelperBinary(
9060 deadline ,
9161 ) ;
9262 const toolchain = await readHostToolchainIdentity ( host , deadline ) ;
93- const compileArgv = buildFoldHelperCompileArgv ( { sourceRoot : '' , outputPath : '' } ) ;
94- const cacheKey = foldHelperCacheKey ( { sourceHash, toolchain, compileArgv } ) ;
9563 const cacheRoot =
9664 input . cacheRoot ?? path . join ( host . homeDirectory ( ) , '.agent-device' , 'fold-helper' ) ;
97- const manifest = {
98- schemaVersion : FOLD_HELPER_SCHEMA_VERSION ,
99- sourceHash,
100- cacheKey,
101- toolchain,
102- compileArgv,
103- } ;
10465 return await ensureNativeBuildCacheEntry ( {
10566 host,
10667 deadline,
10768 lockDescription : FOLD_HELPER_LOCK_DESCRIPTION ,
10869 cacheRoot,
109- cacheKey,
11070 binaryFilename : FOLD_HELPER_BINARY_FILENAME ,
111- manifest,
112- manifestMatches : ( candidate ) =>
113- nativeBuildManifestFieldsMatch ( candidate , manifest , MANIFEST_FIELDS ) ,
71+ keyInputs : {
72+ schemaVersion : FOLD_HELPER_SCHEMA_VERSION ,
73+ sourceHash,
74+ toolchain,
75+ // Placeholder paths keep the key independent of the install location and build directory.
76+ compileArgv : buildFoldHelperCompileArgv ( { sourceRoot : '' , outputPath : '' } ) ,
77+ } ,
11478 build : ( outputPath ) => compileFoldHelper ( host , deadline , sourceRoot , outputPath ) ,
11579 } ) ;
11680 } catch ( error ) {
@@ -165,38 +129,28 @@ async function compileFoldHelper(
165129 deadline,
166130 argv : buildFoldHelperCompileArgv ( { sourceRoot, outputPath } ) ,
167131 budgetMs : FOLD_HELPER_BUILD_TIMEOUT_MS ,
168- deadlineReason : 'fold-helper-build-deadline' ,
169132 label : 'fold helper' ,
170133 } ) ;
171134 if ( result . exitCode !== 0 || ! host . exists ( outputPath ) ) {
172- throw new AppError (
173- 'COMMAND_FAILED' ,
174- 'Unable to build the simulator fold helper' ,
175- execFailureDetails ( result , {
176- reason : 'fold-helper-build-failed' ,
177- hint : 'Select an Xcode with the iOS simulator SDK and foldable HID support using DEVELOPER_DIR.' ,
178- } ) ,
179- ) ;
135+ throw foldHelperBuildFailed ( execFailureDetails ( result ) ) ;
180136 }
181137}
182138
183139/**
184- * `AppError('COMMAND_FAILED', ..., {reason: 'fold-helper-build-failed'})` for every cache failure,
185- * matching the error `sendSimulatorFoldPose` reported before this cache existed, except a genuine
186- * cancellation: `compileFoldHelper` already throws that exact shape on a build failure, so it
187- * passes through unchanged.
140+ * Rewraps a cache failure as the fold helper's build error, keeping its hint and typed details; a
141+ * cancellation, and any error that is not a snapshot-source failure, passes through unchanged.
188142 */
189143function asFoldHelperCacheError ( error : unknown ) : unknown {
190- if ( ! ( error instanceof SnapshotSourceError ) ) return error ;
191- if ( error . failureKind === 'cancelled' ) return error ;
144+ if ( ! ( error instanceof SnapshotSourceError ) || error . failureKind === 'cancelled' ) return error ;
145+ const { bridgeFailure : _kind , bridgeFailureCode : cause , ...details } = error . details ?? { } ;
146+ return foldHelperBuildFailed ( { ...details , cause } , error ) ;
147+ }
148+
149+ function foldHelperBuildFailed ( details : Readonly < Record < string , unknown > > , cause ?: unknown ) {
192150 return new AppError (
193151 'COMMAND_FAILED' ,
194152 'Unable to build the simulator fold helper' ,
195- {
196- reason : 'fold-helper-build-failed' ,
197- hint : 'Select an Xcode with the iOS simulator SDK and foldable HID support using DEVELOPER_DIR.' ,
198- cause : error . failureCode ,
199- } ,
200- error ,
153+ { hint : FOLD_HELPER_BUILD_HINT , ...details , reason : 'fold-helper-build-failed' } ,
154+ cause ,
201155 ) ;
202156}
0 commit comments