Skip to content

Commit 2afe7f9

Browse files
committed
refactor: remove next-major compatibility surfaces
1 parent 494f1c5 commit 2afe7f9

40 files changed

Lines changed: 367 additions & 851 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Breaking (0.21): removed aggregate performance compatibility (`perf`, `perf sample`, `perf metrics`, the `metrics` alias, optionless `client.observability.perf()`, and SDK `area: 'metrics'`). Use `perf frames`, `perf memory sample`, `perf cpu profile start|stop|report`, or `perf trace start|stop`; removed CLI and raw daemon forms fail with this migration guidance.
6+
- Breaking (0.21): removed legacy batch JSON steps with `positionals`/`flags`. Use `{"command":"...","input":{...}}`; rejected steps now include a concrete structured example.
7+
- Breaking (0.21): removed the deprecated Node client `command.rotate` wrapper and its `RotateCommandOptions` / `RotateCommandResult` exports. Use `command.orientation`; the already-removed CLI `rotate` form keeps its targeted migration error.
58
- Security (MCP/AI-SDK tool surface): the operator-owned endpoint and path inputs — `daemonBaseUrl`,
69
the Metro `proxyBaseUrl`, `stateDir`, `cwd`, `iosSimulatorDeviceSet`, `iosXctestrunFile`,
710
`iosXctestDerivedDataPath`, `iosXctestEnvDir` — follow the credential inputs off the

packages/contracts/src/client-observability.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@ import type { NetworkIncludeMode } from '@agent-device/kernel/contracts';
88
import type { AgentDeviceRequestOverrides, DeviceCommandBaseOptions } from './client-connection.ts';
99

1010
export type PerfOptions = DeviceCommandBaseOptions & {
11-
/**
12-
* Select focused performance evidence. Omitted values retain the deprecated aggregate
13-
* metrics response for compatibility; prefer an explicit area.
14-
*/
15-
area?: PerfArea;
11+
/** Select focused performance evidence. */
12+
area: PerfArea;
1613
subject?: PerfSubject;
1714
action?: PerfAction;
1815
kind?: PerfKind;

packages/contracts/src/facades/interaction.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ export type {
144144
BackCommandResult,
145145
HomeCommandResult,
146146
OrientationCommandResult,
147-
RotateCommandResult,
148147
TvRemoteCommandResult,
149148
} from '../navigation.ts';
150149
export {

packages/contracts/src/facades/observability.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export type { NetworkEntry } from '../network-log.ts';
2929
export {
3030
PERF_ACTION_ERROR_MESSAGE,
3131
PERF_ACTION_VALUES,
32+
PERF_AGGREGATE_ALIAS,
33+
PERF_AGGREGATE_REMOVED_ERROR_MESSAGE,
3234
PERF_AREA_ERROR_MESSAGE,
3335
PERF_AREA_VALUES,
3436
PERF_KIND_ERROR_MESSAGE,
@@ -40,6 +42,7 @@ export {
4042
isPerfArea,
4143
isPerfKind,
4244
isPerfMemoryKind,
45+
isRemovedAggregatePerfToken,
4346
isPerfSubject,
4447
} from '../perf.ts';
4548
export type { PerfAction, PerfArea, PerfKind, PerfSubject } from '../perf.ts';

packages/contracts/src/navigation.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,6 @@ export type OrientationCommandResult = {
3939
message: string;
4040
};
4141

42-
/**
43-
* @deprecated The `rotate` command was renamed to `orientation`. This is the
44-
* legacy response contract (`action: 'rotate'`) that shipped in v0.18/v0.19,
45-
* retained for existing SDK consumers until the next major version. New code
46-
* should use {@link OrientationCommandResult}.
47-
*/
48-
export type RotateCommandResult = {
49-
action: 'rotate';
50-
orientation: DeviceRotation;
51-
message: string;
52-
};
53-
5442
/** `app-switcher` — `{ action: 'app-switcher', message: 'Opened app switcher' }`. */
5543
export type AppSwitcherCommandResult = {
5644
action: 'app-switcher';

packages/contracts/src/perf.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineStringEnum } from './string-enum.ts';
22

3-
export const PERF_AREA_VALUES = ['metrics', 'frames', 'memory', 'cpu', 'trace'] as const;
3+
export const PERF_AREA_VALUES = ['frames', 'memory', 'cpu', 'trace'] as const;
4+
export const PERF_AGGREGATE_ALIAS = 'metrics';
45
export const PERF_ACTION_VALUES = ['sample', 'snapshot', 'start', 'stop', 'report'] as const;
56
export const PERF_SUBJECT_VALUES = ['profile'] as const;
67
export const PERF_KIND_VALUES = [
@@ -22,9 +23,11 @@ export type PerfAction = (typeof PERF_ACTION_VALUES)[number];
2223
export type PerfSubject = (typeof PERF_SUBJECT_VALUES)[number];
2324
export type PerfKind = (typeof PERF_KIND_VALUES)[number];
2425

25-
export const PERF_AREA_ERROR_MESSAGE = 'perf area must be metrics, frames, memory, cpu, or trace';
26+
export const PERF_AREA_ERROR_MESSAGE = 'perf area must be frames, memory, cpu, or trace';
2627
export const PERF_ACTION_ERROR_MESSAGE =
2728
'perf action must be sample, snapshot, start, stop, or report';
29+
export const PERF_AGGREGATE_REMOVED_ERROR_MESSAGE =
30+
'Aggregate perf was removed. Use perf frames, perf memory sample, perf cpu profile start|stop|report, or perf trace start|stop.';
2831
export const PERF_SUBJECT_ERROR_MESSAGE = 'perf cpu requires profile';
2932
export const PERF_KIND_ERROR_MESSAGE =
3033
'perf --kind must be xctrace, simpleperf, perfetto, android-hprof, or memgraph';
@@ -40,3 +43,10 @@ export const isPerfSubject = PERF_SUBJECTS.is;
4043
export const isPerfKind = PERF_KINDS.is;
4144

4245
export const isPerfMemoryKind = PERF_MEMORY_KINDS.is;
46+
47+
export function isRemovedAggregatePerfToken(value: unknown): boolean {
48+
if (value == null) return true;
49+
if (typeof value !== 'string') return false;
50+
const normalized = value.toLowerCase();
51+
return normalized === PERF_AGGREGATE_ALIAS || isPerfAction(normalized);
52+
}

scripts/__tests__/test-file-size-ratchet.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const PINNED_TEST_FILE_LINES: Readonly<Record<string, number>> = Object.freeze({
4242
'src/utils/__tests__/output.test.ts': 1861,
4343
'src/platforms/android/__tests__/snapshot.test.ts': 1445,
4444
'src/platforms/apple/core/__tests__/runner-client.test.ts': 1615,
45-
'src/__tests__/client.test.ts': 1598,
45+
'src/__tests__/client.test.ts': 1592,
4646
'test/integration/provider-scenarios/android-lifecycle.test.ts': 1559,
4747
'src/utils/__tests__/daemon-client-lifecycle.test.ts': 1414,
4848
'src/platforms/apple/core/__tests__/runner-command-retry.test.ts': 1327,

scripts/layering/bin-alias-fast-path.test.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ const REGISTRY_FIXTURE = `
4343
import type { CliFlags } from '@agent-device/contracts/command';
4444
const CLI_COMMAND_ALIASES = [
4545
{ alias: 'long-press', command: 'longpress' },
46-
{ alias: 'metrics', command: 'perf' },
4746
{ alias: 'tap', command: 'press' },
4847
{ alias: 'launch', command: 'open' },
4948
{ alias: 'relaunch', command: 'open', impliedFlags: ['relaunch'] },
@@ -55,7 +54,6 @@ test('registryAliasTokens reads every alias property value out of the registry s
5554
assert.deepEqual(registryAliasTokens(REGISTRY_FIXTURE), [
5655
'launch',
5756
'long-press',
58-
'metrics',
5957
'relaunch',
6058
'tap',
6159
]);
@@ -269,17 +267,16 @@ const commandHelp = buildCommandUsageText(normalizeCliCommandAlias(argv[1]));
269267
});
270268

271269
test('localAliasLiterals reports every requested token present as a string literal', () => {
272-
// The pre-fix bin.ts shape: a hand-written table re-declaring two of the five tokens.
270+
// A stale bin.ts shape with a hand-written alias table.
273271
const preFixBinSource = `
274272
function normalizeHelpTarget(command) {
275273
if (command === 'long-press') return 'longpress';
276-
if (command === 'metrics') return 'perf';
277274
return command;
278275
}
279276
`;
280277
assert.deepEqual(
281-
localAliasLiterals(preFixBinSource, ['long-press', 'metrics', 'tap', 'launch', 'relaunch']),
282-
['long-press', 'metrics'],
278+
localAliasLiterals(preFixBinSource, ['long-press', 'tap', 'launch', 'relaunch']),
279+
['long-press'],
283280
);
284281
});
285282

@@ -294,7 +291,7 @@ import { normalizeCliCommandAlias } from './commands/cli-command-aliases.ts';
294291
const commandHelp = buildCommandUsageText(normalizeCliCommandAlias(helpTarget));
295292
`;
296293
assert.deepEqual(
297-
localAliasLiterals(fixedBinSource, ['long-press', 'metrics', 'tap', 'launch', 'relaunch']),
294+
localAliasLiterals(fixedBinSource, ['long-press', 'tap', 'launch', 'relaunch']),
298295
[],
299296
);
300297
});
@@ -305,7 +302,7 @@ test('the real tree imports the resolver, calls it into buildCommandUsageText, h
305302
const registrySource = readFileSync(path.join(repoRoot, ALIAS_REGISTRY_FILE), 'utf8');
306303
const binSource = readFileSync(path.join(repoRoot, BIN_FILE), 'utf8');
307304
const tokens = registryAliasTokens(registrySource);
308-
assert.deepEqual(tokens, ['launch', 'long-press', 'metrics', 'relaunch', 'tap']);
305+
assert.deepEqual(tokens, ['launch', 'long-press', 'relaunch', 'tap']);
309306
const localName = aliasResolverLocalName(binSource);
310307
assert.equal(localName, 'normalizeCliCommandAlias');
311308
assert.equal(helpTargetBindingName(binSource), 'helpTarget');

scripts/layering/bin-alias-fast-path.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// `bin.ts`'s `--help` fast path resolves a command alias (`tap`, `launch`, …) to its canonical
44
// command before looking up static help text. #1618-adjacent: bin.ts once carried its own
5-
// hand-written two-entry table (`long-press`, `metrics`) instead of calling the real alias
5+
// hand-written two-entry table (`long-press`, `launch`) instead of calling the real alias
66
// registry, `commands/cli-command-aliases.ts` (five entries). The table silently fell out of
77
// sync — `tap`, `launch`, `relaunch` missed the fast path entirely and paid a full CLI bootstrap
88
// just to print static help text — and nothing failed, because bin.ts's own top-level dispatch
@@ -45,7 +45,7 @@
4545
// would otherwise let the composition read correctly while calling something else entirely, and
4646
// a second `helpTarget` declaration would let the resolver run on an unrelated value.
4747
//
48-
// All three were false on the pre-fix bin.ts (no import; both 'long-press' and 'metrics' present
48+
// All three were false on the pre-fix bin.ts (no import; both 'long-press' and 'launch' present
4949
// as literals; no composition to find), so the set is a real regression pin, not just a
5050
// description of intent.
5151
//

scripts/perf/scenario.ts

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import path from 'node:path';
22
import type { ResolvedProfile } from './platform-profiles.ts';
33

4-
// A legacy-form batch step: maps through the exact documented CLI grammar.
5-
// `flags` uses internal CliFlags field names (e.g. snapshotInteractiveOnly).
4+
// A structured batch step: maps through the same command input as MCP and the Node client.
65
export type BatchStepSpec = {
76
command: string;
8-
positionals?: string[];
9-
flags?: Record<string, unknown>;
7+
input: Record<string, unknown>;
108
};
119

1210
type ScenarioStepBase = {
@@ -19,7 +17,7 @@ type ScenarioStepBase = {
1917
};
2018

2119
// Discriminated on execMode so the invoker gets the right payload without `!`/`?? []`:
22-
// standalone carries full CLI args; batch carries one legacy batch step.
20+
// standalone carries full CLI args; batch carries one structured batch step.
2321
export type ScenarioStep =
2422
| (ScenarioStepBase & { execMode: 'standalone'; args: string[] })
2523
| (ScenarioStepBase & { execMode: 'batch'; step: BatchStepSpec; isSnapshot?: boolean });
@@ -56,32 +54,50 @@ export function buildSettingsTour(p: ResolvedProfile, ctx: StepContext): Scenari
5654
bat(
5755
'fill search',
5856
'fill',
59-
{ command: 'fill', positionals: [s.searchFieldEditable, 'general'] },
57+
{
58+
command: 'fill',
59+
input: {
60+
target: { kind: 'selector', selector: s.searchFieldEditable },
61+
text: 'general',
62+
},
63+
},
6064
{ freshRoot: true },
6165
),
62-
bat('type', 'type', { command: 'type', positionals: ['wifi'] }),
66+
bat('type', 'type', { command: 'type', input: { text: 'wifi' } }),
6367
bat('get editable text', 'get', {
6468
command: 'get',
65-
positionals: ['text', s.searchFieldEditable],
69+
input: {
70+
format: 'text',
71+
target: { kind: 'selector', selector: s.searchFieldEditable },
72+
},
6673
}),
67-
bat('keyboard return', 'keyboard', { command: 'keyboard', positionals: ['return'] }),
74+
bat('keyboard return', 'keyboard', { command: 'keyboard', input: { action: 'return' } }),
6875
]
6976
: [
7077
// Android: tap the search entry first to reveal the editable, then type/fill it.
7178
bat(
7279
'press search field',
7380
'press',
74-
{ command: 'press', positionals: [s.searchField] },
81+
{
82+
command: 'press',
83+
input: { target: { kind: 'selector', selector: s.searchField } },
84+
},
7585
{ freshRoot: true },
7686
),
77-
bat('type', 'type', { command: 'type', positionals: ['wifi'] }),
87+
bat('type', 'type', { command: 'type', input: { text: 'wifi' } }),
7888
bat('fill search', 'fill', {
7989
command: 'fill',
80-
positionals: [s.searchFieldEditable, 'general'],
90+
input: {
91+
target: { kind: 'selector', selector: s.searchFieldEditable },
92+
text: 'general',
93+
},
8194
}),
8295
bat('get editable text', 'get', {
8396
command: 'get',
84-
positionals: ['text', s.searchFieldEditable],
97+
input: {
98+
format: 'text',
99+
target: { kind: 'selector', selector: s.searchFieldEditable },
100+
},
85101
}),
86102
];
87103

@@ -93,16 +109,24 @@ export function buildSettingsTour(p: ResolvedProfile, ctx: StepContext): Scenari
93109
bat(
94110
'press series (sequence)',
95111
'press',
96-
{ command: 'press', positionals: ['200', '95'], flags: { count: 2, intervalMs: 50 } },
112+
{
113+
command: 'press',
114+
input: { target: { kind: 'point', x: 200, y: 95 }, count: 2, intervalMs: 50 },
115+
},
97116
{ freshRoot: true },
98117
),
99118
bat(
100119
'swipe series (sequence)',
101120
'swipe',
102121
{
103122
command: 'swipe',
104-
positionals: ['200', '650', '200', '450', '120'],
105-
flags: { count: 2, pauseMs: 50, pattern: 'ping-pong' },
123+
input: {
124+
from: { x: 200, y: 650 },
125+
to: { x: 200, y: 450 },
126+
count: 2,
127+
pauseMs: 50,
128+
pattern: 'ping-pong',
129+
},
106130
},
107131
{ freshRoot: true },
108132
),
@@ -117,27 +141,30 @@ export function buildSettingsTour(p: ResolvedProfile, ctx: StepContext): Scenari
117141
bat(
118142
'snapshot -i (root)',
119143
'snapshot',
120-
{ command: 'snapshot', flags: { snapshotInteractiveOnly: true } },
144+
{ command: 'snapshot', input: { interactiveOnly: true } },
121145
{ isSnapshot: true },
122146
),
123-
bat('snapshot (root)', 'snapshot', { command: 'snapshot' }, { isSnapshot: true }),
147+
bat('snapshot (root)', 'snapshot', { command: 'snapshot', input: {} }, { isSnapshot: true }),
124148

125149
// --- navigate into a sub-screen from a fresh root (freshRoot resets scroll so the
126150
// deep-screen row is in view), read it, then return ---
127151
bat(
128152
'press → deep screen',
129153
'press',
130-
{ command: 'press', positionals: [s.deepScreen] },
154+
{
155+
command: 'press',
156+
input: { target: { kind: 'selector', selector: s.deepScreen } },
157+
},
131158
{ freshRoot: true },
132159
),
133-
bat('snapshot (deep)', 'snapshot', { command: 'snapshot' }, { isSnapshot: true }),
160+
bat('snapshot (deep)', 'snapshot', { command: 'snapshot', input: {} }, { isSnapshot: true }),
134161
bat(
135162
'snapshot -i (deep)',
136163
'snapshot',
137-
{ command: 'snapshot', flags: { snapshotInteractiveOnly: true } },
164+
{ command: 'snapshot', input: { interactiveOnly: true } },
138165
{ isSnapshot: true },
139166
),
140-
bat('back', 'back', { command: 'back' }),
167+
bat('back', 'back', { command: 'back', input: {} }),
141168

142169
// --- iOS runner series commands surfaced by PR #643 ---
143170
...iosRunnerSeries,
@@ -146,26 +173,38 @@ export function buildSettingsTour(p: ResolvedProfile, ctx: StepContext): Scenari
146173
bat(
147174
'wait text',
148175
'wait',
149-
{ command: 'wait', positionals: ['text', s.anchorText, '3000'] },
176+
{ command: 'wait', input: { text: s.anchorText, timeoutMs: 3000 } },
150177
{ freshRoot: true },
151178
),
152-
bat('find', 'find', { command: 'find', positionals: [s.anchorText] }),
153-
bat('get text', 'get', { command: 'get', positionals: ['text', s.anchorLabel] }),
154-
bat('is visible', 'is', { command: 'is', positionals: ['visible', s.anchorLabel] }),
179+
bat('find', 'find', { command: 'find', input: { query: s.anchorText } }),
180+
bat('get text', 'get', {
181+
command: 'get',
182+
input: {
183+
format: 'text',
184+
target: { kind: 'selector', selector: s.anchorLabel },
185+
},
186+
}),
187+
bat('is visible', 'is', {
188+
command: 'is',
189+
input: { predicate: 'visible', selector: s.anchorLabel },
190+
}),
155191

156192
// --- text entry (platform-specific order; see textEntry above) then scroll results ---
157193
...textEntry,
158-
bat('scroll down', 'scroll', { command: 'scroll', positionals: ['down'] }),
194+
bat('scroll down', 'scroll', { command: 'scroll', input: { direction: 'down' } }),
159195

160196
// --- artifact-producing commands; record brackets the rest so the clip has >1s of
161197
// footage (an instant start→stop makes simctl recordVideo fail to finalize) ---
162198
std('record start', 'record', ['record', 'start', rec, '--hide-touches']),
163-
bat('screenshot', 'screenshot', { command: 'screenshot', positionals: [shot] }),
164-
bat('logs mark', 'logs', { command: 'logs', positionals: ['mark', 'perf-mark'] }),
165-
bat('logs clear', 'logs', { command: 'logs', positionals: ['clear'] }),
199+
bat('screenshot', 'screenshot', { command: 'screenshot', input: { path: shot } }),
200+
bat('logs mark', 'logs', {
201+
command: 'logs',
202+
input: { action: 'mark', message: 'perf-mark' },
203+
}),
204+
bat('logs clear', 'logs', { command: 'logs', input: { action: 'clear' } }),
166205
std('trace start', 'trace', ['trace', 'start', trace]),
167206
std('trace stop', 'trace', ['trace', 'stop']),
168-
bat('perf', 'perf', { command: 'perf' }),
207+
bat('perf frames', 'perf', { command: 'perf', input: { area: 'frames' } }),
169208
std('record stop', 'record', ['record', 'stop']),
170209
];
171210
}

0 commit comments

Comments
 (0)