@leejungkiin/awkit 1.7.0 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/bin/awk.js +576 -84
  2. package/core/CLAUDE.md +1 -1
  3. package/core/GEMINI.md +148 -167
  4. package/core/GEMINI.md.bak +149 -116
  5. package/core/skill-runtime-manifest.json +3 -0
  6. package/docs/Claude Fable 5.md +3826 -0
  7. package/docs/android_kotlin_system_instruction.md +210 -0
  8. package/docs/brainstorm_ponytail_integration.md +146 -0
  9. package/docs/brainstorm_smart_setup.md +113 -0
  10. package/docs/deep-research-report (1).md +293 -0
  11. package/docs/history/GEMINI.v1.md +135 -0
  12. package/docs/history/brainstorm_antigravity_unified_architecture.v1.md +105 -0
  13. package/docs/history/implementation_plan.v1.md +58 -0
  14. package/package.json +4 -1
  15. package/scripts/artifact-storage.js +130 -0
  16. package/scripts/automation-gate.js +40 -7
  17. package/scripts/claude-plan.js +76 -0
  18. package/scripts/dependency-manager.js +210 -0
  19. package/scripts/exec-rtk.js +11 -5
  20. package/scripts/i18n-helper.js +381 -0
  21. package/scripts/multi-model-pipeline.js +144 -0
  22. package/skill-packs/mobile-ios/pack.json +4 -2
  23. package/skill-packs/reverse-engineering/pack.json +1 -0
  24. package/skills/CATALOG.md +20 -0
  25. package/skills/GEMINI.md +9 -1
  26. package/skills/TRIGGER_INDEX.md +10 -0
  27. package/skills/ai-music/SKILL.md +275 -0
  28. package/skills/android-re-analyzer/SKILL.md +238 -0
  29. package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
  30. package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
  31. package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
  32. package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
  33. package/skills/android-re-analyzer/references/setup-guide.md +221 -0
  34. package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
  35. package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
  36. package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
  37. package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
  38. package/skills/animal-island-ui-style/SKILL.md +1450 -0
  39. package/skills/app-store-review-agent/SKILL.md +164 -0
  40. package/skills/app-store-review-agent/references/guidelines/README.md +154 -0
  41. package/skills/app-store-review-agent/references/guidelines/by-app-type/ai_apps.md +37 -0
  42. package/skills/app-store-review-agent/references/guidelines/by-app-type/all_apps.md +50 -0
  43. package/skills/app-store-review-agent/references/guidelines/by-app-type/crypto_finance.md +31 -0
  44. package/skills/app-store-review-agent/references/guidelines/by-app-type/games.md +31 -0
  45. package/skills/app-store-review-agent/references/guidelines/by-app-type/health_fitness.md +31 -0
  46. package/skills/app-store-review-agent/references/guidelines/by-app-type/kids.md +27 -0
  47. package/skills/app-store-review-agent/references/guidelines/by-app-type/macos.md +38 -0
  48. package/skills/app-store-review-agent/references/guidelines/by-app-type/social_ugc.md +32 -0
  49. package/skills/app-store-review-agent/references/guidelines/by-app-type/subscription_iap.md +34 -0
  50. package/skills/app-store-review-agent/references/guidelines/by-app-type/vpn.md +18 -0
  51. package/skills/app-store-review-agent/references/rules/design/minimum_functionality.md +96 -0
  52. package/skills/app-store-review-agent/references/rules/design/sign_in_with_apple.md +54 -0
  53. package/skills/app-store-review-agent/references/rules/entitlements/unused_entitlements.md +83 -0
  54. package/skills/app-store-review-agent/references/rules/metadata/accurate_metadata.md +54 -0
  55. package/skills/app-store-review-agent/references/rules/metadata/apple_trademark.md +99 -0
  56. package/skills/app-store-review-agent/references/rules/metadata/china_storefront.md +72 -0
  57. package/skills/app-store-review-agent/references/rules/metadata/competitor_terms.md +56 -0
  58. package/skills/app-store-review-agent/references/rules/metadata/subscription_metadata.md +81 -0
  59. package/skills/app-store-review-agent/references/rules/privacy/privacy_manifest.md +84 -0
  60. package/skills/app-store-review-agent/references/rules/privacy/unnecessary_data.md +60 -0
  61. package/skills/app-store-review-agent/references/rules/subscription/misleading_pricing.md +63 -0
  62. package/skills/app-store-review-agent/references/rules/subscription/missing_tos_pp.md +54 -0
  63. package/skills/awf-ponytail/SKILL.md +91 -0
  64. package/skills/awf-ponytail-review/SKILL.md +67 -0
  65. package/skills/awf-session-restore/SKILL.md +3 -3
  66. package/skills/brainstorm-agent/SKILL.md +11 -2
  67. package/skills/brainstorm-agent/templates/brief-template.md +8 -0
  68. package/skills/claude-planner/SKILL.md +47 -0
  69. package/skills/code-review/SKILL.md +87 -0
  70. package/skills/expo-game-development/SKILL.md +163 -0
  71. package/skills/flutter/LICENSE.txt +202 -0
  72. package/skills/flutter/SKILL.md +127 -0
  73. package/skills/flutter-project-creater/LICENSE.txt +202 -0
  74. package/skills/flutter-project-creater/SKILL.md +106 -0
  75. package/skills/game-developer/SKILL.md +163 -0
  76. package/skills/game-developer/references/ecs-patterns.md +501 -0
  77. package/skills/game-developer/references/multiplayer-networking.md +475 -0
  78. package/skills/game-developer/references/performance-optimization.md +422 -0
  79. package/skills/game-developer/references/unity-patterns.md +271 -0
  80. package/skills/game-developer/references/unreal-cpp.md +352 -0
  81. package/skills/generate-gui-assets/SKILL.md +305 -0
  82. package/skills/generate-gui-assets/agents/openai.yaml +4 -0
  83. package/skills/generate-gui-assets/references/catalog-schema.md +58 -0
  84. package/skills/generate-gui-assets/references/extraction-techniques.md +21 -0
  85. package/skills/generate-gui-assets/references/prompt-patterns.md +58 -0
  86. package/skills/generate-gui-assets/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
  87. package/skills/generate-gui-assets/scripts/build_gui_contact_sheet.py +51 -0
  88. package/skills/generate-gui-assets/scripts/clean_chroma_edges.py +262 -0
  89. package/skills/generate-gui-assets/scripts/copy_approved_icons.py +64 -0
  90. package/skills/generate-gui-assets/scripts/prepare_gui_asset_run.py +91 -0
  91. package/skills/generate-gui-assets/scripts/suggest_grid_options.py +63 -0
  92. package/skills/generate-gui-assets/scripts/validate_gui_catalog.py +50 -0
  93. package/skills/godot-game-development/SKILL.md +142 -0
  94. package/skills/hatch-pet/LICENSE.txt +201 -0
  95. package/skills/hatch-pet/SKILL.md +420 -0
  96. package/skills/hatch-pet/agents/openai.yaml +4 -0
  97. package/skills/hatch-pet/references/animation-rows.md +29 -0
  98. package/skills/hatch-pet/references/codex-pet-contract.md +35 -0
  99. package/skills/hatch-pet/references/qa-rubric.md +60 -0
  100. package/skills/hatch-pet/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
  101. package/skills/hatch-pet/scripts/clean_chroma_edges.py +262 -0
  102. package/skills/hatch-pet/scripts/compose_atlas.py +150 -0
  103. package/skills/hatch-pet/scripts/derive_running_left_from_running_right.py +143 -0
  104. package/skills/hatch-pet/scripts/extract_strip_frames.py +323 -0
  105. package/skills/hatch-pet/scripts/finalize_pet_run.py +382 -0
  106. package/skills/hatch-pet/scripts/generate_pet_images.py +287 -0
  107. package/skills/hatch-pet/scripts/inspect_frames.py +246 -0
  108. package/skills/hatch-pet/scripts/make_contact_sheet.py +96 -0
  109. package/skills/hatch-pet/scripts/package_custom_pet.py +108 -0
  110. package/skills/hatch-pet/scripts/pet_job_status.py +117 -0
  111. package/skills/hatch-pet/scripts/prepare_pet_run.py +673 -0
  112. package/skills/hatch-pet/scripts/queue_pet_repairs.py +172 -0
  113. package/skills/hatch-pet/scripts/record_imagegen_result.py +250 -0
  114. package/skills/hatch-pet/scripts/render_animation_videos.py +134 -0
  115. package/skills/hatch-pet/scripts/render_animation_videos.sh +5 -0
  116. package/skills/hatch-pet/scripts/validate_atlas.py +139 -0
  117. package/skills/i18n-orchestrator/SKILL.md +37 -0
  118. package/skills/ios-simulator-skill/SKILL.md +390 -0
  119. package/skills/ios-simulator-skill/scripts/accessibility_audit.py +300 -0
  120. package/skills/ios-simulator-skill/scripts/app_launcher.py +326 -0
  121. package/skills/ios-simulator-skill/scripts/app_state_capture.py +400 -0
  122. package/skills/ios-simulator-skill/scripts/appearance.py +385 -0
  123. package/skills/ios-simulator-skill/scripts/build_and_test.py +348 -0
  124. package/skills/ios-simulator-skill/scripts/clipboard.py +103 -0
  125. package/skills/ios-simulator-skill/scripts/common/__init__.py +61 -0
  126. package/skills/ios-simulator-skill/scripts/common/cache_utils.py +289 -0
  127. package/skills/ios-simulator-skill/scripts/common/device_utils.py +462 -0
  128. package/skills/ios-simulator-skill/scripts/common/env_config.py +35 -0
  129. package/skills/ios-simulator-skill/scripts/common/hang_pipeline.py +862 -0
  130. package/skills/ios-simulator-skill/scripts/common/hang_sessions.py +490 -0
  131. package/skills/ios-simulator-skill/scripts/common/idb_utils.py +180 -0
  132. package/skills/ios-simulator-skill/scripts/common/screenshot_utils.py +338 -0
  133. package/skills/ios-simulator-skill/scripts/container.py +668 -0
  134. package/skills/ios-simulator-skill/scripts/gesture.py +394 -0
  135. package/skills/ios-simulator-skill/scripts/hang_watcher.py +1533 -0
  136. package/skills/ios-simulator-skill/scripts/keyboard.py +391 -0
  137. package/skills/ios-simulator-skill/scripts/localization_audit.py +483 -0
  138. package/skills/ios-simulator-skill/scripts/location.py +467 -0
  139. package/skills/ios-simulator-skill/scripts/log_monitor.py +493 -0
  140. package/skills/ios-simulator-skill/scripts/model_inspector.py +645 -0
  141. package/skills/ios-simulator-skill/scripts/navigator.py +461 -0
  142. package/skills/ios-simulator-skill/scripts/privacy_manager.py +310 -0
  143. package/skills/ios-simulator-skill/scripts/push_notification.py +240 -0
  144. package/skills/ios-simulator-skill/scripts/screen_mapper.py +296 -0
  145. package/skills/ios-simulator-skill/scripts/sim_health_check.sh +245 -0
  146. package/skills/ios-simulator-skill/scripts/sim_list.py +299 -0
  147. package/skills/ios-simulator-skill/scripts/simctl_boot.py +312 -0
  148. package/skills/ios-simulator-skill/scripts/simctl_create.py +316 -0
  149. package/skills/ios-simulator-skill/scripts/simctl_delete.py +357 -0
  150. package/skills/ios-simulator-skill/scripts/simctl_erase.py +351 -0
  151. package/skills/ios-simulator-skill/scripts/simctl_shutdown.py +290 -0
  152. package/skills/ios-simulator-skill/scripts/simulator_selector.py +375 -0
  153. package/skills/ios-simulator-skill/scripts/status_bar.py +250 -0
  154. package/skills/ios-simulator-skill/scripts/test_recorder.py +323 -0
  155. package/skills/ios-simulator-skill/scripts/visual_diff.py +235 -0
  156. package/skills/ios-simulator-skill/scripts/xcode/__init__.py +13 -0
  157. package/skills/ios-simulator-skill/scripts/xcode/builder.py +397 -0
  158. package/skills/ios-simulator-skill/scripts/xcode/cache.py +204 -0
  159. package/skills/ios-simulator-skill/scripts/xcode/config.py +178 -0
  160. package/skills/ios-simulator-skill/scripts/xcode/reporter.py +343 -0
  161. package/skills/ios-simulator-skill/scripts/xcode/xcresult.py +451 -0
  162. package/skills/ios-visual-qa-strategist/SKILL.md +111 -0
  163. package/skills/ios-visual-qa-strategist/agents/openai.yaml +4 -0
  164. package/skills/ios-visual-qa-strategist/references/ios-tool-selection.md +61 -0
  165. package/skills/ios-visual-qa-strategist/references/minimal-capture-policy.md +56 -0
  166. package/skills/ios-visual-qa-strategist/references/visual-reasoning-heuristics.md +53 -0
  167. package/skills/orchestrator/SKILL.md +0 -20
  168. package/skills/persistent-storage/SKILL.md +55 -0
  169. package/skills/short-maker/SKILL.md +23 -0
  170. package/skills/short-maker/scripts/effects.js +56 -0
  171. package/skills/short-maker/scripts/shortmaker-bridge.js +332 -0
  172. package/skills/short-maker/scripts/videomix.js +601 -0
  173. package/skills/short-maker/templates/hyperframes/cinematic-character.template.html +172 -0
  174. package/skills/short-maker/templates/hyperframes/index.template.html +194 -0
  175. package/skills/smali-to-kotlin/SKILL.md +128 -0
  176. package/skills/smali-to-kotlin/examples/getting-started/tech-stack.md +58 -0
  177. package/skills/smali-to-kotlin/examples/pipeline/data-ui-parity.md +118 -0
  178. package/skills/smali-to-kotlin/examples/pipeline/scanner-and-bootstrap.md +106 -0
  179. package/skills/smali-to-kotlin/library-patterns.md +189 -0
  180. package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
  181. package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
  182. package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
  183. package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
  184. package/skills/smali-to-kotlin/phase-3-build.md +248 -0
  185. package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
  186. package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
  187. package/skills/smali-to-kotlin/templates/app-map.md +101 -0
  188. package/skills/smali-to-kotlin/templates/architecture.md +142 -0
  189. package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
  190. package/skills/spec-gate/SKILL.md +6 -2
  191. package/skills/symphony-enforcer/SKILL.md +8 -0
  192. package/skills/symphony-enforcer/examples/mindful-stop.md +2 -0
  193. package/skills/symphony-enforcer/examples/three-phase.md +16 -0
  194. package/skills/symphony-enforcer/examples/trigger-points.md +7 -1
  195. package/skills/unity-game-development/SKILL.md +231 -0
  196. package/skills/verification-gate/SKILL.md +4 -2
  197. package/skills/video-edit/SKILL.md +36 -0
  198. package/skills/video-edit/scripts/video_edit.py +324 -0
  199. package/templates/setup-mapping.json +48 -0
  200. package/templates/specs/design-template.md +161 -71
  201. package/templates/specs/requirements-template.md +65 -133
  202. package/templates/specs/task-spec-template.xml +3 -0
  203. package/workflows/_uncategorized/critic.md +40 -0
  204. package/workflows/_uncategorized/git-rebase-flow.md +81 -0
  205. package/workflows/_uncategorized/image-gen.md +118 -0
  206. package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
  207. package/workflows/_uncategorized/pixel-gen.md +86 -0
  208. package/workflows/_uncategorized/pixel-setup.md +90 -0
  209. package/workflows/_uncategorized/ponytail-review.md +59 -0
  210. package/workflows/_uncategorized/reverse-android-build.md +222 -0
  211. package/workflows/_uncategorized/reverse-android-design.md +139 -0
  212. package/workflows/_uncategorized/reverse-android-discover.md +150 -0
  213. package/workflows/_uncategorized/reverse-android-scan.md +158 -0
  214. package/workflows/_uncategorized/reverse-android.md +143 -0
  215. package/workflows/_uncategorized/reverse-ios-build.md +240 -0
  216. package/workflows/_uncategorized/reverse-ios-design.md +112 -0
  217. package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
  218. package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
  219. package/workflows/_uncategorized/reverse-ios.md +152 -0
  220. package/workflows/_uncategorized/safety-router.md +34 -0
  221. package/workflows/_uncategorized/teach.md +89 -0
  222. package/workflows/_uncategorized/verify-ui.md +53 -0
  223. package/workflows/_uncategorized/visualize-screenshots.md +34 -0
  224. package/workflows/ads/ads-analyst.md +201 -0
  225. package/workflows/ads/ads-audit.md +106 -0
  226. package/workflows/ads/ads-optimize.md +97 -0
  227. package/workflows/ads/ads-targeting.md +241 -0
  228. package/workflows/ads/adsExpert.md +160 -0
  229. package/workflows/ads/smali-ads-config.md +400 -0
  230. package/workflows/ads/smali-ads-flow.md +331 -0
  231. package/workflows/ads/smali-ads-interstitial.md +377 -0
  232. package/workflows/ads/smali-ads-native.md +382 -0
  233. package/workflows/context/teach.md +89 -0
  234. package/workflows/gitnexus.md +8 -8
  235. package/workflows/lifecycle/brainstorm.md +43 -0
  236. package/workflows/lifecycle/code.md +5 -0
  237. package/workflows/lifecycle/init.md +23 -5
  238. package/workflows/lifecycle/multi-model-pipeline.md +60 -0
  239. package/workflows/quality/ponytail-review.md +59 -0
  240. package/workflows/roles/critic.md +40 -0
  241. package/workflows/roles/safety-router.md +34 -0
@@ -0,0 +1,310 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ iOS Privacy & Permissions Manager
4
+
5
+ Grant/revoke app permissions for testing permission flows.
6
+ Supports 13+ services with audit trail tracking.
7
+
8
+ Usage: python scripts/privacy_manager.py --grant camera --bundle-id com.app
9
+ """
10
+
11
+ import argparse
12
+ import subprocess
13
+ import sys
14
+ from datetime import datetime
15
+
16
+ from common import resolve_udid
17
+
18
+
19
+ class PrivacyManager:
20
+ """Manages iOS app privacy and permissions."""
21
+
22
+ # Supported services
23
+ SUPPORTED_SERVICES = {
24
+ "camera": "Camera access",
25
+ "microphone": "Microphone access",
26
+ "location": "Location services",
27
+ "contacts": "Contacts access",
28
+ "photos": "Photos library access",
29
+ "calendar": "Calendar access",
30
+ "health": "Health data access",
31
+ "reminders": "Reminders access",
32
+ "motion": "Motion & fitness",
33
+ "keyboard": "Keyboard access",
34
+ "mediaLibrary": "Media library",
35
+ "calls": "Call history",
36
+ "siri": "Siri access",
37
+ }
38
+
39
+ def __init__(self, udid: str | None = None):
40
+ """Initialize privacy manager.
41
+
42
+ Args:
43
+ udid: Optional device UDID (auto-detects booted simulator if None)
44
+ """
45
+ self.udid = udid
46
+
47
+ def grant_permission(
48
+ self,
49
+ bundle_id: str,
50
+ service: str,
51
+ scenario: str | None = None,
52
+ step: int | None = None,
53
+ ) -> bool:
54
+ """
55
+ Grant permission for app.
56
+
57
+ Args:
58
+ bundle_id: App bundle ID
59
+ service: Service name (camera, microphone, location, etc.)
60
+ scenario: Test scenario name for audit trail
61
+ step: Step number in test scenario
62
+
63
+ Returns:
64
+ Success status
65
+ """
66
+ if service not in self.SUPPORTED_SERVICES:
67
+ print(f"Error: Unknown service '{service}'")
68
+ print(f"Supported: {', '.join(self.SUPPORTED_SERVICES.keys())}")
69
+ return False
70
+
71
+ cmd = ["xcrun", "simctl", "privacy"]
72
+
73
+ if self.udid:
74
+ cmd.append(self.udid)
75
+ else:
76
+ cmd.append("booted")
77
+
78
+ cmd.extend(["grant", service, bundle_id])
79
+
80
+ try:
81
+ subprocess.run(cmd, capture_output=True, check=True)
82
+
83
+ # Log audit entry
84
+ self._log_audit("grant", bundle_id, service, scenario, step)
85
+
86
+ return True
87
+ except subprocess.CalledProcessError:
88
+ return False
89
+
90
+ def revoke_permission(
91
+ self,
92
+ bundle_id: str,
93
+ service: str,
94
+ scenario: str | None = None,
95
+ step: int | None = None,
96
+ ) -> bool:
97
+ """
98
+ Revoke permission for app.
99
+
100
+ Args:
101
+ bundle_id: App bundle ID
102
+ service: Service name
103
+ scenario: Test scenario name for audit trail
104
+ step: Step number in test scenario
105
+
106
+ Returns:
107
+ Success status
108
+ """
109
+ if service not in self.SUPPORTED_SERVICES:
110
+ print(f"Error: Unknown service '{service}'")
111
+ return False
112
+
113
+ cmd = ["xcrun", "simctl", "privacy"]
114
+
115
+ if self.udid:
116
+ cmd.append(self.udid)
117
+ else:
118
+ cmd.append("booted")
119
+
120
+ cmd.extend(["revoke", service, bundle_id])
121
+
122
+ try:
123
+ subprocess.run(cmd, capture_output=True, check=True)
124
+
125
+ # Log audit entry
126
+ self._log_audit("revoke", bundle_id, service, scenario, step)
127
+
128
+ return True
129
+ except subprocess.CalledProcessError:
130
+ return False
131
+
132
+ def reset_permission(
133
+ self,
134
+ bundle_id: str,
135
+ service: str,
136
+ scenario: str | None = None,
137
+ step: int | None = None,
138
+ ) -> bool:
139
+ """
140
+ Reset permission to default.
141
+
142
+ Args:
143
+ bundle_id: App bundle ID
144
+ service: Service name
145
+ scenario: Test scenario name for audit trail
146
+ step: Step number in test scenario
147
+
148
+ Returns:
149
+ Success status
150
+ """
151
+ if service not in self.SUPPORTED_SERVICES:
152
+ print(f"Error: Unknown service '{service}'")
153
+ return False
154
+
155
+ cmd = ["xcrun", "simctl", "privacy"]
156
+
157
+ if self.udid:
158
+ cmd.append(self.udid)
159
+ else:
160
+ cmd.append("booted")
161
+
162
+ cmd.extend(["reset", service, bundle_id])
163
+
164
+ try:
165
+ subprocess.run(cmd, capture_output=True, check=True)
166
+
167
+ # Log audit entry
168
+ self._log_audit("reset", bundle_id, service, scenario, step)
169
+
170
+ return True
171
+ except subprocess.CalledProcessError:
172
+ return False
173
+
174
+ @staticmethod
175
+ def _log_audit(
176
+ action: str,
177
+ bundle_id: str,
178
+ service: str,
179
+ scenario: str | None = None,
180
+ step: int | None = None,
181
+ ) -> None:
182
+ """Log permission change to audit trail (for test tracking).
183
+
184
+ Args:
185
+ action: grant, revoke, or reset
186
+ bundle_id: App bundle ID
187
+ service: Service name
188
+ scenario: Test scenario name
189
+ step: Step number
190
+ """
191
+ # Could write to file, but for now just log to stdout for transparency
192
+ timestamp = datetime.now().isoformat()
193
+ location = f" (step {step})" if step else ""
194
+ scenario_info = f" in {scenario}" if scenario else ""
195
+ print(
196
+ f"[Audit] {timestamp}: {action.upper()} {service} for {bundle_id}{scenario_info}{location}"
197
+ )
198
+
199
+
200
+ def main():
201
+ """Main entry point."""
202
+ parser = argparse.ArgumentParser(description="Manage iOS app privacy and permissions")
203
+
204
+ # Required
205
+ parser.add_argument("--bundle-id", required=True, help="App bundle ID (e.g., com.example.app)")
206
+
207
+ # Action (mutually exclusive)
208
+ action_group = parser.add_mutually_exclusive_group(required=True)
209
+ action_group.add_argument(
210
+ "--grant",
211
+ help="Grant permission (service name or comma-separated list)",
212
+ )
213
+ action_group.add_argument(
214
+ "--revoke", help="Revoke permission (service name or comma-separated list)"
215
+ )
216
+ action_group.add_argument(
217
+ "--reset",
218
+ help="Reset permission to default (service name or comma-separated list)",
219
+ )
220
+ action_group.add_argument(
221
+ "--list",
222
+ action="store_true",
223
+ help="List all supported services",
224
+ )
225
+
226
+ # Test tracking
227
+ parser.add_argument(
228
+ "--scenario",
229
+ help="Test scenario name for audit trail",
230
+ )
231
+ parser.add_argument("--step", type=int, help="Step number in test scenario")
232
+
233
+ # Device
234
+ parser.add_argument(
235
+ "--udid",
236
+ help="Device UDID (auto-detects booted simulator if not provided)",
237
+ )
238
+
239
+ args = parser.parse_args()
240
+
241
+ # List supported services
242
+ if args.list:
243
+ print("Supported Privacy Services:\n")
244
+ for service, description in PrivacyManager.SUPPORTED_SERVICES.items():
245
+ print(f" {service:<15} - {description}")
246
+ print()
247
+ print("Examples:")
248
+ print(" python scripts/privacy_manager.py --grant camera --bundle-id com.app")
249
+ print(" python scripts/privacy_manager.py --revoke location --bundle-id com.app")
250
+ print(" python scripts/privacy_manager.py --grant camera,photos --bundle-id com.app")
251
+ sys.exit(0)
252
+
253
+ # Resolve UDID with auto-detection
254
+ try:
255
+ udid = resolve_udid(args.udid)
256
+ except RuntimeError as e:
257
+ print(f"Error: {e}")
258
+ sys.exit(1)
259
+
260
+ manager = PrivacyManager(udid=udid)
261
+
262
+ # Parse service names (support comma-separated list)
263
+ if args.grant:
264
+ services = [s.strip() for s in args.grant.split(",")]
265
+ action = "grant"
266
+ action_fn = manager.grant_permission
267
+ elif args.revoke:
268
+ services = [s.strip() for s in args.revoke.split(",")]
269
+ action = "revoke"
270
+ action_fn = manager.revoke_permission
271
+ else: # reset
272
+ services = [s.strip() for s in args.reset.split(",")]
273
+ action = "reset"
274
+ action_fn = manager.reset_permission
275
+
276
+ # Execute action for each service
277
+ all_success = True
278
+ for service in services:
279
+ if service not in PrivacyManager.SUPPORTED_SERVICES:
280
+ print(f"Error: Unknown service '{service}'")
281
+ all_success = False
282
+ continue
283
+
284
+ success = action_fn(
285
+ args.bundle_id,
286
+ service,
287
+ scenario=args.scenario,
288
+ step=args.step,
289
+ )
290
+
291
+ if success:
292
+ description = PrivacyManager.SUPPORTED_SERVICES[service]
293
+ print(f"✓ {action.capitalize()} {service}: {description}")
294
+ else:
295
+ print(f"✗ Failed to {action} {service}")
296
+ all_success = False
297
+
298
+ if not all_success:
299
+ sys.exit(1)
300
+
301
+ # Summary
302
+ if len(services) > 1:
303
+ print(f"\nPermissions {action}ed: {', '.join(services)}")
304
+
305
+ if args.scenario:
306
+ print(f"Test scenario: {args.scenario}" + (f" (step {args.step})" if args.step else ""))
307
+
308
+
309
+ if __name__ == "__main__":
310
+ main()
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ iOS Push Notification Simulator
4
+
5
+ Send simulated push notifications to test notification handling.
6
+ Supports custom payloads and test tracking.
7
+
8
+ Usage: python scripts/push_notification.py --bundle-id com.app --title "Alert" --body "Message"
9
+ """
10
+
11
+ import argparse
12
+ import json
13
+ import subprocess
14
+ import sys
15
+ import tempfile
16
+ from pathlib import Path
17
+
18
+ from common import resolve_udid
19
+
20
+
21
+ class PushNotificationSender:
22
+ """Sends simulated push notifications to iOS simulator."""
23
+
24
+ def __init__(self, udid: str | None = None):
25
+ """Initialize push notification sender.
26
+
27
+ Args:
28
+ udid: Optional device UDID (auto-detects booted simulator if None)
29
+ """
30
+ self.udid = udid
31
+
32
+ def send(
33
+ self,
34
+ bundle_id: str,
35
+ payload: dict | str,
36
+ _test_name: str | None = None,
37
+ _expected_behavior: str | None = None,
38
+ ) -> bool:
39
+ """
40
+ Send push notification to app.
41
+
42
+ Args:
43
+ bundle_id: Target app bundle ID
44
+ payload: Push payload (dict or JSON string) or path to JSON file
45
+ test_name: Test scenario name for tracking
46
+ expected_behavior: Expected behavior after notification arrives
47
+
48
+ Returns:
49
+ Success status
50
+ """
51
+ # Handle different payload formats
52
+ if isinstance(payload, str):
53
+ # Check if it's a file path
54
+ payload_path = Path(payload)
55
+ if payload_path.exists():
56
+ with open(payload_path) as f:
57
+ payload_data = json.load(f)
58
+ else:
59
+ # Try to parse as JSON string
60
+ try:
61
+ payload_data = json.loads(payload)
62
+ except json.JSONDecodeError:
63
+ print(f"Error: Invalid JSON payload: {payload}")
64
+ return False
65
+ else:
66
+ payload_data = payload
67
+
68
+ # Ensure payload has aps dictionary
69
+ if "aps" not in payload_data:
70
+ payload_data = {"aps": payload_data}
71
+
72
+ # Create temp file with payload
73
+ try:
74
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
75
+ json.dump(payload_data, f)
76
+ temp_payload_path = f.name
77
+
78
+ # Build simctl command
79
+ cmd = ["xcrun", "simctl", "push"]
80
+
81
+ if self.udid:
82
+ cmd.append(self.udid)
83
+ else:
84
+ cmd.append("booted")
85
+
86
+ cmd.extend([bundle_id, temp_payload_path])
87
+
88
+ # Send notification
89
+ subprocess.run(cmd, capture_output=True, text=True, check=True)
90
+
91
+ # Clean up temp file
92
+ Path(temp_payload_path).unlink()
93
+
94
+ return True
95
+
96
+ except subprocess.CalledProcessError as e:
97
+ print(f"Error sending push notification: {e.stderr}")
98
+ return False
99
+ except Exception as e:
100
+ print(f"Error: {e}")
101
+ return False
102
+
103
+ def send_simple(
104
+ self,
105
+ bundle_id: str,
106
+ title: str | None = None,
107
+ body: str | None = None,
108
+ badge: int | None = None,
109
+ sound: bool = True,
110
+ ) -> bool:
111
+ """
112
+ Send simple push notification with common parameters.
113
+
114
+ Args:
115
+ bundle_id: Target app bundle ID
116
+ title: Alert title
117
+ body: Alert body
118
+ badge: Badge number
119
+ sound: Whether to play sound
120
+
121
+ Returns:
122
+ Success status
123
+ """
124
+ payload = {}
125
+
126
+ if title or body:
127
+ alert = {}
128
+ if title:
129
+ alert["title"] = title
130
+ if body:
131
+ alert["body"] = body
132
+ payload["alert"] = alert
133
+
134
+ if badge is not None:
135
+ payload["badge"] = badge
136
+
137
+ if sound:
138
+ payload["sound"] = "default"
139
+
140
+ # Wrap in aps
141
+ full_payload = {"aps": payload}
142
+
143
+ return self.send(bundle_id, full_payload)
144
+
145
+
146
+ def main():
147
+ """Main entry point."""
148
+ parser = argparse.ArgumentParser(description="Send simulated push notification to iOS app")
149
+
150
+ # Required
151
+ parser.add_argument(
152
+ "--bundle-id", required=True, help="Target app bundle ID (e.g., com.example.app)"
153
+ )
154
+
155
+ # Simple payload options
156
+ parser.add_argument("--title", help="Alert title (for simple notifications)")
157
+ parser.add_argument("--body", help="Alert body message")
158
+ parser.add_argument("--badge", type=int, help="Badge number")
159
+ parser.add_argument("--no-sound", action="store_true", help="Don't play notification sound")
160
+
161
+ # Custom payload
162
+ parser.add_argument(
163
+ "--payload",
164
+ help="Custom JSON payload file or inline JSON string",
165
+ )
166
+
167
+ # Test tracking
168
+ parser.add_argument("--test-name", help="Test scenario name for tracking")
169
+ parser.add_argument(
170
+ "--expected",
171
+ help="Expected behavior after notification",
172
+ )
173
+
174
+ # Device
175
+ parser.add_argument(
176
+ "--udid",
177
+ help="Device UDID (auto-detects booted simulator if not provided)",
178
+ )
179
+
180
+ args = parser.parse_args()
181
+
182
+ # Resolve UDID with auto-detection
183
+ try:
184
+ udid = resolve_udid(args.udid)
185
+ except RuntimeError as e:
186
+ print(f"Error: {e}")
187
+ sys.exit(1)
188
+
189
+ sender = PushNotificationSender(udid=udid)
190
+
191
+ # Send notification
192
+ if args.payload:
193
+ # Custom payload mode
194
+ success = sender.send(args.bundle_id, args.payload)
195
+ else:
196
+ # Simple notification mode
197
+ success = sender.send_simple(
198
+ args.bundle_id,
199
+ title=args.title,
200
+ body=args.body,
201
+ badge=args.badge,
202
+ sound=not args.no_sound,
203
+ )
204
+
205
+ if success:
206
+ # Token-efficient output
207
+ output = "Push notification sent"
208
+
209
+ if args.test_name:
210
+ output += f" (test: {args.test_name})"
211
+
212
+ print(output)
213
+
214
+ if args.expected:
215
+ print(f"Expected: {args.expected}")
216
+
217
+ print()
218
+ print("Notification details:")
219
+ if args.title:
220
+ print(f" Title: {args.title}")
221
+ if args.body:
222
+ print(f" Body: {args.body}")
223
+ if args.badge:
224
+ print(f" Badge: {args.badge}")
225
+
226
+ print()
227
+ print("Verify notification handling:")
228
+ print("1. Check app log output: python scripts/log_monitor.py --app " + args.bundle_id)
229
+ print(
230
+ "2. Capture state: python scripts/app_state_capture.py --app-bundle-id "
231
+ + args.bundle_id
232
+ )
233
+
234
+ else:
235
+ print("Failed to send push notification")
236
+ sys.exit(1)
237
+
238
+
239
+ if __name__ == "__main__":
240
+ main()