agent-scenario-loop 0.1.0

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.
Files changed (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/app/profile-session.ts +812 -0
  4. package/core/config-template.json +41 -0
  5. package/dist/core/agent-summary.d.ts +15 -0
  6. package/dist/core/agent-summary.js +177 -0
  7. package/dist/core/artifact-contract.d.ts +151 -0
  8. package/dist/core/artifact-contract.js +897 -0
  9. package/dist/core/artifact-layout.d.ts +56 -0
  10. package/dist/core/artifact-layout.js +61 -0
  11. package/dist/core/artifact-writer.d.ts +44 -0
  12. package/dist/core/artifact-writer.js +55 -0
  13. package/dist/core/comparison.d.ts +133 -0
  14. package/dist/core/comparison.js +294 -0
  15. package/dist/core/evidence-interpreter.d.ts +28 -0
  16. package/dist/core/evidence-interpreter.js +69 -0
  17. package/dist/core/execution-plan.d.ts +44 -0
  18. package/dist/core/execution-plan.js +95 -0
  19. package/dist/core/planner.d.ts +132 -0
  20. package/dist/core/planner.js +812 -0
  21. package/dist/core/ports.d.ts +198 -0
  22. package/dist/core/ports.js +146 -0
  23. package/dist/core/run-index.d.ts +62 -0
  24. package/dist/core/run-index.js +143 -0
  25. package/dist/core/schema-validator.d.ts +86 -0
  26. package/dist/core/schema-validator.js +407 -0
  27. package/dist/index.d.ts +11 -0
  28. package/dist/index.js +27 -0
  29. package/dist/runner/agent-device-driver.d.ts +126 -0
  30. package/dist/runner/agent-device-driver.js +168 -0
  31. package/dist/runner/agent-device.d.ts +295 -0
  32. package/dist/runner/agent-device.js +1271 -0
  33. package/dist/runner/android-adb-driver.d.ts +175 -0
  34. package/dist/runner/android-adb-driver.js +399 -0
  35. package/dist/runner/android-adb.d.ts +254 -0
  36. package/dist/runner/android-adb.js +1618 -0
  37. package/dist/runner/argent-driver.d.ts +183 -0
  38. package/dist/runner/argent-driver.js +297 -0
  39. package/dist/runner/argent.d.ts +349 -0
  40. package/dist/runner/argent.js +1211 -0
  41. package/dist/runner/check-plan.d.ts +45 -0
  42. package/dist/runner/check-plan.js +210 -0
  43. package/dist/runner/cli.d.ts +20 -0
  44. package/dist/runner/cli.js +23 -0
  45. package/dist/runner/compare-latest.d.ts +99 -0
  46. package/dist/runner/compare-latest.js +233 -0
  47. package/dist/runner/compare.d.ts +58 -0
  48. package/dist/runner/compare.js +157 -0
  49. package/dist/runner/demo-loop.d.ts +45 -0
  50. package/dist/runner/demo-loop.js +170 -0
  51. package/dist/runner/example-android-live.d.ts +137 -0
  52. package/dist/runner/example-android-live.js +454 -0
  53. package/dist/runner/example-ios-live.d.ts +137 -0
  54. package/dist/runner/example-ios-live.js +471 -0
  55. package/dist/runner/host-doctor.d.ts +131 -0
  56. package/dist/runner/host-doctor.js +628 -0
  57. package/dist/runner/init-project.d.ts +88 -0
  58. package/dist/runner/init-project.js +263 -0
  59. package/dist/runner/ios-simctl-driver.d.ts +69 -0
  60. package/dist/runner/ios-simctl-driver.js +97 -0
  61. package/dist/runner/ios-simctl.d.ts +254 -0
  62. package/dist/runner/ios-simctl.js +1415 -0
  63. package/dist/runner/live-android.d.ts +137 -0
  64. package/dist/runner/live-android.js +539 -0
  65. package/dist/runner/live-comparison.d.ts +67 -0
  66. package/dist/runner/live-comparison.js +147 -0
  67. package/dist/runner/live-ios.d.ts +137 -0
  68. package/dist/runner/live-ios.js +460 -0
  69. package/dist/runner/live-proof-summary.d.ts +263 -0
  70. package/dist/runner/live-proof-summary.js +465 -0
  71. package/dist/runner/live-proof.d.ts +467 -0
  72. package/dist/runner/live-proof.js +920 -0
  73. package/dist/runner/local-env.d.ts +64 -0
  74. package/dist/runner/local-env.js +155 -0
  75. package/dist/runner/profile-android.d.ts +82 -0
  76. package/dist/runner/profile-android.js +671 -0
  77. package/dist/runner/profile-ios.d.ts +108 -0
  78. package/dist/runner/profile-ios.js +532 -0
  79. package/dist/runner/profile-mobile.d.ts +254 -0
  80. package/dist/runner/profile-mobile.js +1307 -0
  81. package/dist/runner/validate-project.d.ts +273 -0
  82. package/dist/runner/validate-project.js +1501 -0
  83. package/docs/adapters.md +145 -0
  84. package/docs/api.md +94 -0
  85. package/docs/authoring.md +196 -0
  86. package/docs/concepts.md +136 -0
  87. package/docs/consumer-rehearsal.md +115 -0
  88. package/docs/contracts.md +267 -0
  89. package/docs/live-proofs.md +270 -0
  90. package/docs/principles.md +46 -0
  91. package/examples/event-logs/app-startup-baseline.log +4 -0
  92. package/examples/event-logs/app-startup-current.log +4 -0
  93. package/examples/minimal-app/README.md +70 -0
  94. package/examples/mobile-app/README.md +302 -0
  95. package/examples/mobile-app/app.json +22 -0
  96. package/examples/mobile-app/asl/package-scripts.json +32 -0
  97. package/examples/mobile-app/asl.config.json +37 -0
  98. package/examples/mobile-app/event-logs/android-app-startup.log +4 -0
  99. package/examples/mobile-app/event-logs/android-open-close-cycle.log +12 -0
  100. package/examples/mobile-app/event-logs/android-scroll-settle.log +12 -0
  101. package/examples/mobile-app/event-logs/app-startup.log +4 -0
  102. package/examples/mobile-app/event-logs/open-close-cycle.log +12 -0
  103. package/examples/mobile-app/event-logs/scroll-settle.log +12 -0
  104. package/examples/mobile-app/index.ts +20 -0
  105. package/examples/mobile-app/metro.config.js +20 -0
  106. package/examples/mobile-app/package.json +62 -0
  107. package/examples/mobile-app/patches/expo-modules-jsi@56.0.10.patch +19 -0
  108. package/examples/mobile-app/plugins/with-ios-build-compat.js +271 -0
  109. package/examples/mobile-app/pnpm-lock.yaml +4440 -0
  110. package/examples/mobile-app/runner-manifests/evidence-provider.json +79 -0
  111. package/examples/mobile-app/runner-manifests/primary-runner.json +19 -0
  112. package/examples/mobile-app/scenarios/android/app-startup-video.json +73 -0
  113. package/examples/mobile-app/scenarios/android/app-startup.json +44 -0
  114. package/examples/mobile-app/scenarios/android/open-close-cycle.json +54 -0
  115. package/examples/mobile-app/scenarios/android/scroll-settle.json +49 -0
  116. package/examples/mobile-app/scenarios/ios/app-startup.json +44 -0
  117. package/examples/mobile-app/scenarios/ios/open-close-cycle.json +54 -0
  118. package/examples/mobile-app/scenarios/ios/scroll-settle.json +49 -0
  119. package/examples/mobile-app/scenarios/mobile/app-startup.json +91 -0
  120. package/examples/mobile-app/scenarios/mobile/open-close-cycle.json +160 -0
  121. package/examples/mobile-app/scenarios/mobile/scroll-settle.json +148 -0
  122. package/examples/mobile-app/scripts/asl-capture-accessibility-provider.mjs +112 -0
  123. package/examples/mobile-app/scripts/asl-capture-profiler-provider.mjs +127 -0
  124. package/examples/mobile-app/src/devtools/profile-session.ts +7 -0
  125. package/examples/mobile-app/src/example-screen.tsx +322 -0
  126. package/examples/mobile-app/tsconfig.json +16 -0
  127. package/examples/mobile-app/tsconfig.typecheck.json +13 -0
  128. package/examples/runners/README.md +44 -0
  129. package/examples/runners/adb-android.json +25 -0
  130. package/examples/runners/agent-device-android.json +27 -0
  131. package/examples/runners/agent-device-ios.json +27 -0
  132. package/examples/runners/argent-android.json +32 -0
  133. package/examples/runners/argent-ios.json +32 -0
  134. package/examples/runners/argent-react-profiler-provider.json +15 -0
  135. package/examples/runners/axe-accessibility-provider.json +24 -0
  136. package/examples/runners/manual-log-ingest.json +9 -0
  137. package/examples/runners/rozenite-profiler-provider.json +9 -0
  138. package/examples/runners/script-accessibility-provider.json +24 -0
  139. package/examples/runners/script-memory-provider.json +24 -0
  140. package/examples/runners/script-network-provider.json +24 -0
  141. package/examples/runners/script-profiler-provider.json +30 -0
  142. package/examples/runners/xcodebuildmcp-ios.json +29 -0
  143. package/examples/scenarios/ios/app-startup.json +28 -0
  144. package/examples/scenarios/ios/open-close-cycle.json +35 -0
  145. package/examples/scenarios/mobile/app-startup.json +72 -0
  146. package/examples/scenarios/mobile/media-open-close.json +141 -0
  147. package/examples/scenarios/mobile/open-close-cycle.json +135 -0
  148. package/examples/scenarios/mobile/scroll-settle.json +106 -0
  149. package/package.json +240 -0
  150. package/schemas/budget-verdict.schema.json +115 -0
  151. package/schemas/causal-run.schema.json +279 -0
  152. package/schemas/comparison.schema.json +196 -0
  153. package/schemas/health.schema.json +108 -0
  154. package/schemas/live-proof-set.schema.json +195 -0
  155. package/schemas/live-proof.schema.json +413 -0
  156. package/schemas/manifest.schema.json +204 -0
  157. package/schemas/metrics.schema.json +137 -0
  158. package/schemas/project-validation.schema.json +343 -0
  159. package/schemas/runner-capabilities.schema.json +217 -0
  160. package/schemas/scenario.schema.json +400 -0
  161. package/schemas/verdict.schema.json +88 -0
  162. package/templates/evidence-provider.json +83 -0
  163. package/templates/gitignore-snippet +9 -0
  164. package/templates/integration-readme.md +125 -0
  165. package/templates/mobile-scenario.json +133 -0
  166. package/templates/package-scripts.json +32 -0
  167. package/templates/primary-runner.json +19 -0
  168. package/templates/project.config.json +37 -0
  169. package/templates/scripts/asl-capture-accessibility-provider.mjs +112 -0
  170. package/templates/scripts/asl-capture-profiler-provider.mjs +127 -0
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ type CliArgs = {
3
+ 'dry-run'?: string | boolean;
4
+ force?: string | boolean;
5
+ out?: string | boolean;
6
+ scenario?: string | boolean;
7
+ [key: string]: string | boolean | undefined;
8
+ };
9
+ type ScaffoldFile = {
10
+ destination: string;
11
+ scenarioId?: string;
12
+ source: string;
13
+ transform?: 'scenario' | 'template';
14
+ };
15
+ type InitProjectOptions = {
16
+ dryRun?: boolean;
17
+ force?: boolean;
18
+ outDir?: string;
19
+ packageRoot?: string;
20
+ scenarioId?: string;
21
+ };
22
+ type InitProjectResult = {
23
+ created: string[];
24
+ skipped: string[];
25
+ targetDir: string;
26
+ };
27
+ /**
28
+ * Prints CLI usage.
29
+ *
30
+ * @param {{write: (message: string) => unknown}} [output]
31
+ * @returns {void}
32
+ */
33
+ declare function usage(output?: {
34
+ write: (message: string) => unknown;
35
+ }): void;
36
+ /**
37
+ * Parses `--key value` arguments for the init CLI.
38
+ *
39
+ * @param {string[]} argv
40
+ * @returns {CliArgs}
41
+ */
42
+ declare function parseArgs(argv: string[]): CliArgs;
43
+ /**
44
+ * Resolves the installed package root from source or built CLI execution.
45
+ *
46
+ * @returns {string}
47
+ */
48
+ declare function defaultPackageRoot(): string;
49
+ /**
50
+ * Normalizes a scenario id into a portable filename stem.
51
+ *
52
+ * @param {string | undefined} value
53
+ * @returns {string}
54
+ */
55
+ declare function normalizeScenarioId(value: string | undefined): string;
56
+ /**
57
+ * Builds the scaffold file map for a consuming app.
58
+ *
59
+ * @param {{packageRoot: string, scenarioId: string, targetDir: string}} options
60
+ * @returns {ScaffoldFile[]}
61
+ */
62
+ declare function buildScaffoldFiles({ packageRoot, scenarioId, targetDir, }: {
63
+ packageRoot: string;
64
+ scenarioId: string;
65
+ targetDir: string;
66
+ }): ScaffoldFile[];
67
+ /**
68
+ * Scaffolds Agent Scenario Loop starter files into a consuming app directory.
69
+ *
70
+ * @param {InitProjectOptions} [options]
71
+ * @returns {Promise<InitProjectResult>}
72
+ */
73
+ declare function initProject(options?: InitProjectOptions): Promise<InitProjectResult>;
74
+ /**
75
+ * Formats init output for humans and agents.
76
+ *
77
+ * @param {InitProjectResult} result
78
+ * @returns {string}
79
+ */
80
+ declare function formatResult(result: InitProjectResult): string;
81
+ /**
82
+ * Runs the init CLI.
83
+ *
84
+ * @returns {Promise<void>}
85
+ */
86
+ declare function main(): Promise<void>;
87
+ export { buildScaffoldFiles, defaultPackageRoot, formatResult, initProject, main, normalizeScenarioId, parseArgs, usage, };
88
+ export type { CliArgs, InitProjectOptions, InitProjectResult, ScaffoldFile, };
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.buildScaffoldFiles = buildScaffoldFiles;
5
+ exports.defaultPackageRoot = defaultPackageRoot;
6
+ exports.formatResult = formatResult;
7
+ exports.initProject = initProject;
8
+ exports.main = main;
9
+ exports.normalizeScenarioId = normalizeScenarioId;
10
+ exports.parseArgs = parseArgs;
11
+ exports.usage = usage;
12
+ const fs = require('node:fs');
13
+ const fsp = require('node:fs/promises');
14
+ const path = require('node:path');
15
+ const { hasHelpFlag, writeUsage } = require('./cli');
16
+ const TEMPLATE_FILES = {
17
+ config: 'project.config.json',
18
+ evidenceProvider: 'evidence-provider.json',
19
+ gitignoreSnippet: 'gitignore-snippet',
20
+ integrationReadme: 'integration-readme.md',
21
+ packageScripts: 'package-scripts.json',
22
+ primaryRunner: 'primary-runner.json',
23
+ accessibilityProviderScript: path.join('scripts', 'asl-capture-accessibility-provider.mjs'),
24
+ profilerProviderScript: path.join('scripts', 'asl-capture-profiler-provider.mjs'),
25
+ scenario: 'mobile-scenario.json',
26
+ };
27
+ /**
28
+ * Prints CLI usage.
29
+ *
30
+ * @param {{write: (message: string) => unknown}} [output]
31
+ * @returns {void}
32
+ */
33
+ function usage(output = process.stderr) {
34
+ writeUsage([
35
+ 'Usage: asl-init [--out <dir>] [--scenario <id>] [--force] [--dry-run]',
36
+ '',
37
+ 'Copies public Agent Scenario Loop templates into a consuming app layout.',
38
+ 'Refuses to overwrite existing files unless --force is provided.',
39
+ ], output);
40
+ }
41
+ /**
42
+ * Parses `--key value` arguments for the init CLI.
43
+ *
44
+ * @param {string[]} argv
45
+ * @returns {CliArgs}
46
+ */
47
+ function parseArgs(argv) {
48
+ const args = {};
49
+ for (let index = 0; index < argv.length; index += 1) {
50
+ const token = argv[index];
51
+ if (token === '--') {
52
+ continue;
53
+ }
54
+ if (!token || !token.startsWith('--')) {
55
+ continue;
56
+ }
57
+ const key = token.slice(2);
58
+ const value = argv[index + 1];
59
+ if (value && !value.startsWith('--')) {
60
+ args[key] = value;
61
+ index += 1;
62
+ }
63
+ else {
64
+ args[key] = true;
65
+ }
66
+ }
67
+ return args;
68
+ }
69
+ /**
70
+ * Resolves the installed package root from source or built CLI execution.
71
+ *
72
+ * @returns {string}
73
+ */
74
+ function defaultPackageRoot() {
75
+ return path.resolve(__dirname, '..', '..');
76
+ }
77
+ /**
78
+ * Returns true when a boolean-style CLI value is enabled.
79
+ *
80
+ * @param {string | boolean | undefined} value
81
+ * @returns {boolean}
82
+ */
83
+ function isEnabled(value) {
84
+ return value === true || value === 'true';
85
+ }
86
+ /**
87
+ * Normalizes a scenario id into a portable filename stem.
88
+ *
89
+ * @param {string | undefined} value
90
+ * @returns {string}
91
+ */
92
+ function normalizeScenarioId(value) {
93
+ const normalized = (value || 'first-journey')
94
+ .trim()
95
+ .toLowerCase()
96
+ .replace(/[^a-z0-9]+/gu, '-')
97
+ .replace(/^-+|-+$/gu, '');
98
+ return normalized || 'first-journey';
99
+ }
100
+ /**
101
+ * Builds the scaffold file map for a consuming app.
102
+ *
103
+ * @param {{packageRoot: string, scenarioId: string, targetDir: string}} options
104
+ * @returns {ScaffoldFile[]}
105
+ */
106
+ function buildScaffoldFiles({ packageRoot, scenarioId, targetDir, }) {
107
+ const templatesRoot = path.join(packageRoot, 'templates');
108
+ return [
109
+ {
110
+ source: path.join(templatesRoot, TEMPLATE_FILES.config),
111
+ destination: path.join(targetDir, 'asl.config.json'),
112
+ },
113
+ {
114
+ source: path.join(templatesRoot, TEMPLATE_FILES.scenario),
115
+ destination: path.join(targetDir, 'scenarios', 'mobile', `${scenarioId}.json`),
116
+ scenarioId,
117
+ transform: 'scenario',
118
+ },
119
+ {
120
+ source: path.join(templatesRoot, TEMPLATE_FILES.primaryRunner),
121
+ destination: path.join(targetDir, 'runner-manifests', 'primary-runner.json'),
122
+ },
123
+ {
124
+ source: path.join(templatesRoot, TEMPLATE_FILES.evidenceProvider),
125
+ destination: path.join(targetDir, 'runner-manifests', 'evidence-provider.json'),
126
+ },
127
+ {
128
+ source: path.join(templatesRoot, TEMPLATE_FILES.accessibilityProviderScript),
129
+ destination: path.join(targetDir, 'scripts', 'asl-capture-accessibility-provider.mjs'),
130
+ },
131
+ {
132
+ source: path.join(templatesRoot, TEMPLATE_FILES.profilerProviderScript),
133
+ destination: path.join(targetDir, 'scripts', 'asl-capture-profiler-provider.mjs'),
134
+ },
135
+ {
136
+ source: path.join(templatesRoot, TEMPLATE_FILES.integrationReadme),
137
+ destination: path.join(targetDir, 'asl', 'README.md'),
138
+ scenarioId,
139
+ transform: 'template',
140
+ },
141
+ {
142
+ source: path.join(templatesRoot, TEMPLATE_FILES.packageScripts),
143
+ destination: path.join(targetDir, 'asl', 'package-scripts.json'),
144
+ scenarioId,
145
+ transform: 'template',
146
+ },
147
+ {
148
+ source: path.join(templatesRoot, TEMPLATE_FILES.gitignoreSnippet),
149
+ destination: path.join(targetDir, 'asl', 'gitignore-snippet'),
150
+ },
151
+ {
152
+ source: path.join(packageRoot, 'app', 'profile-session.ts'),
153
+ destination: path.join(targetDir, 'src', 'devtools', 'profile-session.ts'),
154
+ },
155
+ ];
156
+ }
157
+ /**
158
+ * Copies one scaffold template unless it already exists and overwrite is disabled.
159
+ * Scenario templates get their stable id fields aligned with the requested filename.
160
+ *
161
+ * @param {{dryRun: boolean, file: ScaffoldFile, force: boolean}} options
162
+ * @returns {Promise<'created' | 'skipped'>}
163
+ */
164
+ async function copyScaffoldFile({ dryRun, file, force, }) {
165
+ if (fs.existsSync(file.destination) && !force) {
166
+ return 'skipped';
167
+ }
168
+ if (dryRun) {
169
+ return 'created';
170
+ }
171
+ await fsp.mkdir(path.dirname(file.destination), { recursive: true });
172
+ if (file.transform === 'scenario' && file.scenarioId) {
173
+ const scenario = JSON.parse(await fsp.readFile(file.source, 'utf8'));
174
+ scenario.id = file.scenarioId;
175
+ scenario.flowId = file.scenarioId;
176
+ await fsp.writeFile(file.destination, `${JSON.stringify(scenario, null, 2)}\n`, 'utf8');
177
+ }
178
+ else if (file.transform === 'template' && file.scenarioId) {
179
+ const template = await fsp.readFile(file.source, 'utf8');
180
+ await fsp.writeFile(file.destination, template.replace(/\{\{SCENARIO_ID\}\}/gu, file.scenarioId), 'utf8');
181
+ }
182
+ else {
183
+ await fsp.copyFile(file.source, file.destination);
184
+ }
185
+ return 'created';
186
+ }
187
+ /**
188
+ * Scaffolds Agent Scenario Loop starter files into a consuming app directory.
189
+ *
190
+ * @param {InitProjectOptions} [options]
191
+ * @returns {Promise<InitProjectResult>}
192
+ */
193
+ async function initProject(options = {}) {
194
+ const packageRoot = options.packageRoot ?? defaultPackageRoot();
195
+ const targetDir = path.resolve(options.outDir ?? process.cwd());
196
+ const scenarioId = normalizeScenarioId(options.scenarioId);
197
+ const files = buildScaffoldFiles({ packageRoot, scenarioId, targetDir });
198
+ const created = [];
199
+ const skipped = [];
200
+ for (const file of files) {
201
+ const result = await copyScaffoldFile({
202
+ dryRun: Boolean(options.dryRun),
203
+ file,
204
+ force: Boolean(options.force),
205
+ });
206
+ const relativeDestination = path.relative(targetDir, file.destination).split(path.sep).join('/');
207
+ if (result === 'created') {
208
+ created.push(relativeDestination);
209
+ }
210
+ else {
211
+ skipped.push(relativeDestination);
212
+ }
213
+ }
214
+ return {
215
+ created,
216
+ skipped,
217
+ targetDir,
218
+ };
219
+ }
220
+ /**
221
+ * Formats init output for humans and agents.
222
+ *
223
+ * @param {InitProjectResult} result
224
+ * @returns {string}
225
+ */
226
+ function formatResult(result) {
227
+ const lines = [
228
+ `Agent Scenario Loop files initialized in ${result.targetDir}`,
229
+ ];
230
+ if (result.created.length > 0) {
231
+ lines.push('created:', ...result.created.map((filePath) => `- ${filePath}`));
232
+ }
233
+ if (result.skipped.length > 0) {
234
+ lines.push('skipped:', ...result.skipped.map((filePath) => `- ${filePath}`));
235
+ }
236
+ return lines.join('\n');
237
+ }
238
+ /**
239
+ * Runs the init CLI.
240
+ *
241
+ * @returns {Promise<void>}
242
+ */
243
+ async function main() {
244
+ const argv = process.argv.slice(2);
245
+ if (hasHelpFlag(argv)) {
246
+ usage(process.stdout);
247
+ return;
248
+ }
249
+ const args = parseArgs(argv);
250
+ const result = await initProject({
251
+ dryRun: isEnabled(args['dry-run']),
252
+ force: isEnabled(args.force),
253
+ ...(typeof args.out === 'string' ? { outDir: args.out } : {}),
254
+ ...(typeof args.scenario === 'string' ? { scenarioId: args.scenario } : {}),
255
+ });
256
+ process.stdout.write(`${formatResult(result)}\n`);
257
+ }
258
+ if (require.main === module) {
259
+ main().catch((error) => {
260
+ console.error(error instanceof Error ? error.message : String(error));
261
+ process.exitCode = 1;
262
+ });
263
+ }
@@ -0,0 +1,69 @@
1
+ type IosSimctlCommandResult = {
2
+ action: string;
3
+ args: string[];
4
+ command: string;
5
+ exitCode: number;
6
+ rawFileName: string;
7
+ stderr: string;
8
+ stdout: string;
9
+ };
10
+ type IosSimctlCommandExecutor = (command: string, args: string[]) => Promise<{
11
+ args: string[];
12
+ command: string;
13
+ exitCode: number;
14
+ stderr: string;
15
+ stdout: string;
16
+ }>;
17
+ type IosSimctlDriverOptions = {
18
+ deviceUdid: string;
19
+ executor: IosSimctlCommandExecutor;
20
+ xcrunPath: string;
21
+ };
22
+ type IosSimctlDeepLinkOptions = {
23
+ rawFileName?: string;
24
+ url: string;
25
+ };
26
+ type IosSimctlReadLogsOptions = {
27
+ last?: string;
28
+ predicate?: string;
29
+ rawFileName?: string;
30
+ };
31
+ type IosSimctlScreenshotOptions = {
32
+ display?: string;
33
+ imageType?: string;
34
+ mask?: string;
35
+ outputPath: string;
36
+ rawFileName?: string;
37
+ };
38
+ type IosSimctlDriver = {
39
+ appInfo: (bundleId: string) => Promise<IosSimctlCommandResult>;
40
+ launchBundle: (bundleId: string) => Promise<IosSimctlCommandResult>;
41
+ openDeepLink: (options: IosSimctlDeepLinkOptions) => Promise<IosSimctlCommandResult>;
42
+ readLogs: (options?: IosSimctlReadLogsOptions) => Promise<IosSimctlCommandResult>;
43
+ screenshot: (options: IosSimctlScreenshotOptions) => Promise<IosSimctlCommandResult>;
44
+ terminateBundle: (bundleId: string) => Promise<IosSimctlCommandResult>;
45
+ };
46
+ declare const PROFILE_LOG_PREDICATE = "eventMessage CONTAINS \"[profile-event]\" OR eventMessage CONTAINS \"[profile-session]\"";
47
+ /**
48
+ * Combines stdout and stderr into the raw evidence text written by callers.
49
+ *
50
+ * @param {{stdout: string, stderr: string}} result
51
+ * @returns {string}
52
+ */
53
+ declare function formatIosSimctlRawOutput(result: {
54
+ stdout: string;
55
+ stderr: string;
56
+ }): string;
57
+ /**
58
+ * Creates a small simctl-backed iOS driver for lifecycle helpers and evidence capture.
59
+ *
60
+ * The generic driver capabilities exposed here are `readLogs` and `screenshot`.
61
+ * Launching bundles, terminating bundles, and opening deep links remain
62
+ * lifecycle helpers used by built-in runners.
63
+ *
64
+ * @param {IosSimctlDriverOptions} options
65
+ * @returns {IosSimctlDriver}
66
+ */
67
+ declare function createIosSimctlDriver({ deviceUdid, executor, xcrunPath, }: IosSimctlDriverOptions): IosSimctlDriver;
68
+ export { PROFILE_LOG_PREDICATE, createIosSimctlDriver, formatIosSimctlRawOutput, };
69
+ export type { IosSimctlCommandExecutor, IosSimctlCommandResult, IosSimctlDeepLinkOptions, IosSimctlDriver, IosSimctlDriverOptions, IosSimctlReadLogsOptions, IosSimctlScreenshotOptions, };
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROFILE_LOG_PREDICATE = void 0;
4
+ exports.createIosSimctlDriver = createIosSimctlDriver;
5
+ exports.formatIosSimctlRawOutput = formatIosSimctlRawOutput;
6
+ const PROFILE_LOG_PREDICATE = 'eventMessage CONTAINS "[profile-event]" OR eventMessage CONTAINS "[profile-session]"';
7
+ exports.PROFILE_LOG_PREDICATE = PROFILE_LOG_PREDICATE;
8
+ /**
9
+ * Adds stable driver metadata to one simctl command result.
10
+ *
11
+ * @param {{action: string, rawFileName: string, result: Awaited<ReturnType<IosSimctlCommandExecutor>>}} options
12
+ * @returns {IosSimctlCommandResult}
13
+ */
14
+ function buildDriverResult({ action, rawFileName, result, }) {
15
+ return {
16
+ action,
17
+ args: result.args,
18
+ command: result.command,
19
+ exitCode: result.exitCode,
20
+ rawFileName,
21
+ stderr: result.stderr,
22
+ stdout: result.stdout,
23
+ };
24
+ }
25
+ /**
26
+ * Combines stdout and stderr into the raw evidence text written by callers.
27
+ *
28
+ * @param {{stdout: string, stderr: string}} result
29
+ * @returns {string}
30
+ */
31
+ function formatIosSimctlRawOutput(result) {
32
+ return [result.stdout, result.stderr].filter(Boolean).join('\n');
33
+ }
34
+ /**
35
+ * Creates a small simctl-backed iOS driver for lifecycle helpers and evidence capture.
36
+ *
37
+ * The generic driver capabilities exposed here are `readLogs` and `screenshot`.
38
+ * Launching bundles, terminating bundles, and opening deep links remain
39
+ * lifecycle helpers used by built-in runners.
40
+ *
41
+ * @param {IosSimctlDriverOptions} options
42
+ * @returns {IosSimctlDriver}
43
+ */
44
+ function createIosSimctlDriver({ deviceUdid, executor, xcrunPath, }) {
45
+ return {
46
+ async appInfo(bundleId) {
47
+ const rawFileName = 'ios-app-info.txt';
48
+ const result = await executor(xcrunPath, ['simctl', 'appinfo', deviceUdid, bundleId]);
49
+ return buildDriverResult({ action: 'appInfo', rawFileName, result });
50
+ },
51
+ async launchBundle(bundleId) {
52
+ const rawFileName = 'ios-launch.txt';
53
+ const result = await executor(xcrunPath, ['simctl', 'launch', deviceUdid, bundleId]);
54
+ return buildDriverResult({ action: 'launchBundle', rawFileName, result });
55
+ },
56
+ async openDeepLink({ rawFileName = 'ios-deep-link.txt', url, }) {
57
+ const result = await executor(xcrunPath, ['simctl', 'openurl', deviceUdid, url]);
58
+ return buildDriverResult({ action: 'openDeepLink', rawFileName, result });
59
+ },
60
+ async readLogs({ last = '2m', predicate = PROFILE_LOG_PREDICATE, rawFileName = 'ios-simctl-log.txt', } = {}) {
61
+ const result = await executor(xcrunPath, [
62
+ 'simctl',
63
+ 'spawn',
64
+ deviceUdid,
65
+ 'log',
66
+ 'show',
67
+ '--style',
68
+ 'compact',
69
+ '--last',
70
+ last,
71
+ '--predicate',
72
+ predicate,
73
+ ]);
74
+ return buildDriverResult({ action: 'readLogs', rawFileName, result });
75
+ },
76
+ async screenshot({ display, imageType, mask, outputPath, rawFileName = 'ios-screenshot.txt', }) {
77
+ const args = ['simctl', 'io', deviceUdid, 'screenshot'];
78
+ if (imageType) {
79
+ args.push(`--type=${imageType}`);
80
+ }
81
+ if (display) {
82
+ args.push(`--display=${display}`);
83
+ }
84
+ if (mask) {
85
+ args.push(`--mask=${mask}`);
86
+ }
87
+ args.push(outputPath);
88
+ const result = await executor(xcrunPath, args);
89
+ return buildDriverResult({ action: 'screenshot', rawFileName, result });
90
+ },
91
+ async terminateBundle(bundleId) {
92
+ const rawFileName = 'ios-terminate.txt';
93
+ const result = await executor(xcrunPath, ['simctl', 'terminate', deviceUdid, bundleId]);
94
+ return buildDriverResult({ action: 'terminateBundle', rawFileName, result });
95
+ },
96
+ };
97
+ }