Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,154 @@
"truncateUtf8Field",
"isRecordingExportQuality"
]
},
{
"comment": "HarmonyOS package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its selected host route explicit here.",
"file": "packages/platform-harmonyos/src/hdc.ts",
"exports": ["ensureHarmonyToolchainPathConfigured"]
},
{
"comment": "HarmonyOS package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its selected lifecycle route explicit here.",
"file": "packages/platform-harmonyos/src/app-lifecycle.ts",
"exports": ["listHarmonyApps", "openHarmonyApp", "closeHarmonyApp"]
},
{
"comment": "HarmonyOS archive metadata parsing is exercised by package-owned tests but has no production caller; keep the direct test seam explicit while the implementation remains colocated with app lifecycle mechanics.",
"file": "packages/platform-harmonyos/src/app-lifecycle.ts",
"exports": ["resolveHarmonyArchiveBundleName"]
},
{
"comment": "HarmonyOS package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its selected diagnostics route explicit here.",
"file": "packages/platform-harmonyos/src/doctor.ts",
"exports": ["harmonyToolchainCheck"]
},
{
"comment": "HarmonyOS package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its selected interaction route explicit here.",
"file": "packages/platform-harmonyos/src/input-actions.ts",
"exports": [
"pressHarmony",
"doubleClickHarmony",
"longPressHarmony",
"typeHarmony",
"fillHarmony",
"scrollHarmony",
"performHarmonyGesture",
"homeHarmony",
"appSwitcherHarmony",
"pressHarmonyKeyboardKey",
"setHarmonyOrientation"
]
},
{
"comment": "HarmonyOS package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-harmonyos/src/perf.ts",
"exports": ["sampleHarmonyMemoryPerf"]
},
{
"comment": "HarmonyOS package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-harmonyos/src/screenshot.ts",
"exports": ["screenshotHarmony"]
},
{
"comment": "HarmonyOS package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-harmonyos/src/settings.ts",
"exports": ["setHarmonySetting"]
},
{
"comment": "HarmonyOS package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its selected host route explicit here.",
"file": "packages/platform-harmonyos/src/index.ts",
"exports": ["sampleHarmonyMemoryPerf"]
},
{
"comment": "Linux package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its selected interactor/snapshot routes explicit here.",
"file": "packages/platform-linux/src/app-lifecycle.ts",
"exports": ["openLinuxApp", "closeLinuxApp", "backLinux", "homeLinux"]
},
{
"comment": "Linux package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its selected interactor route explicit here.",
"file": "packages/platform-linux/src/input-actions.ts",
"exports": [
"rightClickLinux",
"middleClickLinux",
"doubleClickLinux",
"longPressLinux",
"focusLinux",
"swipeLinux"
]
},
{
"comment": "Linux package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-linux/src/screenshot.ts",
"exports": ["screenshotLinux"]
},
{
"comment": "Linux package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-linux/src/snapshot.ts",
"exports": ["readLinuxTextAtPoint"]
},
{
"comment": "Linux package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis.",
"file": "packages/platform-linux/src/clipboard.ts",
"exports": ["readLinuxClipboard", "writeLinuxClipboard"]
},
{
"comment": "Linux package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis.",
"file": "packages/platform-linux/src/input-actions.ts",
"exports": ["scrollLinux", "fillLinux"]
},
{
"comment": "Linux package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-linux/src/surface-snapshot.ts",
"exports": ["captureLinuxSurfaceSnapshot"]
},
{
"comment": "Linux package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-linux/src/index.ts",
"exports": ["createLocalLinuxToolProvider"]
},
{
"comment": "Vega package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-vega/src/doctor.ts",
"exports": ["vegaToolchainCheck"]
},
{
"comment": "Web package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its managed-backend/provider routes explicit here.",
"file": "packages/platform-web/src/agent-browser-tool.ts",
"exports": ["doctorManagedAgentBrowser"]
},
{
"comment": "Web package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-web/src/agent-browser-tool.ts",
"exports": ["setupManagedAgentBrowser"]
},
{
"comment": "Web package façade loader is a named external seam. Its dynamic property read is not visible to --production analysis.",
"file": "packages/platform-web/src/doctor.ts",
"exports": ["webBrowserLifecycleCheck"]
},
{
"comment": "Web package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, so keep the façade and its request-scoped provider routes explicit here.",
"file": "packages/platform-web/src/provider.ts",
"exports": ["resolveWebProvider", "hasScopedWebProvider"]
},
{
"comment": "Web package façade loaders are named external seams. Their dynamic property reads are not visible to --production analysis, and the timeout is pinned by the daemon teardown test.",
"file": "packages/platform-web/src/index.ts",
"exports": [
"AGENT_BROWSER_TIMEOUT_MS",
"inspectManagedAgentBrowserProcesses",
"summarizeAgentBrowserProcesses"
]
},
{
"comment": "Web process matching helpers are retained for package-owned lifecycle tests; production export analysis does not traverse those test consumers.",
"file": "packages/platform-web/src/agent-browser-lifecycle.ts",
"exports": [
"agentBrowserChromeLaunchMarker",
"matchAgentBrowserChromeProcess",
"summarizeAgentBrowserProcesses",
"summarizeManagedAgentBrowserProcesses"
]
}
],
"usedClassMembers": [
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ on:
- 'packages/*/src/**/*.test.ts'
- 'packages/*/src/**/__tests__/**/*.ts'
- 'src/platforms/android/**'
- 'src/platforms/harmonyos/**'
- 'src/platforms/linux/**'
- 'src/platforms/vega/**'
- 'src/platforms/web/**'
- 'packages/platform-android/src/**'
- 'packages/platform-harmonyos/src/**'
- 'packages/platform-linux/src/**'
Expand Down
8 changes: 4 additions & 4 deletions fallow-baselines/health.json
Original file line number Diff line number Diff line change
Expand Up @@ -400,12 +400,12 @@
"count": 1
}
},
"src/platforms/linux/atspi-bridge.ts": {
"packages/platform-linux/src/atspi-bridge.ts": {
"crap_moderate": {
"count": 1
}
},
"src/platforms/linux/input-actions.ts": {
"packages/platform-linux/src/input-actions.ts": {
"complexity_moderate": {
"count": 1
}
Expand Down Expand Up @@ -591,13 +591,13 @@
"src/platforms/apple/core/perf-xml.ts:high impact",
"src/daemon/daemon-process.ts:high impact",
"src/utils/screenshot-result.ts:high impact",
"src/platforms/web/json-utils.ts:high impact",
"packages/platform-web/src/json-utils.ts:high impact",
"src/platforms/android/settings.ts:complexity",
"src/utils/text-surface.ts:high impact",
"src/daemon/handlers/session-test-sharding.ts:high impact",
"src/daemon/handlers/session-replay-runtime.ts:complexity",
"src/platforms/apple/core/debug-symbols/utils.ts:high impact",
"src/platforms/linux/snapshot.ts:high impact",
"packages/platform-linux/src/snapshot.ts:high impact",
"src/core/interaction-targeting.ts:high impact",
"packages/maestro/src/internal/runtime-targets.ts:high impact",
"src/utils/source-value.ts:high impact",
Expand Down
4 changes: 4 additions & 0 deletions packages/host-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
"types": "./src/file.ts",
"default": "./src/file.ts"
},
"./host-file": {
"types": "./src/host-file.ts",
"default": "./src/host-file.ts"
},
"./process": {
"types": "./src/process.ts",
"default": "./src/process.ts"
Expand Down
18 changes: 18 additions & 0 deletions packages/host-kit/src/host-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export {
createHostDirectoryLinkSync,
ensureHostDirectorySync,
hostFileExistsSync,
hostFileLstatSync,
hostFileStatSync,
hostHomeDirectory,
hostTemporaryDirectory,
makeHostTemporaryDirectory,
readHostBinaryFile,
readHostDirectorySync,
readHostSymbolicLinkSync,
readHostTextFile,
readHostTextFileSync,
removeHostDirectory,
removeHostFileSync,
writeHostTextFileSync,
} from './internal/host-file.ts';
68 changes: 68 additions & 0 deletions packages/host-kit/src/internal/host-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';

export function hostHomeDirectory(): string {
return os.homedir();
}

export function hostTemporaryDirectory(): string {
return os.tmpdir();
}

export async function makeHostTemporaryDirectory(prefix: string): Promise<string> {
return await fsPromises.mkdtemp(path.join(hostTemporaryDirectory(), prefix));
}

export async function readHostTextFile(filePath: string): Promise<string> {
return await fsPromises.readFile(filePath, 'utf8');
}

export async function readHostBinaryFile(filePath: string): Promise<Buffer> {
return await fsPromises.readFile(filePath);
}

export async function removeHostDirectory(directoryPath: string): Promise<void> {
await fsPromises.rm(directoryPath, { recursive: true, force: true });
}

export function ensureHostDirectorySync(directoryPath: string): void {
fs.mkdirSync(directoryPath, { recursive: true });
}

export function readHostTextFileSync(filePath: string): string {
return fs.readFileSync(filePath, 'utf8');
}

export function writeHostTextFileSync(filePath: string, contents: string): void {
fs.writeFileSync(filePath, contents, 'utf8');
}

export function hostFileExistsSync(filePath: string): boolean {
return fs.existsSync(filePath);
}

export function hostFileStatSync(filePath: string): fs.Stats {
return fs.statSync(filePath);
}

export function hostFileLstatSync(filePath: string): fs.Stats {
return fs.lstatSync(filePath);
}

export function readHostSymbolicLinkSync(filePath: string): string {
return fs.readlinkSync(filePath);
}

export function removeHostFileSync(filePath: string): void {
fs.unlinkSync(filePath);
}

export function createHostDirectoryLinkSync(targetPath: string, linkPath: string): void {
fs.symlinkSync(targetPath, linkPath, 'dir');
}

export function readHostDirectorySync(directoryPath: string): string[] {
return fs.readdirSync(directoryPath);
}
24 changes: 24 additions & 0 deletions packages/host-kit/src/internal/host-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ type HostProcessRunCommand = (
options: ExecOptions,
) => Promise<ExecResult>;

export function hostEnvironment(): NodeJS.ProcessEnv {
return process.env;
}

export function hostCurrentWorkingDirectory(): string {
return process.cwd();
}

export function hostNodeExecutablePath(): string {
return process.execPath;
}

export function hostNodeVersion(): string {
return process.version;
}

export function hostPlatform(): NodeJS.Platform {
return process.platform;
}

export function hostProcessId(): number {
return process.pid;
}

export type ListHostProcessesOptions = {
timeoutMs: number;
runCommand?: HostProcessRunCommand;
Expand Down
6 changes: 6 additions & 0 deletions packages/host-kit/src/process.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export {
expandProcessTree,
hostCurrentWorkingDirectory,
hostEnvironment,
hostNodeExecutablePath,
hostNodeVersion,
hostPlatform,
hostProcessId,
type HostProcessIdentityObservation,
type HostProcessInfo,
isProcessAlive,
Expand Down
4 changes: 3 additions & 1 deletion packages/platform-harmonyos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"dependencies": {
"@agent-device/capture-kit": "workspace:*",
"@agent-device/contracts": "workspace:*",
"@agent-device/kernel": "workspace:*"
"@agent-device/host-kit": "workspace:*",
"@agent-device/kernel": "workspace:*",
"@agent-device/provision-kit": "workspace:*"
},
"exports": {
".": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ const { readFile, runHarmonyHdc } = vi.hoisted(() => ({
runHarmonyHdc: vi.fn(),
}));

vi.mock('node:fs', () => ({ promises: { readFile } }));
vi.mock('@agent-device/host-kit/host-file', async (importOriginal) => ({
...(await importOriginal<typeof import('@agent-device/host-kit/host-file')>()),
readHostBinaryFile: readFile,
}));
vi.mock('../hdc.ts', () => ({ runHarmonyHdc }));

import { screenshotHarmony } from '../screenshot.ts';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { DeviceInfo } from '@agent-device/kernel/device';
import { promises as fs } from 'node:fs';
import path from 'node:path';
import { runCmd, type ExecOptions, type ExecResult } from '@agent-device/host-kit/command';
import {
isExecutablePath,
runCmd,
type ExecOptions,
type ExecResult,
} from '@agent-device/host-kit/command';
import { hostEnvironment } from '@agent-device/host-kit/process';

export type HarmonyHdcOptions = Pick<
ExecOptions,
Expand All @@ -27,7 +32,7 @@ export async function runHarmonyHdc(
* Honor the documented roots so the daemon sees the same HDC binary as a shell.
*/
export async function ensureHarmonyToolchainPathConfigured(
env: NodeJS.ProcessEnv = process.env,
env: NodeJS.ProcessEnv = hostEnvironment(),
): Promise<void> {
const toolchainRoots = [
env.HDC_SDK_PATH,
Expand All @@ -40,11 +45,8 @@ export async function ensureHarmonyToolchainPathConfigured(
].filter((value): value is string => Boolean(value?.trim()));
const executableRoots: string[] = [];
for (const root of toolchainRoots) {
try {
await fs.access(path.join(root, 'hdc'), fs.constants.X_OK);
if (await isExecutablePath(path.join(root, 'hdc'))) {
executableRoots.push(root);
} catch {
// Continue through explicit alternatives; the final missing-tool error names recovery.
}
}
if (executableRoots.length === 0) return;
Expand Down
Loading
Loading