Skip to content
Merged
1 change: 1 addition & 0 deletions skills/agent-device/references/exploration.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ Common batch error categories:
- `SESSION_NOT_FOUND`: open or select the correct session, then retry.
- `UNSUPPORTED_OPERATION`: switch to a supported command or surface.
- `AMBIGUOUS_MATCH`: refine the selector or locator, then retry the failed step.
- `DEVICE_IN_USE`: the device is held by another session — close or reuse the existing session before retrying.
- `COMMAND_FAILED`: add sync guards and retry from the failing step.

## Stop conditions
Expand Down
4 changes: 1 addition & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,7 @@ export async function runCli(argv: string[], deps: CliDeps = DEFAULT_CLI_DEPS):
process.stderr.write(`\n[daemon log]\n${tail}\n`);
}
}
} catch {
// ignore
}
} catch {}
}
}
if (logTailStopper) logTailStopper();
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/apps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { buildSelectionOptions, writeCommandOutput } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const appsCommand: ClientCommandHandler = async ({ flags, client }) => {
const apps = await client.apps.list({
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/client-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { AppError } from '../../utils/errors.ts';
import { parseWaitArgs } from '../../daemon/handlers/snapshot.ts';
import { parseDeviceRotation } from '../../core/device-rotation.ts';
import { buildSelectionOptions, writeCommandMessage, writeCommandOutput } from './shared.ts';
import type { ClientCommandHandlerMap } from './router.ts';
import type { ClientCommandHandlerMap } from './router-types.ts';

export const clientCommandMethodHandlers = {
[CLIENT_COMMANDS.wait]: async ({ positionals, flags, client }) => {
Expand Down
7 changes: 2 additions & 5 deletions src/cli/commands/connection-runtime.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { resolveDaemonPaths } from '../../daemon/config.ts';
import { stopMetroTunnel } from '../../metro.ts';
import { resolveRemoteConfigProfile } from '../../remote-config.ts';
import type { MetroBridgeScope } from '../../client-metro-companion-contract.ts';
import {
buildRemoteConnectionDaemonState,
hashRemoteConfigFile,
Expand Down Expand Up @@ -173,11 +174,7 @@ export async function prepareConnectedMetro(
client: AgentDeviceClient,
remoteConfigPath: string,
session: string,
bridgeScope: {
tenantId: string;
runId: string;
leaseId: string;
},
bridgeScope: MetroBridgeScope,
): Promise<{
runtime?: SessionRuntimeHints;
cleanup?: NonNullable<RemoteConnectionState['metro']>;
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import { writeCommandOutput } from './shared.ts';
import type { LeaseBackend } from '../../contracts.ts';
import type { CliFlags } from '../../utils/command-schema.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const connectCommand: ClientCommandHandler = async ({ flags, client }) => {
if (!flags.remoteConfig) {
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/devices.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { serializeDevice } from '../../client-shared.ts';
import type { AgentDeviceDevice } from '../../client.ts';
import { buildSelectionOptions, writeCommandOutput } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const devicesCommand: ClientCommandHandler = async ({ flags, client }) => {
const devices = await client.devices.list(buildSelectionOptions(flags));
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/ensure-simulator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AppError } from '../../utils/errors.ts';
import { serializeEnsureSimulatorResult } from '../../client-shared.ts';
import { writeCommandOutput } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const ensureSimulatorCommand: ClientCommandHandler = async ({ flags, client }) => {
if (!flags.device) {
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { AppError } from '../../utils/errors.ts';
import type { CliFlags } from '../../utils/command-schema.ts';
import { buildSelectionOptions } from './shared.ts';
import { writeCommandCliOutput } from './output.ts';
import type { ClientCommandHandler, ClientCommandHandlerMap } from './router.ts';
import type { ClientCommandHandler, ClientCommandHandlerMap } from './router-types.ts';

type GenericClientCommandRunner = (params: {
client: AgentDeviceClient;
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { serializeDeployResult, serializeInstallFromSourceResult } from '../../c
import type { CliFlags } from '../../utils/command-schema.ts';
import type { AgentDeviceClient, AppDeployResult } from '../../client.ts';
import { buildSelectionOptions, writeCommandMessage } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const installCommand: ClientCommandHandler = async ({ positionals, flags, client }) => {
const result = await runDeployCommand('install', positionals, flags, client);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/metro.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AppError } from '../../utils/errors.ts';
import { writeCommandOutput } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const metroCommand: ClientCommandHandler = async ({ positionals, flags, client }) => {
const action = (positionals[0] ?? '').toLowerCase();
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/open.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { serializeCloseResult, serializeOpenResult } from '../../client-shared.ts';
import { buildSelectionOptions, writeCommandMessage } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const openCommand: ClientCommandHandler = async ({ positionals, flags, client }) => {
const result = await client.apps.open({
Expand Down
11 changes: 11 additions & 0 deletions src/cli/commands/router-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { CliFlags } from '../../utils/command-schema.ts';
import type { AgentDeviceClient } from '../../client.ts';

export type ClientCommandParams = {
positionals: string[];
flags: CliFlags;
client: AgentDeviceClient;
};

export type ClientCommandHandler = (params: ClientCommandParams) => Promise<boolean>;
export type ClientCommandHandlerMap = Partial<Record<string, ClientCommandHandler>>;
17 changes: 9 additions & 8 deletions src/cli/commands/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ import { snapshotCommand } from './snapshot.ts';
import { screenshotCommand, diffCommand } from './screenshot.ts';
import { clientCommandMethodHandlers } from './client-command.ts';
import { genericClientCommandHandlers } from './generic.ts';
import type {
ClientCommandHandler,
ClientCommandHandlerMap,
} from './router-types.ts';

export type ClientCommandParams = {
positionals: string[];
flags: CliFlags;
client: AgentDeviceClient;
};

export type ClientCommandHandler = (params: ClientCommandParams) => Promise<boolean>;
export type ClientCommandHandlerMap = Partial<Record<string, ClientCommandHandler>>;
export type {
ClientCommandHandler,
ClientCommandHandlerMap,
ClientCommandParams,
} from './router-types.ts';

const dedicatedClientApiHandlers = {
session: sessionCommand,
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { createLocalArtifactAdapter } from '../../io.ts';
import { createAgentDevice, localCommandPolicy } from '../../runtime.ts';
import type { CliFlags } from '../../utils/command-schema.ts';
import { buildSelectionOptions, writeCommandOutput } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const screenshotCommand: ClientCommandHandler = async ({ positionals, flags, client }) => {
const result = await client.capture.screenshot({
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/session.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AppError } from '../../utils/errors.ts';
import { serializeSessionListEntry } from '../../client-shared.ts';
import { writeCommandOutput } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const sessionCommand: ClientCommandHandler = async ({ positionals, flags, client }) => {
const sub = positionals[0] ?? 'list';
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/snapshot.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { formatSnapshotText } from '../../utils/output.ts';
import { serializeSnapshotResult } from '../../client-shared.ts';
import { buildSelectionOptions, writeCommandOutput } from './shared.ts';
import type { ClientCommandHandler } from './router.ts';
import type { ClientCommandHandler } from './router-types.ts';

export const snapshotCommand: ClientCommandHandler = async ({ flags, client }) => {
const result = await client.capture.snapshot({
Expand Down
9 changes: 5 additions & 4 deletions src/client-metro-companion-contract.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { MetroBridgeScope } from './client-metro.ts';

export const METRO_COMPANION_RUN_ARG = '--agent-device-run-metro-companion';
export const METRO_COMPANION_RECONNECT_DELAY_MS = 1_000;
export const METRO_COMPANION_LEASE_CHECK_INTERVAL_MS = 250;
Expand All @@ -14,8 +12,11 @@ export const ENV_SCOPE_TENANT_ID = 'AGENT_DEVICE_METRO_COMPANION_SCOPE_TENANT_ID
export const ENV_SCOPE_RUN_ID = 'AGENT_DEVICE_METRO_COMPANION_SCOPE_RUN_ID';
export const ENV_SCOPE_LEASE_ID = 'AGENT_DEVICE_METRO_COMPANION_SCOPE_LEASE_ID';

export type { MetroTunnelRequestMessage as MetroCompanionRequest } from './metro.ts';
export type { MetroBridgeScope };
export type MetroBridgeScope = {
tenantId: string;
runId: string;
leaseId: string;
};

export type CompanionOptions = {
serverBaseUrl: string;
Expand Down
8 changes: 6 additions & 2 deletions src/client-metro-companion-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import {
METRO_COMPANION_RUN_ARG,
WS_READY_STATE_OPEN,
} from './client-metro-companion-contract.ts';
import type { CompanionOptions, MetroCompanionRequest } from './client-metro-companion-contract.ts';
import type { CompanionOptions } from './client-metro-companion-contract.ts';
import type {
MetroTunnelRequestMessage as MetroCompanionRequest,
MetroTunnelResponseMessage,
} from './metro.ts';
import { normalizeBaseUrl } from './utils/url.ts';

function createHeaders(serverBaseUrl: string, token: string): Record<string, string> {
Expand Down Expand Up @@ -87,7 +91,7 @@ function normalizeOutgoingCloseCode(code: number): number {
return 3001;
}

function sendJson(socket: WebSocket, payload: object): void {
function sendJson(socket: WebSocket, payload: MetroTunnelResponseMessage): void {
if (socket.readyState !== WS_READY_STATE_OPEN) return;
socket.send(JSON.stringify(payload));
}
Expand Down
12 changes: 5 additions & 7 deletions src/client-metro.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import fs from 'node:fs';
import path from 'node:path';
import { sleep } from './utils/timeouts.ts';
import { ensureMetroCompanion } from './client-metro-companion.ts';
import type { MetroBridgeScope } from './client-metro-companion-contract.ts';
import type {
MetroBridgeDescriptor,
MetroBridgeResult,
MetroBridgeRuntimePayload,
MetroRuntimeHints,
} from './metro.ts';
} from './metro-types.ts';
import { AppError } from './utils/errors.ts';
import { runCmdSync, runCmdDetached } from './utils/exec.ts';
import { resolveUserPath } from './utils/path-resolution.ts';
Expand All @@ -20,11 +22,7 @@ export type MetroPrepareKind = 'auto' | 'react-native' | 'expo';
type ResolvedMetroKind = Exclude<MetroPrepareKind, 'auto'>;
type EnvSource = NodeJS.ProcessEnv | Record<string, string | undefined>;

export type MetroBridgeScope = {
tenantId: string;
runId: string;
leaseId: string;
};
export type { MetroBridgeScope };

type PackageJsonShape = {
dependencies?: Record<string, string>;
Expand Down Expand Up @@ -218,7 +216,7 @@ function installDependenciesIfNeeded(
}

async function wait(ms: number): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, ms));
await sleep(ms);
}

async function fetchText(
Expand Down
12 changes: 5 additions & 7 deletions src/client-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import type {
SessionRuntimeHints,
} from './contracts.ts';
import type { DeviceKind, DeviceTarget, Platform, PlatformSelector } from './utils/device.ts';
import type { FindLocator } from './utils/finders.ts';
import type { ScreenshotOverlayRef, SnapshotNode, SnapshotVisibility } from './utils/snapshot.ts';
import type { MetroPrepareKind, PrepareMetroRuntimeResult } from './client-metro.ts';
import type { MetroBridgeScope } from './client-metro-companion-contract.ts';

export type { FindLocator } from './utils/finders.ts';

type DaemonTransportMode = 'auto' | 'socket' | 'http';
type DaemonServerMode = 'socket' | 'http' | 'dual';
Expand Down Expand Up @@ -267,11 +271,7 @@ export type MetroPrepareOptions = {
publicBaseUrl: string;
proxyBaseUrl?: string;
bearerToken?: string;
bridgeScope?: {
tenantId: string;
runId: string;
leaseId: string;
};
bridgeScope?: MetroBridgeScope;
launchUrl?: string;
companionProfileKey?: string;
companionConsumerKey?: string;
Expand Down Expand Up @@ -594,8 +594,6 @@ type IsStatePredicateOptions = ClientCommandBaseOptions &

export type IsOptions = IsTextPredicateOptions | IsStatePredicateOptions;

export type FindLocator = 'any' | 'text' | 'label' | 'value' | 'role' | 'id';

type FindBaseOptions = ClientCommandBaseOptions &
FindSnapshotCommandOptions & {
locator?: FindLocator;
Expand Down
4 changes: 2 additions & 2 deletions src/commands/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import type {
BackendInstallResult,
BackendInstallSource,
} from '../backend.ts';
import type { AgentDeviceRuntime, CommandContext } from '../runtime.ts';
import type { AgentDeviceRuntime, CommandContext } from '../runtime-contract.ts';
import { AppError } from '../utils/errors.ts';
import { successText } from '../utils/success-text.ts';
import type { RuntimeCommand } from './index.ts';
import type { RuntimeCommand } from './runtime-types.ts';
import { resolveCommandInput } from './io-policy.ts';
import { toBackendContext } from './selector-read-utils.ts';

Expand Down
4 changes: 2 additions & 2 deletions src/commands/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import type {
BackendPushInput,
} from '../backend.ts';
import type { FileInputRef } from '../io.ts';
import type { AgentDeviceRuntime, CommandContext } from '../runtime.ts';
import type { AgentDeviceRuntime, CommandContext } from '../runtime-contract.ts';
import { AppError } from '../utils/errors.ts';
import { successText } from '../utils/success-text.ts';
import { resolveCommandInput } from './io-policy.ts';
import type { RuntimeCommand } from './index.ts';
import type { RuntimeCommand } from './runtime-types.ts';

const APP_EVENT_NAME_PATTERN = /^[A-Za-z0-9_.:-]{1,64}$/;
const MAX_APP_EVENT_PAYLOAD_BYTES = 8 * 1024;
Expand Down
4 changes: 2 additions & 2 deletions src/commands/capture-diff-screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import type {
ReservedOutputFile,
ResolvedInputFile,
} from '../io.ts';
import type { AgentDeviceRuntime, CommandContext } from '../runtime.ts';
import type { AgentDeviceRuntime, CommandContext } from '../runtime-contract.ts';
import { AppError } from '../utils/errors.ts';
import { compareScreenshots, type ScreenshotDiffResult } from '../utils/screenshot-diff.ts';
import { attachCurrentOverlayMatches } from '../utils/screenshot-diff-overlay-matches.ts';
import type { RuntimeCommand } from './index.ts';
import type { RuntimeCommand } from './runtime-types.ts';
import { createCommandTempFile, reserveCommandOutput, resolveCommandInput } from './io-policy.ts';

export type LiveScreenshotInputRef = {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/capture-screenshot.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AppError } from '../utils/errors.ts';
import { successText } from '../utils/success-text.ts';
import type { ArtifactDescriptor } from '../io.ts';
import type { RuntimeCommand, ScreenshotCommandOptions } from './index.ts';
import type { RuntimeCommand, ScreenshotCommandOptions } from './runtime-types.ts';
import { reserveCommandOutput } from './io-policy.ts';

export type ScreenshotCommandResult = {
Expand Down
18 changes: 5 additions & 13 deletions src/commands/capture-snapshot.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import type { BackendSnapshotResult } from '../backend.ts';
import type { AgentDeviceRuntime, CommandSessionRecord } from '../runtime.ts';
import type { AgentDeviceRuntime, CommandSessionRecord } from '../runtime-contract.ts';
import { AppError } from '../utils/errors.ts';
import { buildSnapshotDiff, countSnapshotComparableLines } from '../utils/snapshot-diff.ts';
import type { SnapshotDiffLine, SnapshotDiffSummary } from '../utils/snapshot-diff.ts';
import type { SnapshotNode, SnapshotState, SnapshotVisibility } from '../utils/snapshot.ts';
import { buildSnapshotVisibility } from '../utils/snapshot-visibility.ts';
import type {
DiffSnapshotCommandOptions,
RuntimeCommand,
SnapshotCommandOptions,
} from './index.ts';
} from './runtime-types.ts';
import { now } from './selector-read-utils.ts';

export type { SnapshotDiffLine, SnapshotDiffSummary } from '../utils/snapshot-diff.ts';

export type SnapshotCommandResult = {
nodes: SnapshotNode[];
truncated: boolean;
Expand All @@ -20,17 +23,6 @@ export type SnapshotCommandResult = {
warnings?: string[];
};

export type SnapshotDiffLine = {
kind: 'added' | 'removed' | 'unchanged';
text: string;
};

export type SnapshotDiffSummary = {
additions: number;
removals: number;
unchanged: number;
};

export type DiffSnapshotCommandResult = {
mode: 'snapshot';
baselineInitialized: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/diagnostics-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
DiagnosticsLogsCommandResult,
DiagnosticsNetworkCommandResult,
DiagnosticsPerfCommandResult,
} from './diagnostics.ts';
} from './diagnostics-types.ts';

const PAYLOAD_MAX_CHARS = 2048;
const MESSAGE_MAX_CHARS = 4096;
Expand Down
Loading
Loading