agent-scenario-loop 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/profile-session.ts +263 -17
- package/dist/core/artifact-contract.d.ts +6 -4
- package/dist/core/artifact-contract.js +164 -15
- package/dist/core/schema-validator.d.ts +1 -0
- package/dist/core/schema-validator.js +1 -0
- package/dist/runner/android-adb-driver.d.ts +7 -2
- package/dist/runner/android-adb-driver.js +7 -1
- package/dist/runner/android-adb.d.ts +40 -5
- package/dist/runner/android-adb.js +1046 -664
- package/dist/runner/ios-simctl.d.ts +1 -0
- package/dist/runner/ios-simctl.js +1 -0
- package/dist/runner/profile-android.d.ts +11 -1
- package/dist/runner/profile-android.js +230 -16
- package/dist/runner/profile-ios.d.ts +3 -2
- package/dist/runner/profile-ios.js +223 -20
- package/dist/runner/profile-mobile.d.ts +31 -3
- package/dist/runner/profile-mobile.js +793 -20
- package/dist/runner/validate-project.js +3 -0
- package/dist/scripts/consumer-rehearsal.d.ts +119 -0
- package/dist/scripts/consumer-rehearsal.js +757 -0
- package/dist/scripts/downstream-local-package-gate.d.ts +2 -0
- package/dist/scripts/downstream-local-package-gate.js +264 -0
- package/dist/scripts/package-smoke.d.ts +96 -0
- package/dist/scripts/package-smoke.js +2282 -0
- package/dist/scripts/release-readiness.d.ts +2 -0
- package/dist/scripts/release-readiness.js +520 -0
- package/docs/adapters.md +3 -1
- package/docs/api.md +2 -2
- package/docs/authoring.md +34 -2
- package/docs/consumer-rehearsal.md +27 -1
- package/docs/contracts.md +16 -2
- package/docs/live-proofs.md +5 -3
- package/examples/mobile-app/runner-manifests/evidence-provider.json +3 -3
- package/examples/mobile-app/scripts/asl-capture-profiler-provider.mjs +25 -0
- package/examples/runners/README.md +3 -3
- package/examples/runners/axe-accessibility-provider.json +2 -2
- package/examples/runners/script-accessibility-provider.json +2 -2
- package/examples/runners/script-memory-provider.json +2 -2
- package/examples/runners/script-network-provider.json +2 -2
- package/examples/runners/script-profiler-provider.json +2 -2
- package/package.json +11 -3
- package/schemas/manifest.schema.json +73 -3
- package/schemas/profiler.schema.json +243 -0
- package/schemas/runner-capabilities.schema.json +8 -2
- package/schemas/scenario.schema.json +18 -2
- package/templates/evidence-provider.json +3 -3
- package/templates/scripts/asl-capture-profiler-provider.mjs +20 -0
|
@@ -462,6 +462,7 @@ async function seedProfileSessionStorage({ bundleId, commands = [], dataContaine
|
|
|
462
462
|
...(typeof profileCommand.sequence === 'number' ? { sequence: profileCommand.sequence } : {}),
|
|
463
463
|
timestamp: typeof profileCommand.timestamp === 'number' ? profileCommand.timestamp : startedAt + index + 1,
|
|
464
464
|
...(typeof profileCommand.waitForMilestone === 'string' ? { waitForMilestone: profileCommand.waitForMilestone } : {}),
|
|
465
|
+
...(typeof profileCommand.waitMs === 'number' ? { waitMs: profileCommand.waitMs } : {}),
|
|
465
466
|
...(typeof profileCommand.waitTimeoutMs === 'number' ? { waitTimeoutMs: profileCommand.waitTimeoutMs } : {}),
|
|
466
467
|
}));
|
|
467
468
|
manifest[profileStorageKeys.session] = JSON.stringify(session);
|
|
@@ -35,6 +35,16 @@ declare function resolveProfileSessionCaptureWaitMs({ args, profileSessionEnable
|
|
|
35
35
|
profileSessionEnabled: boolean;
|
|
36
36
|
scenario: Record<string, any>;
|
|
37
37
|
}): number;
|
|
38
|
+
/**
|
|
39
|
+
* Derives a bounded logcat tail large enough to keep command-session evidence.
|
|
40
|
+
*
|
|
41
|
+
* @param {{commands: AndroidAdbProfileCommand[], profileSessionEnabled: boolean}} options
|
|
42
|
+
* @returns {number}
|
|
43
|
+
*/
|
|
44
|
+
declare function deriveProfileSessionLogcatLines({ commands, profileSessionEnabled, }: {
|
|
45
|
+
commands: AndroidAdbProfileCommand[];
|
|
46
|
+
profileSessionEnabled: boolean;
|
|
47
|
+
}): number;
|
|
38
48
|
/**
|
|
39
49
|
* Expands normalized scenario evidence steps into Android adb driver actions.
|
|
40
50
|
*
|
|
@@ -84,4 +94,4 @@ declare function runProfileAndroid(args: import('./profile-mobile').CliArgs, opt
|
|
|
84
94
|
* @returns {Promise<void>}
|
|
85
95
|
*/
|
|
86
96
|
declare function main(): Promise<void>;
|
|
87
|
-
export { deriveProfileSessionCaptureWaitMs, main, parseArgs, resolveAndroidAdbProfileCommands, resolveAndroidAdbDriverSteps, resolveProfileSessionCaptureWaitMs, readAndroidAdbVideoCapturePath, validateAndroidAdbDriverSteps, runProfileAndroid, summarizeFailedAndroidChecks, usage, };
|
|
97
|
+
export { deriveProfileSessionCaptureWaitMs, deriveProfileSessionLogcatLines, main, parseArgs, resolveAndroidAdbProfileCommands, resolveAndroidAdbDriverSteps, resolveProfileSessionCaptureWaitMs, readAndroidAdbVideoCapturePath, validateAndroidAdbDriverSteps, runProfileAndroid, summarizeFailedAndroidChecks, usage, };
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.usage = exports.parseArgs = void 0;
|
|
5
5
|
exports.deriveProfileSessionCaptureWaitMs = deriveProfileSessionCaptureWaitMs;
|
|
6
|
+
exports.deriveProfileSessionLogcatLines = deriveProfileSessionLogcatLines;
|
|
6
7
|
exports.main = main;
|
|
7
8
|
exports.resolveAndroidAdbProfileCommands = resolveAndroidAdbProfileCommands;
|
|
8
9
|
exports.resolveAndroidAdbDriverSteps = resolveAndroidAdbDriverSteps;
|
|
@@ -24,8 +25,12 @@ const { runAgentDeviceCapture } = require('./agent-device');
|
|
|
24
25
|
const { loadAslLocalEnv, readStringArgOrEnv } = require('./local-env');
|
|
25
26
|
const PROFILE_SESSION_CAPTURE_BOOTSTRAP_MS = 1000;
|
|
26
27
|
const PROFILE_SESSION_CAPTURE_MAX_MS = 120000;
|
|
28
|
+
const PROFILE_SESSION_LOGCAT_MIN_LINES = 1000;
|
|
29
|
+
const PROFILE_SESSION_LOGCAT_MAX_LINES = 20000;
|
|
30
|
+
const PROFILE_SESSION_LOGCAT_LINES_PER_COMMAND = 300;
|
|
27
31
|
const DEFAULT_ANDROID_PROFILE_SESSION_STORAGE_KEY = 'agent-scenario-loop.profile-session.1';
|
|
28
32
|
const DEFAULT_ANDROID_PROFILE_COMMAND_STORAGE_KEY = 'agent-scenario-loop.profile-commands.1';
|
|
33
|
+
const DEFAULT_ANDROID_DEV_CLIENT_READY_PATTERN = 'Running "main"';
|
|
29
34
|
const MANIFEST_LIFECYCLE_PHASES = new Set([
|
|
30
35
|
'cold-launch',
|
|
31
36
|
'warm-launch',
|
|
@@ -150,7 +155,7 @@ function resolveAndroidPackageName({ args, config, }) {
|
|
|
150
155
|
* @param {{config: Record<string, unknown>, action: 'start' | 'command', scenario: string, runId: string, command?: string}} options
|
|
151
156
|
* @returns {string}
|
|
152
157
|
*/
|
|
153
|
-
function buildProfileSessionUrl({ action, command, commandId, config, queueId, runId, scenario, sequence, waitForMilestone, waitTimeoutMs, }) {
|
|
158
|
+
function buildProfileSessionUrl({ action, command, commandId, config, queueId, runId, scenario, sequence, waitForMilestone, waitMs, waitTimeoutMs, }) {
|
|
154
159
|
const scheme = typeof config.app?.profileSessionScheme === 'string'
|
|
155
160
|
? config.app.profileSessionScheme
|
|
156
161
|
: typeof config.app?.scheme === 'string'
|
|
@@ -171,6 +176,9 @@ function buildProfileSessionUrl({ action, command, commandId, config, queueId, r
|
|
|
171
176
|
if (waitForMilestone) {
|
|
172
177
|
params.set('waitForMilestone', waitForMilestone);
|
|
173
178
|
}
|
|
179
|
+
if (typeof waitMs === 'number') {
|
|
180
|
+
params.set('waitMs', String(waitMs));
|
|
181
|
+
}
|
|
174
182
|
if (typeof waitTimeoutMs === 'number') {
|
|
175
183
|
params.set('waitTimeoutMs', String(waitTimeoutMs));
|
|
176
184
|
}
|
|
@@ -184,11 +192,10 @@ function buildProfileSessionUrl({ action, command, commandId, config, queueId, r
|
|
|
184
192
|
* @returns {import('./android-adb').AndroidAsyncStorageWrite[]}
|
|
185
193
|
*/
|
|
186
194
|
function buildProfileSessionStorageWrites({ commands, commandStorageKey, commandWaitMs, runId, scenario, sessionStorageKey, }) {
|
|
187
|
-
const timestampBase = Date.now();
|
|
188
195
|
const storedCommands = commands.map((profileCommand, index) => {
|
|
189
|
-
const
|
|
196
|
+
const timestampPlaceholder = `${ANDROID_DEVICE_EPOCH_MS_PLACEHOLDER}+${index + 1}`;
|
|
190
197
|
return {
|
|
191
|
-
id: `${
|
|
198
|
+
id: `${scenario}-${index + 1}-${profileCommand.command}`,
|
|
192
199
|
scenario,
|
|
193
200
|
runId,
|
|
194
201
|
command: profileCommand.command,
|
|
@@ -196,8 +203,9 @@ function buildProfileSessionStorageWrites({ commands, commandStorageKey, command
|
|
|
196
203
|
...(typeof profileCommand.sequence === 'number' ? { sequence: profileCommand.sequence } : {}),
|
|
197
204
|
...(typeof profileCommand.queueId === 'string' ? { queueId: profileCommand.queueId } : {}),
|
|
198
205
|
...(typeof profileCommand.waitForMilestone === 'string' ? { waitForMilestone: profileCommand.waitForMilestone } : {}),
|
|
206
|
+
...(typeof profileCommand.waitMs === 'number' ? { waitMs: profileCommand.waitMs } : {}),
|
|
199
207
|
...(typeof profileCommand.waitTimeoutMs === 'number' ? { waitTimeoutMs: profileCommand.waitTimeoutMs } : {}),
|
|
200
|
-
timestamp,
|
|
208
|
+
timestamp: timestampPlaceholder,
|
|
201
209
|
};
|
|
202
210
|
});
|
|
203
211
|
const commandWaitMsTotal = commands.reduce((total, profileCommand) => (total + (typeof profileCommand.waitMs === 'number' && profileCommand.waitMs > 0 ? profileCommand.waitMs : 0)), 0);
|
|
@@ -274,6 +282,32 @@ function resolveProfileSessionCaptureWaitMs({ args, profileSessionEnabled, scena
|
|
|
274
282
|
}
|
|
275
283
|
return profileSessionEnabled ? deriveProfileSessionCaptureWaitMs(scenario) : 0;
|
|
276
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* Derives a bounded logcat tail large enough to keep command-session evidence.
|
|
287
|
+
*
|
|
288
|
+
* @param {{commands: AndroidAdbProfileCommand[], profileSessionEnabled: boolean}} options
|
|
289
|
+
* @returns {number}
|
|
290
|
+
*/
|
|
291
|
+
function deriveProfileSessionLogcatLines({ commands, profileSessionEnabled, }) {
|
|
292
|
+
if (!profileSessionEnabled || commands.length === 0) {
|
|
293
|
+
return PROFILE_SESSION_LOGCAT_MIN_LINES;
|
|
294
|
+
}
|
|
295
|
+
const derivedLines = PROFILE_SESSION_LOGCAT_MIN_LINES + (commands.length * PROFILE_SESSION_LOGCAT_LINES_PER_COMMAND);
|
|
296
|
+
return Math.min(Math.max(derivedLines, PROFILE_SESSION_LOGCAT_MIN_LINES), PROFILE_SESSION_LOGCAT_MAX_LINES);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Resolves Android logcat capture lines, keeping explicit CLI input authoritative.
|
|
300
|
+
*
|
|
301
|
+
* @param {{args: import('./profile-mobile').CliArgs, commands: AndroidAdbProfileCommand[], profileSessionEnabled: boolean}} options
|
|
302
|
+
* @returns {number}
|
|
303
|
+
*/
|
|
304
|
+
function resolveProfileSessionLogcatLines({ args, commands, profileSessionEnabled, }) {
|
|
305
|
+
const explicitLogcatLines = readScalarArg(args['logcat-lines']);
|
|
306
|
+
if (explicitLogcatLines !== undefined) {
|
|
307
|
+
return parsePositiveInteger(explicitLogcatLines, PROFILE_SESSION_LOGCAT_MIN_LINES);
|
|
308
|
+
}
|
|
309
|
+
return deriveProfileSessionLogcatLines({ commands, profileSessionEnabled });
|
|
310
|
+
}
|
|
277
311
|
/**
|
|
278
312
|
* Reads Android adb adapter metadata from a normalized scenario step.
|
|
279
313
|
*
|
|
@@ -341,16 +375,182 @@ function resolveExecutionPlanProfileCommands(scenario) {
|
|
|
341
375
|
waitMs: readStepWaitMs(step),
|
|
342
376
|
...(nextStep?.portMethod === 'waitForTruthEvent' && typeof nextStep.milestone === 'string'
|
|
343
377
|
? {
|
|
344
|
-
waitForMilestone: nextStep.milestone,
|
|
378
|
+
waitForMilestone: resolveMilestoneEventName(scenario, nextStep.milestone),
|
|
345
379
|
waitTimeoutMs: readPositiveInteger(nextStep.timeoutMs, 0),
|
|
346
380
|
}
|
|
347
381
|
: {}),
|
|
348
382
|
});
|
|
349
383
|
}
|
|
350
|
-
return
|
|
384
|
+
return expandProfileCommandCycles(scenario, commands, repeat);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Returns true when a command is part of the setup prefix that establishes app readiness before repeated cycle work.
|
|
388
|
+
*
|
|
389
|
+
* @param {Record<string, unknown>} scenario
|
|
390
|
+
* @param {AndroidAdbProfileCommand} command
|
|
391
|
+
* @returns {boolean}
|
|
392
|
+
*/
|
|
393
|
+
function isReadinessSetupProfileCommand(scenario, command) {
|
|
394
|
+
if (typeof command.waitForMilestone !== 'string') {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
const readyEvent = resolveScenarioReadinessEvent(scenario);
|
|
398
|
+
return typeof readyEvent === 'string' && command.waitForMilestone === readyEvent;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Reads a string id list from scenario cycles metadata.
|
|
402
|
+
*
|
|
403
|
+
* @param {unknown} value
|
|
404
|
+
* @returns {Set<string>}
|
|
405
|
+
*/
|
|
406
|
+
function readCycleStepIdSet(value) {
|
|
407
|
+
return new Set(Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : []);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Resolves the milestone ids that represent measured cycle boundaries.
|
|
411
|
+
*
|
|
412
|
+
* @param {Record<string, unknown>} scenario
|
|
413
|
+
* @returns {Set<string>}
|
|
414
|
+
*/
|
|
415
|
+
function resolveMeasuredCycleMilestoneEvents(scenario) {
|
|
416
|
+
const milestones = new Set();
|
|
417
|
+
for (const budget of Array.isArray(scenario.budgets) ? scenario.budgets : []) {
|
|
418
|
+
if (!budget || typeof budget !== 'object' || budget.source !== 'milestone') {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
if (typeof budget.fromMilestone === 'string') {
|
|
422
|
+
milestones.add(resolveMilestoneEventName(scenario, budget.fromMilestone));
|
|
423
|
+
}
|
|
424
|
+
if (typeof budget.toMilestone === 'string') {
|
|
425
|
+
milestones.add(resolveMilestoneEventName(scenario, budget.toMilestone));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return milestones;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Resolves how many leading commands are setup-only before repeated cycle work.
|
|
432
|
+
*
|
|
433
|
+
* @param {Record<string, unknown>} scenario
|
|
434
|
+
* @param {AndroidAdbProfileCommand[]} commands
|
|
435
|
+
* @returns {number}
|
|
436
|
+
*/
|
|
437
|
+
function resolveSetupCommandCount(scenario, commands) {
|
|
438
|
+
const explicitSetupStepIds = readCycleStepIdSet(scenario.cycles?.setupStepIds);
|
|
439
|
+
if (explicitSetupStepIds.size > 0) {
|
|
440
|
+
let count = 0;
|
|
441
|
+
for (const command of commands) {
|
|
442
|
+
if (!command.commandId || !explicitSetupStepIds.has(command.commandId)) {
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
count += 1;
|
|
446
|
+
}
|
|
447
|
+
return count;
|
|
448
|
+
}
|
|
449
|
+
const explicitBodyStepIds = readCycleStepIdSet(scenario.cycles?.bodyStepIds);
|
|
450
|
+
if (explicitBodyStepIds.size > 0) {
|
|
451
|
+
const firstBodyIndex = commands.findIndex((command) => (typeof command.commandId === 'string' && explicitBodyStepIds.has(command.commandId)));
|
|
452
|
+
return firstBodyIndex > 0 ? firstBodyIndex : 0;
|
|
453
|
+
}
|
|
454
|
+
let readinessSetupCommandCount = 0;
|
|
455
|
+
for (const command of commands) {
|
|
456
|
+
if (!isReadinessSetupProfileCommand(scenario, command)) {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
readinessSetupCommandCount += 1;
|
|
460
|
+
}
|
|
461
|
+
if (readinessSetupCommandCount > 0) {
|
|
462
|
+
return readinessSetupCommandCount;
|
|
463
|
+
}
|
|
464
|
+
const measuredMilestones = resolveMeasuredCycleMilestoneEvents(scenario);
|
|
465
|
+
if (measuredMilestones.size === 0) {
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
const firstMeasuredCommandIndex = commands.findIndex((command) => (typeof command.waitForMilestone === 'string' && measuredMilestones.has(command.waitForMilestone)));
|
|
469
|
+
return firstMeasuredCommandIndex > 0 ? firstMeasuredCommandIndex : 0;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Expands commands so setup/readiness commands execute once while cycle-body commands repeat.
|
|
473
|
+
*
|
|
474
|
+
* @param {Record<string, unknown>} scenario
|
|
475
|
+
* @param {AndroidAdbProfileCommand[]} commands
|
|
476
|
+
* @param {number} repeat
|
|
477
|
+
* @returns {AndroidAdbProfileCommand[]}
|
|
478
|
+
*/
|
|
479
|
+
function expandProfileCommandCycles(scenario, commands, repeat) {
|
|
480
|
+
const setupCommandCount = resolveSetupCommandCount(scenario, commands);
|
|
481
|
+
const setupCommands = commands.slice(0, setupCommandCount);
|
|
482
|
+
const cycleCommands = commands.slice(setupCommandCount);
|
|
483
|
+
const expandedCommands = cycleCommands.length === 0
|
|
484
|
+
? setupCommands
|
|
485
|
+
: [
|
|
486
|
+
...setupCommands,
|
|
487
|
+
...Array.from({ length: repeat }).flatMap(() => cycleCommands),
|
|
488
|
+
];
|
|
489
|
+
return expandedCommands.map((command, index) => ({
|
|
351
490
|
...command,
|
|
352
|
-
sequence:
|
|
353
|
-
}))
|
|
491
|
+
sequence: index + 1,
|
|
492
|
+
}));
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Resolves a portable milestone id to the app truth event that releases command sequencing.
|
|
496
|
+
*
|
|
497
|
+
* @param {Record<string, unknown>} scenario
|
|
498
|
+
* @param {string} milestone
|
|
499
|
+
* @returns {string}
|
|
500
|
+
*/
|
|
501
|
+
function resolveMilestoneEventName(scenario, milestone) {
|
|
502
|
+
const milestoneEntry = Array.isArray(scenario.milestones)
|
|
503
|
+
? scenario.milestones.find((entry) => entry?.id === milestone)
|
|
504
|
+
: undefined;
|
|
505
|
+
if (typeof milestoneEntry?.event === 'string' && milestoneEntry.event.length > 0) {
|
|
506
|
+
return milestoneEntry.event;
|
|
507
|
+
}
|
|
508
|
+
const metricEvent = scenario.metricEvents?.[milestone];
|
|
509
|
+
return typeof metricEvent === 'string' && metricEvent.length > 0 ? metricEvent : milestone;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Resolves the scenario truth event that represents initial app readiness.
|
|
513
|
+
*
|
|
514
|
+
* @param {Record<string, unknown>} scenario
|
|
515
|
+
* @returns {string | null}
|
|
516
|
+
*/
|
|
517
|
+
function resolveScenarioReadinessEvent(scenario) {
|
|
518
|
+
const explicitReadyEvent = scenario.truthEvents?.ready?.event;
|
|
519
|
+
if (typeof explicitReadyEvent === 'string' && explicitReadyEvent.length > 0) {
|
|
520
|
+
return explicitReadyEvent;
|
|
521
|
+
}
|
|
522
|
+
const milestoneEntry = Array.isArray(scenario.milestones)
|
|
523
|
+
? scenario.milestones.find((entry) => (String(entry?.event ?? '').includes('ready')))
|
|
524
|
+
: undefined;
|
|
525
|
+
return typeof milestoneEntry?.event === 'string' && milestoneEntry.event.length > 0
|
|
526
|
+
? milestoneEntry.event
|
|
527
|
+
: null;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Applies wait gates from the normalized execution plan to platform-declared commands.
|
|
531
|
+
*
|
|
532
|
+
* @param {Record<string, unknown>} scenario
|
|
533
|
+
* @param {AndroidAdbProfileCommand[]} commands
|
|
534
|
+
* @returns {AndroidAdbProfileCommand[]}
|
|
535
|
+
*/
|
|
536
|
+
function applyExecutionPlanCommandGates(scenario, commands) {
|
|
537
|
+
const planCommands = resolveExecutionPlanProfileCommands(scenario);
|
|
538
|
+
if (planCommands.length === 0) {
|
|
539
|
+
return commands;
|
|
540
|
+
}
|
|
541
|
+
return commands.map((command, index) => {
|
|
542
|
+
const planCommand = planCommands[index];
|
|
543
|
+
if (!planCommand || typeof planCommand.waitForMilestone !== 'string' || typeof command.waitForMilestone === 'string') {
|
|
544
|
+
return command;
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
...command,
|
|
548
|
+
waitForMilestone: planCommand.waitForMilestone,
|
|
549
|
+
...(typeof command.waitTimeoutMs === 'number'
|
|
550
|
+
? {}
|
|
551
|
+
: { waitTimeoutMs: readPositiveInteger(planCommand.waitTimeoutMs, 0) }),
|
|
552
|
+
};
|
|
553
|
+
});
|
|
354
554
|
}
|
|
355
555
|
/**
|
|
356
556
|
* Expands normalized scenario evidence steps into Android adb driver actions.
|
|
@@ -478,7 +678,7 @@ function resolveAndroidAdbProfileCommands(scenario) {
|
|
|
478
678
|
});
|
|
479
679
|
}
|
|
480
680
|
}
|
|
481
|
-
return commands;
|
|
681
|
+
return applyExecutionPlanCommandGates(scenario, commands);
|
|
482
682
|
}
|
|
483
683
|
/**
|
|
484
684
|
* Summarizes failed adb capture checks for CLI errors.
|
|
@@ -573,10 +773,14 @@ async function runProfileAndroid(args, options = {}) {
|
|
|
573
773
|
'ASL_ANDROID_DEV_CLIENT_WAIT_MS',
|
|
574
774
|
'ASL_EXAMPLE_ANDROID_DEV_CLIENT_WAIT_MS',
|
|
575
775
|
]), 1000);
|
|
576
|
-
const
|
|
776
|
+
const configuredAndroidDevClientReadyPattern = readStringArgOrEnv(args['android-dev-client-ready-pattern'], [
|
|
577
777
|
'ASL_ANDROID_DEV_CLIENT_READY_PATTERN',
|
|
578
778
|
'ASL_EXAMPLE_ANDROID_DEV_CLIENT_READY_PATTERN',
|
|
579
779
|
]);
|
|
780
|
+
const androidDevClientReadyPattern = configuredAndroidDevClientReadyPattern
|
|
781
|
+
?? (androidDevClientUrl && profileSessionEnabled && profileSessionStorageEnabled
|
|
782
|
+
? DEFAULT_ANDROID_DEV_CLIENT_READY_PATTERN
|
|
783
|
+
: undefined);
|
|
580
784
|
const androidDevClientReadyQuietMs = parsePositiveInteger(readStringArgOrEnv(args['android-dev-client-ready-quiet-ms'], [
|
|
581
785
|
'ASL_ANDROID_DEV_CLIENT_READY_QUIET_MS',
|
|
582
786
|
'ASL_EXAMPLE_ANDROID_DEV_CLIENT_READY_QUIET_MS',
|
|
@@ -585,6 +789,10 @@ async function runProfileAndroid(args, options = {}) {
|
|
|
585
789
|
'ASL_ANDROID_DEV_CLIENT_READY_TIMEOUT_MS',
|
|
586
790
|
'ASL_EXAMPLE_ANDROID_DEV_CLIENT_READY_TIMEOUT_MS',
|
|
587
791
|
]), 60000);
|
|
792
|
+
const adbCommandTimeoutMs = parsePositiveInteger(readStringArgOrEnv(args['adb-command-timeout-ms'], [
|
|
793
|
+
'ASL_ANDROID_ADB_COMMAND_TIMEOUT_MS',
|
|
794
|
+
'ASL_EXAMPLE_ANDROID_ADB_COMMAND_TIMEOUT_MS',
|
|
795
|
+
]), 30000);
|
|
588
796
|
const scenarioName = typeof scenario.name === 'string' ? scenario.name : path.basename(args.scenario, '.json');
|
|
589
797
|
const driverSteps = adbCaptureEnabled ? resolveAndroidAdbDriverSteps(scenario) : [];
|
|
590
798
|
if (adbCaptureEnabled) {
|
|
@@ -619,6 +827,7 @@ async function runProfileAndroid(args, options = {}) {
|
|
|
619
827
|
...(typeof profileCommand.queueId === 'string' ? { queueId: profileCommand.queueId } : {}),
|
|
620
828
|
...(typeof profileCommand.sequence === 'number' ? { sequence: profileCommand.sequence } : {}),
|
|
621
829
|
...(typeof profileCommand.waitForMilestone === 'string' ? { waitForMilestone: profileCommand.waitForMilestone } : {}),
|
|
830
|
+
...(typeof profileCommand.waitMs === 'number' ? { waitMs: profileCommand.waitMs } : {}),
|
|
622
831
|
...(typeof profileCommand.waitTimeoutMs === 'number' ? { waitTimeoutMs: profileCommand.waitTimeoutMs } : {}),
|
|
623
832
|
}),
|
|
624
833
|
waitMs: profileCommand.waitMs,
|
|
@@ -652,13 +861,18 @@ async function runProfileAndroid(args, options = {}) {
|
|
|
652
861
|
...(typeof args.adb === 'string' ? { adbPath: args.adb } : {}),
|
|
653
862
|
captureLogcat: true,
|
|
654
863
|
clearLogcat: isEnabled(args['clear-logcat']),
|
|
864
|
+
commandTimeoutMs: adbCommandTimeoutMs,
|
|
655
865
|
deepLinks: profileSessionDeepLinks,
|
|
656
866
|
...(options.delay ? { delay: options.delay } : {}),
|
|
657
867
|
...(options.executor ? { executor: options.executor } : {}),
|
|
658
868
|
driverSteps,
|
|
659
869
|
launch: isEnabled(args.launch),
|
|
660
870
|
launchWaitMs: parsePositiveInteger(readScalarArg(args['launch-wait-ms']), 0),
|
|
661
|
-
logcatLines:
|
|
871
|
+
logcatLines: resolveProfileSessionLogcatLines({
|
|
872
|
+
args,
|
|
873
|
+
commands: profileSessionCommands,
|
|
874
|
+
profileSessionEnabled,
|
|
875
|
+
}),
|
|
662
876
|
outputDir: resolveAdbCaptureOutputDir({ args, runId }),
|
|
663
877
|
packageName: resolveAndroidPackageName({ args, config }),
|
|
664
878
|
...(typeof args['react-native-debug-host'] === 'string'
|
|
@@ -724,7 +938,7 @@ async function runProfileAndroid(args, options = {}) {
|
|
|
724
938
|
: baseProfileArgs;
|
|
725
939
|
const lifecyclePhase = resolveManifestLifecyclePhase(args);
|
|
726
940
|
const environmentSource = agentDeviceCapture ? 'agent-device' : 'adb';
|
|
727
|
-
const
|
|
941
|
+
const copiedAdbLogArtifact = adbCapture ? 'raw/adb-logcat.txt' : undefined;
|
|
728
942
|
return runProfileMobile(profileArgs, {
|
|
729
943
|
commandTransport: profileSessionStorageEnabled
|
|
730
944
|
? 'profile-session-storage'
|
|
@@ -740,13 +954,13 @@ async function runProfileAndroid(args, options = {}) {
|
|
|
740
954
|
value: 'foreground',
|
|
741
955
|
evidence: 'asserted',
|
|
742
956
|
source: environmentSource,
|
|
743
|
-
artifact:
|
|
957
|
+
...(copiedAdbLogArtifact ? { artifact: copiedAdbLogArtifact } : {}),
|
|
744
958
|
},
|
|
745
959
|
lifecyclePhase: {
|
|
746
960
|
value: 'foreground',
|
|
747
961
|
evidence: 'asserted',
|
|
748
962
|
source: environmentSource,
|
|
749
|
-
artifact:
|
|
963
|
+
...(copiedAdbLogArtifact ? { artifact: copiedAdbLogArtifact } : {}),
|
|
750
964
|
},
|
|
751
965
|
},
|
|
752
966
|
environmentPreconditions: {
|
|
@@ -759,7 +973,7 @@ async function runProfileAndroid(args, options = {}) {
|
|
|
759
973
|
value: lifecyclePhase,
|
|
760
974
|
evidence: 'asserted',
|
|
761
975
|
source: environmentSource,
|
|
762
|
-
artifact:
|
|
976
|
+
...(copiedAdbLogArtifact ? { artifact: copiedAdbLogArtifact } : {}),
|
|
763
977
|
},
|
|
764
978
|
},
|
|
765
979
|
interactionDriver: agentDeviceCapture ? 'agent-device' : 'adb-logcat',
|
|
@@ -49,7 +49,7 @@ declare function resolveIosConflictingBundleIds(config: Record<string, any>): st
|
|
|
49
49
|
* @param {{config: Record<string, unknown>, action: 'start' | 'command', scenario: string, runId: string, command?: string}} options
|
|
50
50
|
* @returns {string}
|
|
51
51
|
*/
|
|
52
|
-
declare function buildProfileSessionUrl({ action, command, commandId, config, queueId, runId, scenario, sequence, waitForMilestone, waitTimeoutMs, }: {
|
|
52
|
+
declare function buildProfileSessionUrl({ action, command, commandId, config, queueId, runId, scenario, sequence, waitForMilestone, waitMs, waitTimeoutMs, }: {
|
|
53
53
|
action: 'start' | 'command';
|
|
54
54
|
command?: string;
|
|
55
55
|
commandId?: string;
|
|
@@ -59,10 +59,11 @@ declare function buildProfileSessionUrl({ action, command, commandId, config, qu
|
|
|
59
59
|
scenario: string;
|
|
60
60
|
sequence?: number;
|
|
61
61
|
waitForMilestone?: string;
|
|
62
|
+
waitMs?: number;
|
|
62
63
|
waitTimeoutMs?: number;
|
|
63
64
|
}): string;
|
|
64
65
|
/**
|
|
65
|
-
* Derives a storage-backed profile capture window from scenario waits and cycles.
|
|
66
|
+
* Derives a storage-backed profile capture window from scenario waits, command gates, and cycles.
|
|
66
67
|
*
|
|
67
68
|
* @param {Record<string, unknown>} scenario
|
|
68
69
|
* @returns {number}
|