@mobileaidev/ai-app-bridge 0.2.15 → 0.3.0-rc.2

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 (156) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +7 -0
  3. package/README.md +306 -53
  4. package/bin/ai-app-bridge.js +51 -4504
  5. package/bin/android-permissions.js +152 -0
  6. package/bin/android-uia-xml.js +74 -0
  7. package/bin/artifact-paths.js +127 -1
  8. package/bin/bridge-forward.js +56 -0
  9. package/bin/command-discovery.js +86 -0
  10. package/bin/command-errors.js +60 -0
  11. package/bin/command-registry.js +504 -0
  12. package/bin/command-request.js +14 -0
  13. package/bin/command-router.js +80 -0
  14. package/bin/connection-cache.js +119 -0
  15. package/bin/device-provider.js +2934 -0
  16. package/bin/execution-host.js +596 -0
  17. package/bin/execution-runtime.js +101 -0
  18. package/bin/fact-codec.js +321 -0
  19. package/bin/fact-recorder.js +691 -0
  20. package/bin/fact-store.js +226 -0
  21. package/bin/feedback-probe.js +285 -0
  22. package/bin/intent/install-intent.js +267 -0
  23. package/bin/intent/intent-action-executor.js +129 -0
  24. package/bin/intent/intent-autonomous-adapter.js +46 -0
  25. package/bin/intent/intent-capture-port.js +64 -0
  26. package/bin/intent/intent-entry.js +226 -0
  27. package/bin/intent/intent-errors.js +28 -0
  28. package/bin/intent/intent-evidence-store.js +121 -0
  29. package/bin/intent/intent-lifetime.js +62 -0
  30. package/bin/intent/intent-observation-target.js +33 -0
  31. package/bin/intent/intent-observer.js +194 -0
  32. package/bin/intent/intent-production-adapter.js +334 -0
  33. package/bin/intent/intent-provider.js +27 -0
  34. package/bin/intent/intent-runtime.js +63 -0
  35. package/bin/intent/intent-worker.js +432 -0
  36. package/bin/intent/ios-intent-adapter.js +90 -0
  37. package/bin/intent/permission-intent.js +239 -0
  38. package/bin/intent/web-intent-adapter.js +31 -0
  39. package/bin/ios-device-outcome.js +47 -0
  40. package/bin/ios-execution.js +108 -0
  41. package/bin/ios-provider.js +497 -583
  42. package/bin/ios-runtime-binding.js +54 -0
  43. package/bin/ios-wda-execution.js +70 -0
  44. package/bin/ios-wda-port.js +98 -0
  45. package/bin/ios-wda-project.js +79 -0
  46. package/bin/mcp-server.js +83 -1040
  47. package/bin/mmap-scan-index.js +329 -0
  48. package/bin/observation-collector.js +862 -0
  49. package/bin/runtime-client.js +154 -0
  50. package/bin/runtime-directory.js +107 -0
  51. package/bin/runtime-protocol.js +36 -0
  52. package/bin/script/bounded-script-registry.js +103 -0
  53. package/bin/script/node-runtime-adapter.js +233 -0
  54. package/bin/script/progress-projector.js +63 -0
  55. package/bin/script/python-runtime-adapter.js +111 -0
  56. package/bin/script/rolling-summary.js +134 -0
  57. package/bin/script/script-agent-port.js +40 -0
  58. package/bin/script/script-assert.js +95 -0
  59. package/bin/script/script-capture-port.js +76 -0
  60. package/bin/script/script-catalog.js +85 -0
  61. package/bin/script/script-durable-restore.js +195 -0
  62. package/bin/script/script-entry-code.js +26 -0
  63. package/bin/script/script-entry-route.js +38 -0
  64. package/bin/script/script-entry.js +3 -0
  65. package/bin/script/script-errors.js +29 -0
  66. package/bin/script/script-evidence-store.js +22 -0
  67. package/bin/script/script-format-removed.js +26 -0
  68. package/bin/script/script-host-port.js +397 -0
  69. package/bin/script/script-ledger.js +64 -0
  70. package/bin/script/script-result.js +57 -0
  71. package/bin/script/script-sdk.js +152 -0
  72. package/bin/script/script-sdk.py +153 -0
  73. package/bin/script/script-session-channel.js +127 -0
  74. package/bin/script/script-spec.js +86 -0
  75. package/bin/script/script-supervisor.js +919 -0
  76. package/bin/script/templates/checkpoint-reentry.js +13 -0
  77. package/bin/segment-index.js +481 -0
  78. package/bin/segmented-fact-store.js +1571 -0
  79. package/bin/shared-kernel/android-h5-target.js +10 -0
  80. package/bin/shared-kernel/android-install-execution.js +176 -0
  81. package/bin/shared-kernel/android-sdk-endpoint.js +42 -0
  82. package/bin/shared-kernel/android-shell-execution.js +195 -0
  83. package/bin/shared-kernel/argument-schema.js +117 -0
  84. package/bin/shared-kernel/canonical-path.js +17 -0
  85. package/bin/shared-kernel/device-acknowledgements.js +53 -0
  86. package/bin/shared-kernel/device-completion-history.js +52 -0
  87. package/bin/shared-kernel/device-mutation-lease.js +219 -0
  88. package/bin/shared-kernel/device-ownership-recovery.js +95 -0
  89. package/bin/shared-kernel/device-ownership-store.js +94 -0
  90. package/bin/shared-kernel/evidence-adapters.js +251 -0
  91. package/bin/shared-kernel/evidence-archive.js +329 -0
  92. package/bin/shared-kernel/evidence-recording.js +131 -0
  93. package/bin/shared-kernel/evidence-schema.js +194 -0
  94. package/bin/shared-kernel/evidence-store.js +190 -0
  95. package/bin/shared-kernel/execution-admission.js +22 -0
  96. package/bin/shared-kernel/execution-contracts.js +171 -0
  97. package/bin/shared-kernel/execution-io.js +106 -0
  98. package/bin/shared-kernel/execution-ledger.js +125 -0
  99. package/bin/shared-kernel/execution-scope.js +87 -0
  100. package/bin/shared-kernel/execution-target.js +115 -0
  101. package/bin/shared-kernel/flutter-execution.js +13 -0
  102. package/bin/shared-kernel/flutter-h5-port.js +60 -0
  103. package/bin/shared-kernel/flutter-h5-target.js +9 -0
  104. package/bin/shared-kernel/flutter-target.js +75 -0
  105. package/bin/shared-kernel/h5-execution.js +11 -0
  106. package/bin/shared-kernel/h5-target.js +31 -0
  107. package/bin/shared-kernel/host-fact-store.js +49 -0
  108. package/bin/shared-kernel/ios-h5-target.js +9 -0
  109. package/bin/shared-kernel/ios-native-target.js +71 -0
  110. package/bin/shared-kernel/live-capture-query.js +115 -0
  111. package/bin/shared-kernel/managed-sdk-execution.js +78 -0
  112. package/bin/shared-kernel/native-execution.js +13 -0
  113. package/bin/shared-kernel/native-target.js +156 -0
  114. package/bin/shared-kernel/provider-command-contracts.js +55 -0
  115. package/bin/shared-kernel/recorded-payload-archive.js +195 -0
  116. package/bin/shared-kernel/request-context.js +35 -0
  117. package/bin/shared-kernel/semantic-node.js +55 -0
  118. package/bin/shared-kernel/summary-transformer.js +352 -0
  119. package/bin/shared-kernel/target-lease-protocol.js +47 -0
  120. package/bin/shared-kernel/text-wait.js +111 -0
  121. package/bin/shared-kernel/uia-execution.js +96 -0
  122. package/bin/shared-kernel/uia-protocol.js +214 -0
  123. package/bin/shared-kernel/uia-runtime-port.js +377 -0
  124. package/bin/shared-kernel/uia-target.js +39 -0
  125. package/bin/shared-kernel/web-dom-target.js +44 -0
  126. package/bin/shared-kernel/xml-attributes.js +25 -0
  127. package/bin/target-execution.js +275 -0
  128. package/bin/web/command-schema.js +60 -0
  129. package/bin/web/session-store.js +157 -0
  130. package/bin/web-provider.js +334 -553
  131. package/docs/COMMAND_CONTRACT.md +1563 -0
  132. package/docs/EVIDENCE_ARCHIVE.md +214 -0
  133. package/docs/INTENT_FOREGROUND.md +71 -0
  134. package/docs/INTENT_NATIVE_EDITING.md +79 -0
  135. package/docs/RELEASE.md +59 -0
  136. package/docs/SCRIPT_AUTHORING.md +489 -0
  137. package/node_modules/@mobileaidev/segmented-fact-store-native/LICENSE +201 -0
  138. package/node_modules/@mobileaidev/segmented-fact-store-native/NOTICE +7 -0
  139. package/node_modules/@mobileaidev/segmented-fact-store-native/binding.gyp +36 -0
  140. package/node_modules/@mobileaidev/segmented-fact-store-native/bindings/node/sfs_node.c +597 -0
  141. package/node_modules/@mobileaidev/segmented-fact-store-native/include/sfs.h +178 -0
  142. package/node_modules/@mobileaidev/segmented-fact-store-native/index.js +5 -0
  143. package/node_modules/@mobileaidev/segmented-fact-store-native/package.json +24 -0
  144. package/node_modules/@mobileaidev/segmented-fact-store-native/src/sfs.c +2349 -0
  145. package/package.json +60 -5
  146. package/runtime/ios-wda/AABWDABinding.h +19 -0
  147. package/runtime/ios-wda/AABWDABinding.m +97 -0
  148. package/runtime/ios-wda/AABWDAExecution.h +26 -0
  149. package/runtime/ios-wda/AABWDAExecution.m +172 -0
  150. package/runtime/ios-wda/AABWDAIntegration.h +71 -0
  151. package/runtime/ios-wda/AABWDAManagedRoutes.h +392 -0
  152. package/runtime/ios-wda/AABWDAReceiptStore.h +10 -0
  153. package/runtime/ios-wda/AABWDAReceiptStore.m +116 -0
  154. package/runtime/uia/ai-app-bridge-uia.jar +0 -0
  155. package/runtime/uia/manifest.json +22 -0
  156. package/skills/ai-app-bridge-use/SKILL.md +23 -360
@@ -0,0 +1,392 @@
1
+ // Included by FBWebServer after AABWDAIntegration. Control routes run on each
2
+ // HTTP connection's queue; only UI preparation/event submission use main.
3
+ #import "AABWDAReceiptStore.m"
4
+ #import "AABWDAExecution.m"
5
+ #include "sfs.c"
6
+ #import "FBW3CActionsSynthesizer.h"
7
+ #import "XCUIElement+FBCaching.h"
8
+ #import "XCUIElement+FBUtilities.h"
9
+ #import "XCUIElement+FBFind.h"
10
+ #import "XCUIElement+FBUID.h"
11
+ #import "XCUIElement+FBWebDriverAttributes.h"
12
+ #import "FBElementTypeTransformer.h"
13
+ #import "XCPointerEventPath.h"
14
+ #import "XCSynthesizedEventRecord.h"
15
+ #import "XCUIDevice.h"
16
+ #import "FBXCDeviceEvent.h"
17
+ #import "XCTRunnerDaemonSession.h"
18
+ #import "XCTestManager_ManagerInterface-Protocol.h"
19
+ #import "RoutingHTTPServer.h"
20
+ #import "RouteRequest.h"
21
+ #import "RouteResponse.h"
22
+ #include <math.h>
23
+
24
+ // WDA 14.1.1 uses this exact event-synthesizer callback (BOOL, NSError *).
25
+ // Keep the declaration local instead of borrowing the different daemon API.
26
+ @protocol AABWDAEventSynthesizer <NSObject>
27
+ - (void)synthesizeEvent:(XCSynthesizedEventRecord *)event completion:(void (^)(BOOL, NSError *))completion;
28
+ @end
29
+
30
+ static AABWDAExecution *AABWDARuntime(void)
31
+ {
32
+ static AABWDAExecution *runtime;
33
+ static dispatch_once_t once;
34
+ dispatch_once(&once, ^{
35
+ NSURL *directory = [[NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject
36
+ URLByAppendingPathComponent:@"ai_app_bridge_wda_execution"];
37
+ AABWDAReceiptStore *store = [[AABWDAReceiptStore alloc] initWithDirectory:directory runnerBundleId:NSBundle.mainBundle.bundleIdentifier];
38
+ runtime = [[AABWDAExecution alloc] initWithStore:store epoch:AABWDABinding.shared.identity[@"runtimeEpoch"]];
39
+ });
40
+ return runtime;
41
+ }
42
+
43
+ static void AABWDAWire(RouteResponse *response, NSDictionary *value)
44
+ {
45
+ [[[FBResponseJSONPayload alloc] initWithDictionary:@{@"wdaBinding": AABWDABinding.shared.identity,
46
+ @"sessionId": NSNull.null, @"value": value} httpStatusCode:200] dispatchWithResponse:response];
47
+ }
48
+
49
+ static NSString *AABWDACheckTarget(AABWDATask *task)
50
+ {
51
+ NSDictionary *target = task.target;
52
+ NSString *operation = task.body[@"operation"];
53
+ if ([operation isEqual:@"session-create"]) {
54
+ if (FBSession.activeSession) return @"ios_wda_session_busy";
55
+ } else if (!AABWDASessionTarget || ![FBSession.activeSession.identifier isEqual:target[@"sessionId"]]
56
+ || ![AABWDASessionTarget[@"bundleId"] isEqual:target[@"bundleId"]]
57
+ || ![AABWDASessionTarget[@"processId"] isEqual:target[@"processId"]]) return @"ios_wda_session_changed";
58
+ if ([operation isEqual:@"session-close"]) return nil;
59
+ NSDictionary *actual = AABWDAForeground();
60
+ return [actual[@"bundleId"] isEqual:target[@"bundleId"]] && [actual[@"processId"] isEqual:target[@"processId"]]
61
+ ? nil : @"ios_wda_target_changed";
62
+ }
63
+
64
+ static XCUIElement *AABWDAEditor(AABWDATask *task, BOOL requireFocus);
65
+
66
+ static BOOL AABWDAIsInput(AABWDATask *task)
67
+ {
68
+ return [@[@"input", @"native-input"] containsObject:task.body[@"operation"]];
69
+ }
70
+
71
+ static XCUIElement *AABWDANativeElement(AABWDATask *task, BOOL resolve)
72
+ {
73
+ NSDictionary *ref = task.body[@"payload"][@"targetRef"];
74
+ NSString *uid = ref[@"elementId"];
75
+ FBElementCache *cache = FBSession.activeSession.elementCache;
76
+ XCUIElement *element;
77
+ if (resolve) {
78
+ // Match the observed accessibility element identity, never its old position
79
+ // or a new element that happens to have the same label.
80
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"UID == %@", uid];
81
+ NSArray<XCUIElement *> *matches = [XCUIApplication.fb_activeApplication
82
+ fb_descendantsMatchingPredicate:predicate shouldReturnAfterFirstMatch:NO];
83
+ if (matches.count != 1) return nil;
84
+ element = matches.firstObject;
85
+ if (![[cache storeElement:element] isEqual:uid]) return nil;
86
+ } else {
87
+ element = [cache elementForUUID:uid checkStaleness:YES];
88
+ }
89
+ FBXCElementSnapshotWrapper *snapshot = [FBXCElementSnapshotWrapper ensureWrapped:[element fb_standardSnapshot]];
90
+ NSString *identifier = snapshot.identifier;
91
+ id actualIdentifier = identifier.length ? identifier : NSNull.null;
92
+ return [snapshot.wdUID isEqual:uid] && [actualIdentifier isEqual:ref[@"identifier"]]
93
+ && [(snapshot.wdLabel ?: NSNull.null) isEqual:ref[@"label"]]
94
+ && [[FBElementTypeTransformer shortStringWithElementType:snapshot.elementType] isEqual:ref[@"type"]]
95
+ && snapshot.isWDEnabled && snapshot.isWDVisible ? element : nil;
96
+ }
97
+
98
+ static void AABWDAEvent(AABWDATask *task, XCSynthesizedEventRecord *event, BOOL keyboard, dispatch_block_t completed)
99
+ {
100
+ NSString *error = AABWDACheckTarget(task);
101
+ id<AABWDAEventSynthesizer> synthesizer = [XCUIDevice.sharedDevice eventSynthesizer];
102
+ if (![synthesizer respondsToSelector:@selector(synthesizeEvent:completion:)]) error = @"ios_wda_event_api_unavailable";
103
+ if (!error && task.body[@"payload"][@"targetRef"] && !AABWDANativeElement(task, NO)) error = @"ios_native_target_changed";
104
+ if (!error && AABWDAIsInput(task) && !AABWDAEditor(task, keyboard))
105
+ error = @"ios_wda_input_target_changed";
106
+ if (!error) error = [task permission];
107
+ if (error) { [task finish:AABWDAFailure(error)]; return; }
108
+ @try {
109
+ [synthesizer synthesizeEvent:event completion:^(BOOL succeeded, NSError *failure) {
110
+ dispatch_async(dispatch_get_main_queue(), ^{
111
+ if (!succeeded || failure) { [task finish:AABWDAFailure(@"ios_wda_event_failed")]; return; }
112
+ @try { completed(); }
113
+ @catch (NSException *exception) { [task finish:AABWDAFailure(@"ios_wda_postcondition_failed")]; }
114
+ });
115
+ }];
116
+ } @catch (NSException *exception) {
117
+ // An exception after handing the event to XCTest is not an end callback.
118
+ [task unresolved:@"ios_wda_event_submission_unknown"];
119
+ }
120
+ }
121
+
122
+ static XCSynthesizedEventRecord *AABWDAPointer(NSArray *actions, NSError **error)
123
+ {
124
+ FBW3CActionsSynthesizer *builder = [[FBW3CActionsSynthesizer alloc] initWithActions:@[
125
+ @{@"type": @"pointer", @"id": @"aab-pointer", @"parameters": @{@"pointerType": @"touch"}, @"actions": actions}
126
+ ] forApplication:XCUIApplication.fb_activeApplication elementCache:FBSession.activeSession.elementCache error:error];
127
+ return builder ? [builder synthesizeWithError:error] : nil;
128
+ }
129
+
130
+ static XCUIElement *AABWDAEditor(AABWDATask *task, BOOL requireFocus)
131
+ {
132
+ NSDictionary *payload = task.body[@"payload"];
133
+ NSString *elementId = [task.body[@"operation"] isEqual:@"native-input"] ? payload[@"targetRef"][@"elementId"] : payload[@"elementId"];
134
+ XCUIElement *element = [FBSession.activeSession.elementCache elementForUUID:elementId checkStaleness:YES];
135
+ if (payload[@"targetRef"] && !AABWDANativeElement(task, NO)) return nil;
136
+ id<FBXCElementSnapshot> snapshot = [element fb_standardSnapshot];
137
+ XCUIElementType type = snapshot.elementType;
138
+ if (![element exists] || !snapshot || (type != XCUIElementTypeTextField && type != XCUIElementTypeSecureTextField
139
+ && type != XCUIElementTypeTextView && type != XCUIElementTypeSearchField)) return nil;
140
+ if (requireFocus && !snapshot.hasKeyboardFocus) return nil;
141
+ return element;
142
+ }
143
+
144
+ static void AABWDAClear(AABWDATask *task, dispatch_block_t completed)
145
+ {
146
+ NSError *preparation;
147
+ // The iPhone supports the same keyboard-clear HID event used by upstream
148
+ // WDA. Submit once through the daemon's original NSError completion instead
149
+ // of assuming a desktop Command-A shortcut changed the editor.
150
+ id event = FBCreateXCDeviceEvent(0x07, 0x9c, 0.01, &preparation);
151
+ id<XCTestManager_ManagerInterface> daemon = XCTRunnerDaemonSession.sharedSession.daemonProxy;
152
+ NSString *error = AABWDACheckTarget(task);
153
+ if (!error && !AABWDAEditor(task, YES)) error = @"ios_wda_input_target_changed";
154
+ if (!error && (!event || ![(id)daemon respondsToSelector:@selector(_XCT_performDeviceEvent:completion:)]))
155
+ error = @"ios_wda_clear_api_unavailable";
156
+ if (!error) error = [task permission];
157
+ if (error) { [task finish:AABWDAFailure(error)]; return; }
158
+ @try {
159
+ [daemon _XCT_performDeviceEvent:event completion:^(NSError *failure) {
160
+ dispatch_async(dispatch_get_main_queue(), ^{
161
+ if (failure) { [task finish:AABWDAFailure(@"ios_wda_clear_event_failed")]; return; }
162
+ @try { completed(); }
163
+ @catch (NSException *exception) { [task finish:AABWDAFailure(@"ios_wda_postcondition_failed")]; }
164
+ });
165
+ }];
166
+ } @catch (NSException *exception) {
167
+ [task unresolved:@"ios_wda_clear_submission_unknown"];
168
+ }
169
+ }
170
+
171
+ static void AABWDAInput(AABWDATask *task, BOOL clear)
172
+ {
173
+ if (AABWDACheckTarget(task) || !AABWDAEditor(task, YES)) { [task finish:AABWDAFailure(@"ios_wda_input_target_changed")]; return; }
174
+ NSDictionary *payload = task.body[@"payload"];
175
+ NSString *text = payload[@"text"];
176
+ if (!clear && text.length == 0) { [task finish:@{@"ok": @YES, @"value": @{@"textLength": @0, @"cleared": payload[@"clearFirst"]}}]; return; }
177
+ if (clear) {
178
+ AABWDAClear(task, ^{
179
+ XCUIElement *current = AABWDAEditor(task, YES);
180
+ if (AABWDACheckTarget(task) || !current) { [task finish:AABWDAFailure(@"ios_wda_input_target_changed")]; return; }
181
+ id value = [current fb_standardSnapshot].value;
182
+ // XCTest represents an empty editor as nil or an empty string. WDA's
183
+ // displayed value may instead contain its placeholder; use the raw value.
184
+ if (value != nil && (![value isKindOfClass:NSString.class] || [value length] != 0)) {
185
+ [task finish:AABWDAFailure(@"ios_wda_clear_not_observed")]; return;
186
+ }
187
+ AABWDAInput(task, NO);
188
+ });
189
+ return;
190
+ }
191
+ XCSynthesizedEventRecord *event = [[XCSynthesizedEventRecord alloc] initWithName:@"Bridge input text"];
192
+ XCPointerEventPath *keys = [[XCPointerEventPath alloc] initForTextInput];
193
+ [keys typeText:text atOffset:0 typingSpeed:FBConfiguration.maxTypingFrequency shouldRedact:YES];
194
+ [event addPointerEventPath:keys];
195
+ AABWDAEvent(task, event, YES, ^{
196
+ if (AABWDACheckTarget(task) || !AABWDAEditor(task, YES)) { [task finish:AABWDAFailure(@"ios_wda_input_target_changed")]; return; }
197
+ [task finish:@{@"ok": @YES, @"value": @{@"textLength": @(text.length), @"cleared": payload[@"clearFirst"]}}];
198
+ });
199
+ }
200
+
201
+ static NSDictionary *AABWDAInterfaceOrientations(void)
202
+ {
203
+ return @{@"portrait": @(UIInterfaceOrientationPortrait), @"landscapeLeft": @(UIInterfaceOrientationLandscapeLeft),
204
+ @"landscapeRight": @(UIInterfaceOrientationLandscapeRight), @"portraitUpsideDown": @(UIInterfaceOrientationPortraitUpsideDown)};
205
+ }
206
+
207
+ static void AABWDASetOrientation(AABWDATask *task)
208
+ {
209
+ NSString *requested = task.body[@"payload"][@"orientation"];
210
+ UIInterfaceOrientation expected = [AABWDAInterfaceOrientations()[requested] integerValue];
211
+ // UIKit's landscape interface direction is opposite to device orientation.
212
+ UIDeviceOrientation deviceOrientation = expected == UIInterfaceOrientationLandscapeLeft ? UIDeviceOrientationLandscapeRight
213
+ : expected == UIInterfaceOrientationLandscapeRight ? UIDeviceOrientationLandscapeLeft : (UIDeviceOrientation)expected;
214
+ id<XCTestManager_ManagerInterface> daemon = XCTRunnerDaemonSession.sharedSession.daemonProxy;
215
+ NSString *error = AABWDACheckTarget(task);
216
+ if (!error && ![(id)daemon respondsToSelector:@selector(_XCT_updateDeviceOrientation:completion:)])
217
+ error = @"ios_wda_orientation_api_unavailable";
218
+ if (!error) error = [task permission];
219
+ if (error) { [task finish:AABWDAFailure(error)]; return; }
220
+ @try {
221
+ [daemon _XCT_updateDeviceOrientation:deviceOrientation completion:^(NSError *failure) {
222
+ dispatch_async(dispatch_get_main_queue(), ^{
223
+ if (failure) { [task finish:AABWDAFailure(@"ios_wda_orientation_event_failed")]; return; }
224
+ @try {
225
+ NSString *changed = AABWDACheckTarget(task);
226
+ if (changed) { [task finish:AABWDAFailure(changed)]; return; }
227
+ UIInterfaceOrientation actual = XCUIApplication.fb_activeApplication.interfaceOrientation;
228
+ id observed = [AABWDAInterfaceOrientations() allKeysForObject:@(actual)].firstObject ?: NSNull.null;
229
+ NSDictionary *value = @{@"requestedOrientation": requested, @"observedInterfaceOrientation": observed};
230
+ [task finish:actual == expected ? @{@"ok": @YES, @"value": value}
231
+ : @{@"ok": @NO, @"error": @"ios_wda_orientation_not_observed", @"value": value}];
232
+ } @catch (NSException *exception) {
233
+ [task finish:AABWDAFailure(@"ios_wda_orientation_observation_failed")];
234
+ }
235
+ });
236
+ }];
237
+ } @catch (NSException *exception) {
238
+ [task unresolved:@"ios_wda_orientation_submission_unknown"];
239
+ }
240
+ }
241
+
242
+ static void AABWDAStartTask(AABWDATask *task)
243
+ {
244
+ @try {
245
+ NSString *error = AABWDACheckTarget(task);
246
+ if (error) { [task finish:AABWDAFailure(error)]; return; }
247
+ NSString *operation = task.body[@"operation"];
248
+ NSDictionary *payload = task.body[@"payload"];
249
+ if ([operation isEqual:@"session-create"] || [operation isEqual:@"session-close"]) {
250
+ error = [task permission];
251
+ if (error) { [task finish:AABWDAFailure(error)]; return; }
252
+ [FBConfiguration setShouldTerminateApp:NO];
253
+ if ([operation isEqual:@"session-close"]) {
254
+ [FBSession.activeSession kill]; AABWDASessionTarget = nil;
255
+ [task finish:@{@"ok": @YES, @"value": NSNull.null}]; return;
256
+ }
257
+ XCUIApplication *app = XCUIApplication.fb_activeApplication;
258
+ if (![app.bundleID isEqual:task.target[@"bundleId"]] || app.processID != [task.target[@"processId"] intValue]) {
259
+ [task finish:AABWDAFailure(@"ios_wda_target_changed")]; return;
260
+ }
261
+ app.fb_shouldWaitForQuiescence = NO;
262
+ FBSession *session = [FBSession initWithApplication:app]; session.defaultActiveApplication = app.bundleID;
263
+ AABWDASessionTarget = @{@"sessionId": session.identifier, @"bundleId": app.bundleID, @"processId": @(app.processID)};
264
+ [task finish:@{@"ok": @YES, @"value": AABWDASessionTarget}]; return;
265
+ }
266
+ if ([operation isEqual:@"set-orientation"]) { AABWDASetOrientation(task); return; }
267
+ NSArray *actions;
268
+ if (payload[@"targetRef"] && !AABWDANativeElement(task, YES)) { [task finish:AABWDAFailure(@"ios_native_target_changed")]; return; }
269
+ if (AABWDAIsInput(task) || [operation isEqual:@"native-tap"]) {
270
+ if (AABWDAIsInput(task) && !AABWDAEditor(task, NO)) { [task finish:AABWDAFailure(@"ios_wda_input_target_changed")]; return; }
271
+ NSString *elementId = payload[@"targetRef"] ? payload[@"targetRef"][@"elementId"] : payload[@"elementId"];
272
+ actions = @[
273
+ @{@"type": @"pointerMove", @"duration": @0, @"x": @0, @"y": @0,
274
+ @"origin": @{@"element-6066-11e4-a52e-4f735466cecf": elementId}},
275
+ @{@"type": @"pointerDown", @"button": @0}, @{@"type": @"pointerUp", @"button": @0}
276
+ ];
277
+ } else if ([operation isEqual:@"tap"]) {
278
+ actions = @[@{@"type": @"pointerMove", @"duration": @0, @"x": payload[@"x"], @"y": payload[@"y"]},
279
+ @{@"type": @"pointerDown", @"button": @0}, @{@"type": @"pointerUp", @"button": @0}];
280
+ } else {
281
+ actions = @[
282
+ @{@"type": @"pointerMove", @"duration": @0, @"x": payload[@"startX"], @"y": payload[@"startY"]},
283
+ @{@"type": @"pointerDown", @"button": @0},
284
+ @{@"type": @"pointerMove", @"duration": payload[@"durationMs"], @"x": payload[@"endX"], @"y": payload[@"endY"]},
285
+ @{@"type": @"pointerUp", @"button": @0}
286
+ ];
287
+ }
288
+ NSError *preparation;
289
+ XCSynthesizedEventRecord *event = AABWDAPointer(actions, &preparation);
290
+ if (!event) { [task finish:AABWDAFailure(@"ios_wda_event_preparation_failed")]; return; }
291
+ AABWDAEvent(task, event, NO, ^{
292
+ if (AABWDAIsInput(task)) AABWDAInput(task, [payload[@"clearFirst"] boolValue]);
293
+ else [task finish:@{@"ok": @YES, @"value": NSNull.null}];
294
+ });
295
+ } @catch (NSException *exception) {
296
+ [task finish:AABWDAFailure(@"ios_wda_target_unavailable")];
297
+ }
298
+ }
299
+
300
+ static NSString *AABWDAValidateBody(NSDictionary *body, NSDictionary *headers)
301
+ {
302
+ if (![[NSSet setWithArray:body.allKeys] isEqual:[NSSet setWithArray:@[@"operation", @"target", @"payload", @"actionId", @"execution"]]])
303
+ return @"invalid_ios_wda_action";
304
+ NSString *operation = body[@"operation"];
305
+ NSDictionary *target = body[@"target"], *payload = body[@"payload"];
306
+ NSDictionary *fields = @{@"tap": @[@"x", @"y"], @"swipe": @[@"startX", @"startY", @"endX", @"endY", @"durationMs"],
307
+ @"input": @[@"elementId", @"text", @"clearFirst"], @"session-create": @[], @"session-close": @[],
308
+ @"native-tap": @[@"targetRef"], @"native-input": @[@"targetRef", @"text", @"clearFirst"],
309
+ @"set-orientation": @[@"orientation"]};
310
+ if (![operation isKindOfClass:NSString.class] || !fields[operation] || ![target isKindOfClass:NSDictionary.class]
311
+ || ![payload isKindOfClass:NSDictionary.class]) return @"invalid_ios_wda_action";
312
+ NSArray *targetFields = [operation isEqual:@"session-create"] ? @[@"bundleId", @"processId"] : @[@"bundleId", @"processId", @"sessionId"];
313
+ if (![[NSSet setWithArray:target.allKeys] isEqual:[NSSet setWithArray:targetFields]] || !AABWDAText(target[@"bundleId"])
314
+ || !AABWDAInteger(target[@"processId"], 1, INT32_MAX)
315
+ || (targetFields.count == 3 && !AABWDAText(target[@"sessionId"]))
316
+ || ![[NSSet setWithArray:payload.allKeys] isEqual:[NSSet setWithArray:fields[operation]]]) return @"invalid_ios_wda_action";
317
+ if ([operation hasPrefix:@"native-"]) {
318
+ NSDictionary *ref = payload[@"targetRef"];
319
+ if (![ref isKindOfClass:NSDictionary.class]
320
+ || ![[NSSet setWithArray:ref.allKeys] isEqual:[NSSet setWithArray:@[@"elementId", @"type", @"identifier", @"label"]]]
321
+ || !AABWDAText(ref[@"elementId"]) || !AABWDAText(ref[@"type"])) return @"invalid_ios_native_target";
322
+ for (NSString *name in @[@"identifier", @"label"]) {
323
+ if (ref[name] != NSNull.null && (![ref[name] isKindOfClass:NSString.class] || [ref[name] length] > 16384))
324
+ return @"invalid_ios_native_target";
325
+ }
326
+ }
327
+ if ([operation isEqual:@"set-orientation"]) {
328
+ if (![payload[@"orientation"] isKindOfClass:NSString.class] || !AABWDAInterfaceOrientations()[payload[@"orientation"]])
329
+ return @"invalid_ios_wda_orientation";
330
+ } else if ([operation isEqual:@"input"] || [operation isEqual:@"native-input"]) {
331
+ if (([operation isEqual:@"input"] && !AABWDAText(payload[@"elementId"])) || ![payload[@"text"] isKindOfClass:NSString.class]
332
+ || [payload[@"text"] lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > 64 * 1024
333
+ || ![payload[@"clearFirst"] isKindOfClass:NSNumber.class]
334
+ || CFGetTypeID((__bridge CFTypeRef)payload[@"clearFirst"]) != CFBooleanGetTypeID()) return @"invalid_ios_wda_input";
335
+ } else for (NSString *field in fields[operation]) {
336
+ if ([field isEqual:@"targetRef"]) continue;
337
+ id value = payload[field];
338
+ if ([field isEqual:@"durationMs"]) { if (!AABWDAInteger(value, 1, 120000)) return @"invalid_ios_wda_duration"; }
339
+ else if (![value isKindOfClass:NSNumber.class] || CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()
340
+ || !isfinite([value doubleValue]) || [value doubleValue] < 0 || [value doubleValue] > 100000) return @"invalid_ios_wda_coordinate";
341
+ }
342
+ return [AABWDABinding.shared targetErrorForHeaders:headers actualTarget:target sessionId:target[@"sessionId"]];
343
+ }
344
+
345
+ static void AABWDARegisterControl(RoutingHTTPServer *server)
346
+ {
347
+ for (NSString *route in @[@"/aab/status", @"/aab/execution/result", @"/aab/execution/cancel", @"/aab/action"]) {
348
+ NSString *method = [route isEqual:@"/aab/action"] || [route hasSuffix:@"/cancel"] ? @"POST" : @"GET";
349
+ [server handleMethod:method withPath:route block:^(RouteRequest *request, RouteResponse *response) {
350
+ NSString *bindingError = [AABWDABinding.shared runtimeErrorForHeaders:request.headers];
351
+ if (bindingError) { [AABWDAReject(bindingError) dispatchWithResponse:response]; return; }
352
+ if ([route isEqual:@"/aab/status"]) {
353
+ NSMutableDictionary *status = [[AABWDARuntime() status] mutableCopy];
354
+ status[@"nativeTargetSchema"] = @"aab.ios-native-target/v1";
355
+ status[@"orientationSchema"] = @"aab.ios-orientation/v1";
356
+ AABWDAWire(response, status); return;
357
+ }
358
+ NSDictionary *body;
359
+ if ([method isEqual:@"POST"]) {
360
+ body = request.body.length <= 256 * 1024 ? [NSJSONSerialization JSONObjectWithData:request.body options:0 error:NULL] : nil;
361
+ } else {
362
+ NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
363
+ for (NSURLQueryItem *item in [NSURLComponents componentsWithURL:request.url resolvingAgainstBaseURL:NO].queryItems) {
364
+ if (parameters[item.name] || !item.value) { AABWDAWire(response, AABWDAFailure(@"invalid_ios_wda_query")); return; }
365
+ parameters[item.name] = item.value;
366
+ }
367
+ body = parameters;
368
+ }
369
+ if (![body isKindOfClass:NSDictionary.class]) { AABWDAWire(response, AABWDAFailure(@"invalid_ios_wda_json")); return; }
370
+ if ([route isEqual:@"/aab/action"]) {
371
+ NSString *error = AABWDAValidateBody(body, request.headers);
372
+ if (error) { AABWDAWire(response, AABWDAFailure(error)); return; }
373
+ NSMutableDictionary *target = [body[@"target"] mutableCopy];
374
+ target[@"runnerBundleId"] = AABWDABinding.shared.identity[@"bundleId"]; target[@"operation"] = body[@"operation"];
375
+ dispatch_semaphore_t finished = dispatch_semaphore_create(0);
376
+ __block NSDictionary *result;
377
+ [AABWDARuntime() submit:body target:target start:^(AABWDATask *task) { AABWDAStartTask(task); }
378
+ reply:^(NSDictionary *value) { result = value; dispatch_semaphore_signal(finished); }];
379
+ dispatch_semaphore_wait(finished, DISPATCH_TIME_FOREVER);
380
+ AABWDAWire(response, result); return;
381
+ }
382
+ NSSet *allowed = [NSSet setWithArray:[method isEqual:@"GET"] ? @[@"actionId", @"runtimeEpoch", @"cursor"] : @[@"actionId", @"runtimeEpoch"]];
383
+ if (![[NSSet setWithArray:body.allKeys] isSubsetOfSet:allowed] || !AABWDAText(body[@"actionId"]) || !AABWDAText(body[@"runtimeEpoch"])
384
+ || (body[@"cursor"] && (![body[@"cursor"] isKindOfClass:NSString.class] || [body[@"cursor"] length] > 2048))) {
385
+ AABWDAWire(response, AABWDAFailure(@"invalid_ios_wda_query")); return;
386
+ }
387
+ AABWDAWire(response, [method isEqual:@"GET"]
388
+ ? [AABWDARuntime() result:body[@"actionId"] epoch:body[@"runtimeEpoch"] cursor:body[@"cursor"]]
389
+ : [AABWDARuntime() cancel:body[@"actionId"] epoch:body[@"runtimeEpoch"]]);
390
+ }];
391
+ }
392
+ }
@@ -0,0 +1,10 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+ @interface AABWDAReceiptStore : NSObject
5
+ @property(nonatomic, readonly) BOOL ready;
6
+ - (instancetype)initWithDirectory:(NSURL *)directory runnerBundleId:(NSString *)bundleId;
7
+ - (nullable NSDictionary *)commit:(NSDictionary *)result;
8
+ - (NSDictionary *)lookupAction:(NSString *)actionId epoch:(NSString *)epoch cursor:(nullable NSString *)cursor;
9
+ @end
10
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,116 @@
1
+ #import "AABWDAReceiptStore.h"
2
+ #import <CommonCrypto/CommonDigest.h>
3
+ #include "sfs.h"
4
+
5
+ static NSString *AABWDADigest(NSData *data)
6
+ {
7
+ unsigned char digest[CC_SHA256_DIGEST_LENGTH];
8
+ CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
9
+ NSMutableString *value = [NSMutableString string];
10
+ for (NSUInteger i = 0; i < sizeof(digest); i++) [value appendFormat:@"%02x", digest[i]];
11
+ return value;
12
+ }
13
+
14
+ static BOOL AABWDAInteger(id value, uint64_t minimum, uint64_t maximum)
15
+ {
16
+ return [value isKindOfClass:NSNumber.class] && CFGetTypeID((__bridge CFTypeRef)value) != CFBooleanGetTypeID()
17
+ && [value doubleValue] >= minimum && [value doubleValue] <= maximum
18
+ && [value doubleValue] == (double)[value unsignedLongLongValue];
19
+ }
20
+
21
+ static NSDictionary *AABWDAStoreFailure(NSString *code)
22
+ {
23
+ return @{@"ok": @NO, @"error": code, @"settled": @NO};
24
+ }
25
+
26
+ @implementation AABWDAReceiptStore {
27
+ sfs_store_t *_store;
28
+ NSString *_bundleId;
29
+ }
30
+ - (instancetype)initWithDirectory:(NSURL *)directory runnerBundleId:(NSString *)bundleId
31
+ {
32
+ self = [super init];
33
+ if (self) {
34
+ _bundleId = bundleId.copy;
35
+ if (![NSFileManager.defaultManager createDirectoryAtURL:directory withIntermediateDirectories:YES attributes:nil error:NULL]) return self;
36
+ sfs_open_options_t options = {0};
37
+ options.struct_size = sizeof(options); options.flags = SFS_OPEN_CREATE;
38
+ options.directory = directory.fileSystemRepresentation; options.segment_size = 256 * 1024;
39
+ options.partition_quotas[5] = 8 * 1024 * 1024;
40
+ sfs_open(&options, &_store, NULL);
41
+ }
42
+ return self;
43
+ }
44
+ - (void)dealloc { if (_store) sfs_close(_store, NULL); }
45
+ - (BOOL)ready { return _store != NULL; }
46
+
47
+ - (NSDictionary *)commit:(NSDictionary *)result
48
+ {
49
+ @synchronized (self) {
50
+ if (!_store) return nil;
51
+ NSDictionary *record = @{@"schemaVersion": @"aab.wda-completion/v1", @"runnerBundleId": _bundleId, @"executionResult": result};
52
+ if (![NSJSONSerialization isValidJSONObject:record]) return nil;
53
+ NSData *data = [NSJSONSerialization dataWithJSONObject:record options:NSJSONWritingSortedKeys error:NULL];
54
+ if (!data || data.length > 128 * 1024) return nil;
55
+ sfs_record_info_t info = {0}; info.struct_size = sizeof(info);
56
+ if (sfs_append(_store, 5, data.bytes, (uint32_t)data.length, SFS_DURABILITY_SYNC, &info, NULL) != SFS_OK || info.sequence == 0) return nil;
57
+ return @{@"committed": @YES, @"sequence": @(info.sequence), @"sha256": AABWDADigest(data)};
58
+ }
59
+ }
60
+
61
+ - (NSDictionary *)lookupAction:(NSString *)actionId epoch:(NSString *)epoch cursor:(NSString *)encoded
62
+ {
63
+ @synchronized (self) {
64
+ sfs_status_t status = {0}; status.struct_size = sizeof(status);
65
+ if (!_store || sfs_flush(_store, NULL) != SFS_OK || sfs_status(_store, &status, NULL) != SFS_OK)
66
+ return AABWDAStoreFailure(@"ios_wda_completion_store_unavailable");
67
+ NSString *scope = AABWDADigest([NSJSONSerialization dataWithJSONObject:@[_bundleId, actionId, epoch] options:0 error:NULL]);
68
+ uint64_t through = status.next_sequence - 1;
69
+ sfs_cursor_t cursor = SFS_CURSOR_PARTITION(5);
70
+ if (encoded) {
71
+ NSData *bytes = encoded.length <= 2048 ? [[NSData alloc] initWithBase64EncodedString:encoded options:0] : nil;
72
+ NSDictionary *value = bytes ? [NSJSONSerialization JSONObjectWithData:bytes options:0 error:NULL] : nil;
73
+ if (![value isKindOfClass:NSDictionary.class] || ![value[@"scope"] isEqual:scope]
74
+ || ![value[@"version"] isEqual:@1] || !AABWDAInteger(value[@"after"], 1, through)
75
+ || !AABWDAInteger(value[@"through"], 1, through) || !AABWDAInteger(value[@"segment"], 1, UINT32_MAX)
76
+ || !AABWDAInteger(value[@"offset"], 64, 256 * 1024)
77
+ || [value[@"after"] unsignedLongLongValue] >= [value[@"through"] unsignedLongLongValue])
78
+ return AABWDAStoreFailure(@"invalid_ios_wda_completion_cursor");
79
+ cursor.after_sequence = [value[@"after"] unsignedLongLongValue];
80
+ cursor.segment_id = [value[@"segment"] unsignedLongLongValue];
81
+ cursor.offset = [value[@"offset"] unsignedLongLongValue];
82
+ through = [value[@"through"] unsignedLongLongValue];
83
+ }
84
+ uint64_t before = cursor.after_sequence;
85
+ NSMutableData *buffer = [NSMutableData dataWithLength:128 * 1024];
86
+ NSUInteger readBytes = 0;
87
+ BOOL ended = before == through;
88
+ for (NSUInteger count = 0; !ended && count < 64 && readBytes < 2 * 1024 * 1024; count++) {
89
+ sfs_record_info_t info = {0}; info.struct_size = sizeof(info);
90
+ sfs_result_t read = sfs_scan(_store, &cursor, buffer.mutableBytes, (uint32_t)buffer.length, &info, NULL);
91
+ if (read == SFS_END) { ended = YES; break; }
92
+ if (read != SFS_OK) return AABWDAStoreFailure(@"ios_wda_completion_read_failed");
93
+ if (info.sequence > through) { ended = YES; break; }
94
+ readBytes += info.payload_length;
95
+ NSData *data = [buffer subdataWithRange:NSMakeRange(0, info.payload_length)];
96
+ NSDictionary *record = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
97
+ if (![record isKindOfClass:NSDictionary.class] || ![record[@"schemaVersion"] isEqual:@"aab.wda-completion/v1"]
98
+ || ![record[@"runnerBundleId"] isEqual:_bundleId]) return AABWDAStoreFailure(@"invalid_ios_wda_completion_record");
99
+ NSDictionary *result = record[@"executionResult"];
100
+ if (![result isKindOfClass:NSDictionary.class]) return AABWDAStoreFailure(@"invalid_ios_wda_completion_record");
101
+ if ([result[@"actionId"] isEqual:actionId] && [result[@"runtimeEpoch"] isEqual:epoch])
102
+ return @{@"ok": @YES, @"found": @YES, @"actionId": actionId, @"runtimeEpoch": epoch, @"executionResult": result,
103
+ @"receipt": @{@"committed": @YES, @"sequence": @(info.sequence), @"sha256": AABWDADigest(data)}};
104
+ ended = cursor.after_sequence >= through;
105
+ }
106
+ uint64_t after = MIN(cursor.after_sequence, through);
107
+ BOOL more = !ended && after < through;
108
+ if (more && after <= before) return AABWDAStoreFailure(@"ios_wda_completion_cursor_stalled");
109
+ NSDictionary *next = @{@"version": @1, @"scope": scope, @"after": @(after), @"through": @(through),
110
+ @"segment": @(cursor.segment_id), @"offset": @(cursor.offset)};
111
+ id nextCursor = more ? [[NSJSONSerialization dataWithJSONObject:next options:0 error:NULL] base64EncodedStringWithOptions:0] : NSNull.null;
112
+ return @{@"ok": @YES, @"found": @NO, @"settled": @NO, @"hasMore": @(more),
113
+ @"nextSequence": @(after), @"throughSequence": @(through), @"nextCursor": nextCursor};
114
+ }
115
+ }
116
+ @end
@@ -0,0 +1,22 @@
1
+ {
2
+ "schemaVersion": "aab.uia.bundle.v1",
3
+ "mainClass": "io.github.mobileaidev.aiappbridge.uia.UiaRuntime",
4
+ "artifact": "ai-app-bridge-uia.jar",
5
+ "sha256": "50287a3e533fa54f995a2e14dcf886d8746931232416bba8c9a10e9e16de0d32",
6
+ "minApi": 33,
7
+ "compileApi": 35,
8
+ "buildTools": "36.0.0",
9
+ "androidJarSha256": "4566663c3876e022b4fa4ced8c8697c4ab1688267f090114fd92d027b32e619b",
10
+ "d8JarSha256": "4097ff9c46c185c6e7214da7fe9b1befb5adeea5cc9ca349270e0249904f9240",
11
+ "sources": {
12
+ "android/ai-app-bridge-uia/build.gradle.kts": "a16e01b920da2e55823aedbfdf11cec449f5b1e31eb9335182554887cef06e56",
13
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/DurableFiles.java": "533130ad6d496005af84b99bc175e4ce237ad7c13a1b354f98c2a0e102f5baca",
14
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/UiaActionEngine.java": "e7cb3ba5b58d87a1024d6c0dd9eff24f07e74ca90fb716f898441255977c3e6f",
15
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/UiaConnection.java": "58237fba2d8f078c09e4019eda95c264ca985c320fa44270786adcf94dc659df",
16
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/UiaHttp.java": "43447130a1829354e50c4df4dac2a66afea3ef7d9f8fa5732d773f526fc6e72a",
17
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/UiaJournal.java": "3cbbfa6070e9c7ee51c45bb4fa7e5330270cfb9b8c93809176834cadf36936ea",
18
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/UiaNodes.java": "4d31c535084283c1091c24dcf0ea007f287c1ace78055928920d0b24ac52ac49",
19
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/UiaRuntime.java": "b3382efdbddc1cf8e94953732846e35d10098aeb64fc6fcfba0d6bbd2f7f5a76",
20
+ "android/ai-app-bridge-uia/src/main/java/io/github/mobileaidev/aiappbridge/uia/Wire.java": "d54e79c5e064abf5c7b36d2ab43205b7d47cca001ec37bf9b82ab01e33cf816c"
21
+ }
22
+ }