Repository navigation
refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3777
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
thymikee
merged 27 commits into
refactor/host-environment
from
refactor/host-environment-platforms
Oct 17, 2026
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
acd5fde
refactor(capture-kit): read swift cache, app-log and PNG worker setti…
thymikee 1217c67
refactor(provision-kit): read proxy settings from the command host
thymikee 3a4c59b
refactor(react-native): read dev-server settings from the command host
thymikee 6df731e
fix(host): run the dev-server helpers inside the client's and the SDK…
thymikee 9bae094
refactor(platform-apple): give detached runner and bridge children an…
thymikee 94fbb68
refactor(platform-apple): read runner settings from the command host
thymikee b064bc6
fix(host): run the daemon clean script's runner lease cleanup inside …
thymikee 20f00ed
refactor(host): hand leaf modules their environment instead of import…
thymikee a45b6ec
test(host): bind the command host in the install-artifact suites
thymikee 52ee0a1
fix(host): answer review of the platform host environment change
thymikee 2e0e4f1
refactor(react-native): take the host explicitly instead of reading t…
thymikee 4f02fcf
refactor(platforms): pass the host explicitly where a platform packag…
thymikee 7ef7690
refactor(daemon): hand the app-log marker the request host's environment
thymikee 8bfc940
fix(capture-kit): report an unavailable PNG worker in the first diagn…
thymikee 774d705
test(host): share the test command host binding through test-kit and …
thymikee 58232c5
refactor(capture-kit): expose the app-log live handle adapter through…
thymikee 96556c0
refactor(test-kit): build the test app-log handle without importing c…
thymikee e71ad8c
refactor(capture-kit): take the host explicitly for Swift cache and r…
thymikee 8752e8c
fix(capture-kit): report a worker that dies under a request in that r…
thymikee ca49585
refactor(platform-android): take the install environment from the caller
thymikee b647f9c
test(platform-apple): pin the detached snapshot bridge's starting dir…
thymikee 1c61b40
test(capture-kit): fail when the pre-warm starts no worker to kill
thymikee 3f35707
test(host): keep the provider scenario host's Swift cache directory f…
thymikee 298babc
fix(capture-kit): key the cached Swift helper paths on the host they …
thymikee d53f78b
test(host): give the Android recording scenarios their host env throu…
thymikee 7cb529c
test(host): scenarios name their own host env; the harness no longer …
thymikee f0b20fa
test(host): drop the iOS recording scenario's process-env block now t…
thymikee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { captureHostEnvironment } from '@agent-device/host-kit/process'; | ||
| import type { HostEnvironment } from '@agent-device/kernel/source-value'; | ||
|
|
||
| /** | ||
| * The host a capture-kit test hands to code that takes one explicitly: a snapshot of the process | ||
| * as it is when called, so `vi.stubEnv` made earlier in the test reaches it. Test-kit depends on | ||
| * capture-kit, so capture-kit keeps this local. | ||
| */ | ||
| export function createTestHost(): HostEnvironment { | ||
| return captureHostEnvironment(process); | ||
| } |
74 changes: 74 additions & 0 deletions
74
packages/capture-kit/src/png-worker-client-unavailable.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| import { EventEmitter } from 'node:events'; | ||
| import { expect, test, vi } from 'vitest'; | ||
| import { PNG } from './png.ts'; | ||
|
|
||
| test('an unavailable worker is reported by the first diagnostics scope that reaches the fallback', async () => { | ||
| vi.resetModules(); | ||
| vi.doMock('./internal-entry.ts', () => ({ resolveInternalEntryModulePath: () => null })); | ||
| // Loaded after the reset so the client and the scope share one diagnostics module. | ||
| const { countDiagnosticEventsByPhase, withDiagnosticsScope } = | ||
| await import('@agent-device/host-kit/diagnostics'); | ||
| const { decodePngAsync, prewarmPngWorker } = await import('./png-worker-client.ts'); | ||
| const png = PNG.sync.write(new PNG({ width: 1, height: 1 })); | ||
|
|
||
| // The daemon pre-warms outside any request scope, where the report would be dropped. | ||
| prewarmPngWorker(); | ||
|
|
||
| const reported = await withDiagnosticsScope({ command: 'screenshot' }, async () => { | ||
| await decodePngAsync(png, 'first'); | ||
| const afterFirst = countDiagnosticEventsByPhase(['png_worker_unavailable']); | ||
| await decodePngAsync(png, 'second'); | ||
| return { afterFirst, afterSecond: countDiagnosticEventsByPhase(['png_worker_unavailable']) }; | ||
| }); | ||
| const laterScope = await withDiagnosticsScope({ command: 'screenshot' }, async () => { | ||
| await decodePngAsync(png, 'third'); | ||
| return countDiagnosticEventsByPhase(['png_worker_unavailable']); | ||
| }); | ||
|
|
||
| expect(reported).toEqual({ afterFirst: 1, afterSecond: 1 }); | ||
| expect(laterScope).toBe(0); | ||
| vi.doUnmock('./internal-entry.ts'); | ||
| }); | ||
|
|
||
| test('a worker that dies under a request is reported in that request, not only the next one', async () => { | ||
| vi.resetModules(); | ||
| class FakeWorker extends EventEmitter { | ||
| static last: FakeWorker | undefined; | ||
| constructor() { | ||
| super(); | ||
| FakeWorker.last = this; | ||
| } | ||
| postMessage(): void {} | ||
| ref(): void {} | ||
| unref(): void {} | ||
| terminate(): Promise<number> { | ||
| return Promise.resolve(0); | ||
| } | ||
| } | ||
| vi.doMock('node:worker_threads', async (importOriginal) => ({ | ||
| ...(await importOriginal<typeof import('node:worker_threads')>()), | ||
| Worker: FakeWorker, | ||
| })); | ||
| const { countDiagnosticEventsByPhase, withDiagnosticsScope } = | ||
| await import('@agent-device/host-kit/diagnostics'); | ||
| const { decodePngAsync, prewarmPngWorker } = await import('./png-worker-client.ts'); | ||
| const png = PNG.sync.write(new PNG({ width: 1, height: 1 })); | ||
| prewarmPngWorker(); | ||
|
|
||
| let jobStarted!: () => void; | ||
| const started = new Promise<void>((resolve) => (jobStarted = resolve)); | ||
| const scoped = withDiagnosticsScope({ command: 'screenshot' }, async () => { | ||
| const decoded = decodePngAsync(png, 'under-request'); | ||
| jobStarted(); | ||
| await decoded; | ||
| return countDiagnosticEventsByPhase(['png_worker_unavailable']); | ||
| }); | ||
| await started; | ||
| // The worker's own error event carries no diagnostics scope. | ||
| const worker = FakeWorker.last; | ||
| if (!worker) throw new Error('the pre-warm did not start a worker'); | ||
| worker.emit('error', new Error('worker crashed')); | ||
|
|
||
| expect(await scoped).toBe(1); | ||
| vi.doUnmock('node:worker_threads'); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
packages/capture-kit/src/recording/__tests__/overlay-script-paths.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import fs from 'node:fs'; | ||
| import path from 'node:path'; | ||
| import { expect, test, vi } from 'vitest'; | ||
| import { mkdtempForTestSync } from '../../tmp-dir.fixtures.ts'; | ||
|
|
||
| vi.mock('@agent-device/host-kit/command', async () => { | ||
| const fs = await import('node:fs'); | ||
| return { | ||
| runCmd: vi.fn(async (_cmd: string, args: string[]) => { | ||
| fs.writeFileSync(args[args.indexOf('--output') + 1]!, 'processed'); | ||
| return { stdout: '', stderr: '', exitCode: 0 }; | ||
| }), | ||
| }; | ||
| }); | ||
| vi.mock('../video.ts', () => ({ waitForPlayableVideo: vi.fn(async () => {}) })); | ||
| vi.mock('../swift-cache.ts', () => ({ | ||
| buildSwiftToolEnv: () => ({}), | ||
| compileSwiftSourceFile: vi.fn(async () => '/bin/true'), | ||
| resolveRecordingScriptPath: vi.fn( | ||
| (name: string, _projectRoot: string, cwd: string) => `${cwd}/${name}`, | ||
| ), | ||
| })); | ||
|
|
||
| import { overlayRecordingTouches, startRecordingOverlayBudget } from '../overlay.ts'; | ||
| import { compileSwiftSourceFile } from '../swift-cache.ts'; | ||
|
|
||
| test('a helper script path is resolved per host working directory, not once per process', async () => { | ||
| const root = mkdtempForTestSync('agent-device-overlay-script-paths-'); | ||
| const videoPath = path.join(root, 'recording.mp4'); | ||
| const telemetryPath = path.join(root, 'recording.gesture-telemetry.json'); | ||
| fs.writeFileSync(telemetryPath, '{"events":[]}'); | ||
| const overlay = async (cwd: string) => { | ||
| fs.writeFileSync(videoPath, 'original'); | ||
| await overlayRecordingTouches({ | ||
| host: { env: {}, cwd }, | ||
| videoPath, | ||
| telemetryPath, | ||
| deadline: startRecordingOverlayBudget(), | ||
| }); | ||
| }; | ||
|
|
||
| await overlay('/cwd/one'); | ||
| await overlay('/cwd/two'); | ||
| await overlay('/cwd/one'); | ||
|
|
||
| expect(vi.mocked(compileSwiftSourceFile).mock.calls.map(([params]) => params.sourcePath)).toEqual( | ||
| [ | ||
| '/cwd/one/recording-overlay.swift', | ||
| '/cwd/two/recording-overlay.swift', | ||
| '/cwd/one/recording-overlay.swift', | ||
| ], | ||
| ); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.