snapdrive-ios 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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +95 -0
  3. package/README.md +95 -0
  4. package/dist/cli.d.ts +7 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +265 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/core/command-executor.d.ts +15 -0
  9. package/dist/core/command-executor.d.ts.map +1 -0
  10. package/dist/core/command-executor.js +64 -0
  11. package/dist/core/command-executor.js.map +1 -0
  12. package/dist/core/element-finder.d.ts +81 -0
  13. package/dist/core/element-finder.d.ts.map +1 -0
  14. package/dist/core/element-finder.js +246 -0
  15. package/dist/core/element-finder.js.map +1 -0
  16. package/dist/core/idb-client.d.ts +68 -0
  17. package/dist/core/idb-client.d.ts.map +1 -0
  18. package/dist/core/idb-client.js +327 -0
  19. package/dist/core/idb-client.js.map +1 -0
  20. package/dist/core/image-differ.d.ts +55 -0
  21. package/dist/core/image-differ.d.ts.map +1 -0
  22. package/dist/core/image-differ.js +211 -0
  23. package/dist/core/image-differ.js.map +1 -0
  24. package/dist/core/index.d.ts +9 -0
  25. package/dist/core/index.d.ts.map +1 -0
  26. package/dist/core/index.js +9 -0
  27. package/dist/core/index.js.map +1 -0
  28. package/dist/core/report-generator.d.ts +31 -0
  29. package/dist/core/report-generator.d.ts.map +1 -0
  30. package/dist/core/report-generator.js +675 -0
  31. package/dist/core/report-generator.js.map +1 -0
  32. package/dist/core/scenario-runner.d.ts +54 -0
  33. package/dist/core/scenario-runner.d.ts.map +1 -0
  34. package/dist/core/scenario-runner.js +701 -0
  35. package/dist/core/scenario-runner.js.map +1 -0
  36. package/dist/core/simctl-client.d.ts +64 -0
  37. package/dist/core/simctl-client.d.ts.map +1 -0
  38. package/dist/core/simctl-client.js +214 -0
  39. package/dist/core/simctl-client.js.map +1 -0
  40. package/dist/index.d.ts +7 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +11 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/interfaces/config.interface.d.ts +37 -0
  45. package/dist/interfaces/config.interface.d.ts.map +1 -0
  46. package/dist/interfaces/config.interface.js +14 -0
  47. package/dist/interfaces/config.interface.js.map +1 -0
  48. package/dist/interfaces/element.interface.d.ts +49 -0
  49. package/dist/interfaces/element.interface.d.ts.map +1 -0
  50. package/dist/interfaces/element.interface.js +5 -0
  51. package/dist/interfaces/element.interface.js.map +1 -0
  52. package/dist/interfaces/index.d.ts +7 -0
  53. package/dist/interfaces/index.d.ts.map +1 -0
  54. package/dist/interfaces/index.js +7 -0
  55. package/dist/interfaces/index.js.map +1 -0
  56. package/dist/interfaces/scenario.interface.d.ts +101 -0
  57. package/dist/interfaces/scenario.interface.d.ts.map +1 -0
  58. package/dist/interfaces/scenario.interface.js +5 -0
  59. package/dist/interfaces/scenario.interface.js.map +1 -0
  60. package/dist/server.d.ts +28 -0
  61. package/dist/server.d.ts.map +1 -0
  62. package/dist/server.js +943 -0
  63. package/dist/server.js.map +1 -0
  64. package/dist/utils/index.d.ts +5 -0
  65. package/dist/utils/index.d.ts.map +1 -0
  66. package/dist/utils/index.js +5 -0
  67. package/dist/utils/index.js.map +1 -0
  68. package/dist/utils/logger.d.ts +24 -0
  69. package/dist/utils/logger.d.ts.map +1 -0
  70. package/dist/utils/logger.js +50 -0
  71. package/dist/utils/logger.js.map +1 -0
  72. package/package.json +67 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Masami
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.ja.md ADDED
@@ -0,0 +1,95 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/header.png">
4
+ <source media="(prefers-color-scheme: light)" srcset="docs/images/header.png">
5
+ <img src="docs/images/header.png" alt="SnapDrive" width="800" style="max-width: 100%; height: auto;">
6
+ </picture>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="README.md">English</a> | <a href="README.ja.md">日本語</a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ # SnapDrive
16
+
17
+ iOS用のスナップショットテストツール。AI Agentを活用してiOS Simulatorを自律的に操作し、テストシナリオとBaselineを自動生成します。UIの変更にも柔軟に対応でき、テストケースの陳腐化を防ぎます。
18
+
19
+ ## 必要環境
20
+
21
+ - macOS + Xcode
22
+ - Node.js 20+
23
+ - Python 3.x + fb-idb
24
+
25
+ ## セットアップ
26
+
27
+ ### 1. fb-idb (Python) をインストール
28
+
29
+ ```bash
30
+ pip install fb-idb
31
+ ```
32
+
33
+ ### 2. Claude Desktop/Codeに設定
34
+
35
+ `.mcp.json`に追加:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "snapdrive": {
41
+ "command": "npx",
42
+ "args": ["snapdrive-ios-mcp"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ 環境変数を設定する場合:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "snapdrive": {
54
+ "command": "npx",
55
+ "args": ["snapdrive-ios-mcp"],
56
+ "env": {
57
+ "SNAPDRIVE_RESULTS_DIR": "/path/to/your/project/results",
58
+ "SNAPDRIVE_LOG_LEVEL": "debug"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ### 環境変数
66
+
67
+ | 変数 | 説明 | デフォルト |
68
+ |------|------|-----------|
69
+ | `SNAPDRIVE_RESULTS_DIR` | 結果出力先 | `./results` |
70
+ | `SNAPDRIVE_LOG_LEVEL` | ログレベル (debug/info/warn/error) | `info` |
71
+
72
+ ## バージョン管理
73
+
74
+ `.snapdrive`ディレクトリはGitにコミットしてください:
75
+
76
+ ```bash
77
+ git add .snapdrive/
78
+ git commit -m "Add SnapDrive test cases and baselines"
79
+ ```
80
+
81
+ テストシナリオとBaselineをチームで共有することで、全環境で一貫したビジュアルリグレッションテストが可能になります。
82
+
83
+ > **Note**: `results/`ディレクトリはテスト実行結果のため、`.gitignore`に追加してください。
84
+
85
+ ## ドキュメント
86
+
87
+ - [使い方ガイド](docs/usage.md) - 基本的な操作方法
88
+ - [テストケース](docs/test-cases.md) - 構造化テストの作成と実行
89
+ - [CLI](docs/cli.md) - コマンドラインツールの使い方
90
+ - [Fastlane統合](docs/fastlane.md) - FastlaneでのCI/CD連携
91
+ - [MCPツール一覧](docs/tools.md) - 利用可能なツールのリファレンス
92
+
93
+ ## ライセンス
94
+
95
+ MIT
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/header.png">
4
+ <source media="(prefers-color-scheme: light)" srcset="docs/images/header.png">
5
+ <img src="docs/images/header.png" alt="SnapDrive" width="800" style="max-width: 100%; height: auto;">
6
+ </picture>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="README.md">English</a> | <a href="README.ja.md">日本語</a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ # SnapDrive
16
+
17
+ Snapshot testing tool for iOS. Leverages AI Agents to autonomously operate iOS Simulator, automatically generating test scenarios and baselines. Adapts flexibly to UI changes, preventing test case decay.
18
+
19
+ ## Requirements
20
+
21
+ - macOS + Xcode
22
+ - Node.js 20+
23
+ - Python 3.x + fb-idb
24
+
25
+ ## Setup
26
+
27
+ ### 1. Install fb-idb (Python)
28
+
29
+ ```bash
30
+ pip install fb-idb
31
+ ```
32
+
33
+ ### 2. Configure Claude Desktop/Code
34
+
35
+ Add to `.mcp.json`:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "snapdrive": {
41
+ "command": "npx",
42
+ "args": ["snapdrive-ios-mcp"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ With environment variables:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "snapdrive": {
54
+ "command": "npx",
55
+ "args": ["snapdrive-ios-mcp"],
56
+ "env": {
57
+ "SNAPDRIVE_RESULTS_DIR": "/path/to/your/project/results",
58
+ "SNAPDRIVE_LOG_LEVEL": "debug"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ### Environment Variables
66
+
67
+ | Variable | Description | Default |
68
+ |----------|-------------|---------|
69
+ | `SNAPDRIVE_RESULTS_DIR` | Results output directory | `./results` |
70
+ | `SNAPDRIVE_LOG_LEVEL` | Log level (debug/info/warn/error) | `info` |
71
+
72
+ ## Version Control
73
+
74
+ The `.snapdrive` directory should be committed to Git:
75
+
76
+ ```bash
77
+ git add .snapdrive/
78
+ git commit -m "Add SnapDrive test cases and baselines"
79
+ ```
80
+
81
+ This allows your team to share test scenarios and baselines, ensuring consistent visual regression testing across all environments.
82
+
83
+ > **Note**: The `results/` directory contains test execution outputs and should be added to `.gitignore`.
84
+
85
+ ## Documentation
86
+
87
+ - [Usage Guide](docs/usage.md) - Basic operations
88
+ - [Test Cases](docs/test-cases.md) - Creating and running structured tests
89
+ - [CLI](docs/cli.md) - Command-line tool usage
90
+ - [Fastlane Integration](docs/fastlane.md) - CI/CD with Fastlane
91
+ - [MCP Tools Reference](docs/tools.md) - Available tools reference
92
+
93
+ ## License
94
+
95
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SnapDrive CLI
4
+ * Run tests directly from command line without Claude/MCP
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG"}
package/dist/cli.js ADDED
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SnapDrive CLI
4
+ * Run tests directly from command line without Claude/MCP
5
+ */
6
+ import { join } from 'node:path';
7
+ import { mkdir } from 'node:fs/promises';
8
+ import { IDBClient } from './core/idb-client.js';
9
+ import { SimctlClient } from './core/simctl-client.js';
10
+ import { ElementFinder } from './core/element-finder.js';
11
+ import { ImageDiffer } from './core/image-differ.js';
12
+ import { ScenarioRunner } from './core/scenario-runner.js';
13
+ import { ReportGenerator } from './core/report-generator.js';
14
+ import { Logger } from './utils/logger.js';
15
+ const VERSION = '0.1.0';
16
+ function printHelp() {
17
+ console.log(`
18
+ SnapDrive CLI v${VERSION}
19
+ iOS Simulator UI testing tool
20
+
21
+ Usage:
22
+ snapdrive <command> [options]
23
+
24
+ Commands:
25
+ list List all test cases
26
+ run <test-case-id> Run a specific test case
27
+ run --all Run all test cases
28
+ help Show this help message
29
+ version Show version
30
+
31
+ Options:
32
+ --all Run all test cases
33
+ --update-baselines Update baseline screenshots instead of comparing
34
+ --snapdrive-dir <path> Path to .snapdrive directory (default: ./.snapdrive)
35
+ --results-dir <path> Path to results directory (default: ./results)
36
+ --device <udid> Target simulator UDID
37
+ --verbose Enable verbose logging
38
+
39
+ Examples:
40
+ snapdrive list
41
+ snapdrive run login-flow
42
+ snapdrive run login-flow --update-baselines
43
+ snapdrive run --all
44
+ snapdrive run --all --verbose
45
+ `);
46
+ }
47
+ function parseArgs(args) {
48
+ const options = {
49
+ command: 'help',
50
+ };
51
+ let i = 0;
52
+ while (i < args.length) {
53
+ const arg = args[i];
54
+ if (arg === 'list') {
55
+ options.command = 'list';
56
+ }
57
+ else if (arg === 'run') {
58
+ options.command = 'run';
59
+ }
60
+ else if (arg === 'help' || arg === '--help' || arg === '-h') {
61
+ options.command = 'help';
62
+ }
63
+ else if (arg === 'version' || arg === '--version' || arg === '-v') {
64
+ options.command = 'version';
65
+ }
66
+ else if (arg === '--all' || arg === '-a') {
67
+ options.all = true;
68
+ }
69
+ else if (arg === '--update-baselines' || arg === '-u') {
70
+ options.updateBaselines = true;
71
+ }
72
+ else if (arg === '--verbose') {
73
+ options.verbose = true;
74
+ }
75
+ else if (arg === '--snapdrive-dir' && args[i + 1]) {
76
+ options.snapdriveDir = args[++i];
77
+ }
78
+ else if (arg === '--results-dir' && args[i + 1]) {
79
+ options.resultsDir = args[++i];
80
+ }
81
+ else if (arg === '--device' && args[i + 1]) {
82
+ options.deviceUdid = args[++i];
83
+ }
84
+ else if (!arg.startsWith('-') && options.command === 'run' && !options.testCaseId) {
85
+ options.testCaseId = arg;
86
+ }
87
+ i++;
88
+ }
89
+ return options;
90
+ }
91
+ async function createRunner(verbose) {
92
+ const logLevel = verbose ? 'debug' : 'info';
93
+ const logger = new Logger('snapdrive-cli', logLevel);
94
+ const idbClient = new IDBClient({}, undefined, logger);
95
+ const simctlClient = new SimctlClient({}, undefined, logger);
96
+ const elementFinder = new ElementFinder();
97
+ const imageDiffer = new ImageDiffer(logger);
98
+ const scenarioRunner = new ScenarioRunner({
99
+ idbClient,
100
+ simctlClient,
101
+ elementFinder,
102
+ imageDiffer,
103
+ logger,
104
+ });
105
+ const reportGenerator = new ReportGenerator(logger);
106
+ return { scenarioRunner, reportGenerator, logger };
107
+ }
108
+ async function listTestCases(options) {
109
+ const snapdriveDir = options.snapdriveDir ?? join(process.cwd(), '.snapdrive');
110
+ const { scenarioRunner } = await createRunner(options.verbose ?? false);
111
+ console.log(`\nLooking for test cases in: ${snapdriveDir}/test-cases\n`);
112
+ const testCases = await scenarioRunner.listTestCases(snapdriveDir);
113
+ if (testCases.length === 0) {
114
+ console.log('No test cases found.');
115
+ console.log('\nCreate a test case with Claude:');
116
+ console.log(' "login-flowという名前でテストケースを作成して"');
117
+ return;
118
+ }
119
+ console.log(`Found ${testCases.length} test case(s):\n`);
120
+ console.log('─'.repeat(60));
121
+ for (const tc of testCases) {
122
+ console.log(` ${tc.id}`);
123
+ console.log(` Name: ${tc.scenario.name}`);
124
+ if (tc.scenario.description) {
125
+ console.log(` Description: ${tc.scenario.description}`);
126
+ }
127
+ console.log(` Steps: ${tc.scenario.steps.length}`);
128
+ console.log('');
129
+ }
130
+ }
131
+ async function runTests(options) {
132
+ const snapdriveDir = options.snapdriveDir ?? join(process.cwd(), '.snapdrive');
133
+ const baseResultsDir = options.resultsDir ?? join(process.cwd(), 'results');
134
+ const resultsDir = join(baseResultsDir, new Date().toISOString().replace(/[:.]/g, '-'));
135
+ const { scenarioRunner, reportGenerator } = await createRunner(options.verbose ?? false);
136
+ // Ensure results directory exists (do not clean up previous results)
137
+ await mkdir(resultsDir, { recursive: true });
138
+ await mkdir(join(resultsDir, 'screenshots'), { recursive: true });
139
+ await mkdir(join(resultsDir, 'diffs'), { recursive: true });
140
+ let testCases;
141
+ if (options.all) {
142
+ testCases = await scenarioRunner.listTestCases(snapdriveDir);
143
+ if (testCases.length === 0) {
144
+ console.error('No test cases found.');
145
+ process.exit(1);
146
+ }
147
+ console.log(`\nRunning all ${testCases.length} test case(s)...\n`);
148
+ }
149
+ else if (options.testCaseId) {
150
+ const tcPath = join(snapdriveDir, 'test-cases', options.testCaseId);
151
+ try {
152
+ const testCase = await scenarioRunner.loadTestCase(tcPath);
153
+ testCases = [testCase];
154
+ console.log(`\nRunning test case: ${options.testCaseId}\n`);
155
+ }
156
+ catch (error) {
157
+ console.error(`Error: Test case not found: ${options.testCaseId}`);
158
+ console.error(`Expected path: ${tcPath}`);
159
+ process.exit(1);
160
+ }
161
+ }
162
+ else {
163
+ console.error('Error: Specify a test case ID or use --all');
164
+ console.error('Usage: snapdrive run <test-case-id>');
165
+ console.error(' snapdrive run --all');
166
+ process.exit(1);
167
+ }
168
+ const startTime = new Date();
169
+ const results = {
170
+ runId: new Date().toISOString().replace(/[:.]/g, '-'),
171
+ startTime: startTime.toISOString(),
172
+ endTime: '',
173
+ durationMs: 0,
174
+ totalTests: testCases.length,
175
+ passed: 0,
176
+ failed: 0,
177
+ results: [],
178
+ resultsDir,
179
+ };
180
+ console.log('─'.repeat(60));
181
+ for (const testCase of testCases) {
182
+ const mode = options.updateBaselines ? '[UPDATE BASELINES]' : '[COMPARE]';
183
+ console.log(`\n${mode} ${testCase.scenario.name}`);
184
+ try {
185
+ const result = await scenarioRunner.runTestCase(testCase, {
186
+ deviceUdid: options.deviceUdid,
187
+ updateBaselines: options.updateBaselines ?? false,
188
+ resultsDir,
189
+ testCasePath: testCase.path,
190
+ });
191
+ results.results.push(result);
192
+ if (result.success) {
193
+ results.passed++;
194
+ console.log(` ✓ PASSED (${(result.durationMs / 1000).toFixed(2)}s)`);
195
+ }
196
+ else {
197
+ results.failed++;
198
+ console.log(` ✗ FAILED (${(result.durationMs / 1000).toFixed(2)}s)`);
199
+ // Show failed steps
200
+ for (const step of result.steps) {
201
+ if (!step.success) {
202
+ console.log(` - Step ${step.stepIndex + 1} (${step.action}): ${step.error}`);
203
+ }
204
+ }
205
+ // Show checkpoint diffs
206
+ for (const cp of result.checkpoints) {
207
+ if (!cp.match) {
208
+ console.log(` - Checkpoint "${cp.name}": ${cp.differencePercent.toFixed(2)}% different`);
209
+ }
210
+ }
211
+ }
212
+ }
213
+ catch (error) {
214
+ results.failed++;
215
+ console.log(` ✗ ERROR: ${String(error)}`);
216
+ }
217
+ }
218
+ const endTime = new Date();
219
+ results.endTime = endTime.toISOString();
220
+ results.durationMs = endTime.getTime() - startTime.getTime();
221
+ console.log('\n' + '─'.repeat(60));
222
+ console.log('\nSummary:');
223
+ console.log(` Total: ${results.totalTests}`);
224
+ console.log(` Passed: ${results.passed}`);
225
+ console.log(` Failed: ${results.failed}`);
226
+ console.log(` Duration: ${(results.durationMs / 1000).toFixed(2)}s`);
227
+ // Generate report if comparing (not updating baselines)
228
+ if (!options.updateBaselines) {
229
+ const reportPath = await reportGenerator.generateReport(results);
230
+ console.log(`\n📄 Report: ${reportPath}`);
231
+ }
232
+ console.log(`📁 Results: ${resultsDir}`);
233
+ console.log('');
234
+ // Exit with error code if tests failed
235
+ if (results.failed > 0) {
236
+ process.exit(1);
237
+ }
238
+ }
239
+ async function main() {
240
+ const args = process.argv.slice(2);
241
+ const options = parseArgs(args);
242
+ try {
243
+ switch (options.command) {
244
+ case 'list':
245
+ await listTestCases(options);
246
+ break;
247
+ case 'run':
248
+ await runTests(options);
249
+ break;
250
+ case 'version':
251
+ console.log(`snapdrive v${VERSION}`);
252
+ break;
253
+ case 'help':
254
+ default:
255
+ printHelp();
256
+ break;
257
+ }
258
+ }
259
+ catch (error) {
260
+ console.error('Error:', String(error));
261
+ process.exit(1);
262
+ }
263
+ }
264
+ main();
265
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Command executor for running subprocess commands
3
+ */
4
+ import type { CommandResult, ExecuteOptions } from '../interfaces/config.interface.js';
5
+ import { type ILogger } from '../utils/logger.js';
6
+ export interface ICommandExecutor {
7
+ execute(command: string, args: string[], options?: ExecuteOptions): Promise<CommandResult>;
8
+ }
9
+ export declare class CommandExecutor implements ICommandExecutor {
10
+ private logger;
11
+ constructor(logger?: ILogger);
12
+ execute(command: string, args: string[], options?: ExecuteOptions): Promise<CommandResult>;
13
+ }
14
+ export declare const commandExecutor: CommandExecutor;
15
+ //# sourceMappingURL=command-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-executor.d.ts","sourceRoot":"","sources":["../../src/core/command-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,oBAAoB,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CACL,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,aAAa,CAAC,CAAC;CAC3B;AAED,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,MAAM,CAAU;gBAEZ,MAAM,CAAC,EAAE,OAAO;IAItB,OAAO,CACX,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC;CA6D1B;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Command executor for running subprocess commands
3
+ */
4
+ import { spawn } from 'node:child_process';
5
+ import { Logger } from '../utils/logger.js';
6
+ export class CommandExecutor {
7
+ logger;
8
+ constructor(logger) {
9
+ this.logger = logger ?? new Logger('command-executor');
10
+ }
11
+ async execute(command, args, options = {}) {
12
+ const { timeoutMs = 30000, cwd, env } = options;
13
+ const fullCommand = `${command} ${args.join(' ')}`;
14
+ this.logger.debug(`Executing: ${fullCommand}`);
15
+ return new Promise((resolve) => {
16
+ let stdout = '';
17
+ let stderr = '';
18
+ let timedOut = false;
19
+ const proc = spawn(command, args, {
20
+ cwd,
21
+ env: env ? { ...process.env, ...env } : process.env,
22
+ shell: false,
23
+ });
24
+ const timeoutId = setTimeout(() => {
25
+ timedOut = true;
26
+ proc.kill('SIGTERM');
27
+ this.logger.warn(`Command timed out after ${timeoutMs}ms: ${fullCommand}`);
28
+ }, timeoutMs);
29
+ proc.stdout.on('data', (data) => {
30
+ stdout += data.toString();
31
+ });
32
+ proc.stderr.on('data', (data) => {
33
+ stderr += data.toString();
34
+ });
35
+ proc.on('close', (code) => {
36
+ clearTimeout(timeoutId);
37
+ const exitCode = code ?? (timedOut ? -1 : 0);
38
+ if (exitCode !== 0 && !timedOut) {
39
+ this.logger.debug(`Command failed with exit code ${exitCode}: ${fullCommand}`, {
40
+ stderr: stderr.slice(0, 200),
41
+ });
42
+ }
43
+ resolve({
44
+ stdout,
45
+ stderr,
46
+ exitCode,
47
+ timedOut,
48
+ });
49
+ });
50
+ proc.on('error', (err) => {
51
+ clearTimeout(timeoutId);
52
+ this.logger.error(`Command error: ${fullCommand}`, { error: err.message });
53
+ resolve({
54
+ stdout,
55
+ stderr: `${stderr}\nError: ${err.message}`,
56
+ exitCode: -1,
57
+ timedOut: false,
58
+ });
59
+ });
60
+ });
61
+ }
62
+ }
63
+ export const commandExecutor = new CommandExecutor();
64
+ //# sourceMappingURL=command-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-executor.js","sourceRoot":"","sources":["../../src/core/command-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Element finder - search and locate UI elements
3
+ */
4
+ import type { AccessibilityElement, Point, ElementSearchResult, ElementPredicate } from '../interfaces/element.interface.js';
5
+ export interface IElementFinder {
6
+ findByLabel(elements: AccessibilityElement[], label: string): AccessibilityElement[];
7
+ findByLabelContains(elements: AccessibilityElement[], partial: string): AccessibilityElement[];
8
+ findByType(elements: AccessibilityElement[], type: string): AccessibilityElement[];
9
+ findByRole(elements: AccessibilityElement[], role: string): AccessibilityElement[];
10
+ findByPredicate(elements: AccessibilityElement[], predicate: ElementPredicate): AccessibilityElement[];
11
+ findBest(elements: AccessibilityElement[], predicate: ElementPredicate, screenCenter?: Point): ElementSearchResult;
12
+ getCenterPoint(element: AccessibilityElement): Point;
13
+ getAllLabels(elements: AccessibilityElement[]): string[];
14
+ findScrollRegion(elements: AccessibilityElement[]): {
15
+ centerX: number;
16
+ centerY: number;
17
+ } | null;
18
+ }
19
+ export declare class ElementFinder implements IElementFinder {
20
+ /**
21
+ * Find elements with exact label match
22
+ */
23
+ findByLabel(elements: AccessibilityElement[], label: string): AccessibilityElement[];
24
+ /**
25
+ * Find elements with partial label match
26
+ */
27
+ findByLabelContains(elements: AccessibilityElement[], partial: string): AccessibilityElement[];
28
+ /**
29
+ * Find elements by type
30
+ */
31
+ findByType(elements: AccessibilityElement[], type: string): AccessibilityElement[];
32
+ /**
33
+ * Find elements by role
34
+ */
35
+ findByRole(elements: AccessibilityElement[], role: string): AccessibilityElement[];
36
+ /**
37
+ * Find elements matching a predicate
38
+ */
39
+ findByPredicate(elements: AccessibilityElement[], predicate: ElementPredicate): AccessibilityElement[];
40
+ /**
41
+ * Find the best matching element and return search result with tap coordinates
42
+ */
43
+ findBest(elements: AccessibilityElement[], predicate: ElementPredicate, screenCenter?: Point): ElementSearchResult;
44
+ /**
45
+ * Calculate center point of element's frame
46
+ */
47
+ getCenterPoint(element: AccessibilityElement): Point;
48
+ /**
49
+ * Get all labels from elements (for debugging/suggestions)
50
+ */
51
+ getAllLabels(elements: AccessibilityElement[]): string[];
52
+ /**
53
+ * Check if element matches predicate
54
+ */
55
+ private matchesPredicate;
56
+ /**
57
+ * Rank elements by priority:
58
+ * 1. Buttons and tappable elements first
59
+ * 2. Enabled elements before disabled
60
+ * 3. Closer to screen center
61
+ */
62
+ private rankElements;
63
+ /**
64
+ * Check if element is button-like (more likely to be tappable)
65
+ */
66
+ private isButtonLike;
67
+ /**
68
+ * Calculate distance from element center to screen center
69
+ */
70
+ private distanceToCenter;
71
+ /**
72
+ * Find the best scroll region by analyzing element frames
73
+ * Returns center point of the largest container that likely contains scrollable content
74
+ */
75
+ findScrollRegion(elements: AccessibilityElement[]): {
76
+ centerX: number;
77
+ centerY: number;
78
+ } | null;
79
+ }
80
+ export declare const elementFinder: ElementFinder;
81
+ //# sourceMappingURL=element-finder.d.ts.map