fsq-mac 0.1.1__tar.gz
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.
- fsq_mac-0.1.1/.claude/skills/mac-automation.md +206 -0
- fsq_mac-0.1.1/.github/workflows/ci.yml +57 -0
- fsq_mac-0.1.1/.github/workflows/publish.yml +57 -0
- fsq_mac-0.1.1/.gitignore +39 -0
- fsq_mac-0.1.1/CLAUDE.md +51 -0
- fsq_mac-0.1.1/LICENSE +21 -0
- fsq_mac-0.1.1/PKG-INFO +12 -0
- fsq_mac-0.1.1/README.md +124 -0
- fsq_mac-0.1.1/docs/architecture-review.md +369 -0
- fsq_mac-0.1.1/docs/architecture.md +134 -0
- fsq_mac-0.1.1/docs/cli-reference.md +197 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase0-foundation-design.md +100 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase0-implementation.md +609 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase2-core-differentiators-design.md +258 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase2-core-differentiators-implementation.md +378 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-ci-design.md +136 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-ci-implementation.md +173 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-trace-loop-design.md +258 -0
- fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-trace-loop-implementation.md +334 -0
- fsq_mac-0.1.1/docs/plans/2026-04-06-phase4-codegen-perf-plugins-docs.md +171 -0
- fsq_mac-0.1.1/docs/plugins.md +190 -0
- fsq_mac-0.1.1/docs/quickstart.md +172 -0
- fsq_mac-0.1.1/docs/releasing.md +151 -0
- fsq_mac-0.1.1/docs/testing/manual-e2e-test-plan.md +346 -0
- fsq_mac-0.1.1/docs/testing/phase-0-1-test-plan.md +273 -0
- fsq_mac-0.1.1/docs/testing/phase-4-manual-e2e-test-plan.md +13 -0
- fsq_mac-0.1.1/docs/trace-codegen.md +228 -0
- fsq_mac-0.1.1/pyproject.toml +40 -0
- fsq_mac-0.1.1/src/fsq_mac/__init__.py +6 -0
- fsq_mac-0.1.1/src/fsq_mac/adapters/__init__.py +56 -0
- fsq_mac-0.1.1/src/fsq_mac/adapters/appium_mac2.py +1224 -0
- fsq_mac-0.1.1/src/fsq_mac/adapters/protocol.py +108 -0
- fsq_mac-0.1.1/src/fsq_mac/cli.py +333 -0
- fsq_mac-0.1.1/src/fsq_mac/client.py +175 -0
- fsq_mac-0.1.1/src/fsq_mac/codegen.py +158 -0
- fsq_mac-0.1.1/src/fsq_mac/conf/config.template.json +14 -0
- fsq_mac-0.1.1/src/fsq_mac/core.py +711 -0
- fsq_mac-0.1.1/src/fsq_mac/daemon.py +496 -0
- fsq_mac-0.1.1/src/fsq_mac/doctor.py +204 -0
- fsq_mac-0.1.1/src/fsq_mac/formatters.py +89 -0
- fsq_mac-0.1.1/src/fsq_mac/models.py +303 -0
- fsq_mac-0.1.1/src/fsq_mac/session.py +140 -0
- fsq_mac-0.1.1/src/fsq_mac/trace.py +205 -0
- fsq_mac-0.1.1/tests/__init__.py +0 -0
- fsq_mac-0.1.1/tests/conftest.py +48 -0
- fsq_mac-0.1.1/tests/test_adapter_delays.py +56 -0
- fsq_mac-0.1.1/tests/test_adapter_helpers.py +151 -0
- fsq_mac-0.1.1/tests/test_adapter_methods.py +739 -0
- fsq_mac-0.1.1/tests/test_adapter_registry.py +91 -0
- fsq_mac-0.1.1/tests/test_ci_workflow.py +54 -0
- fsq_mac-0.1.1/tests/test_cli.py +522 -0
- fsq_mac-0.1.1/tests/test_cli_version.py +20 -0
- fsq_mac-0.1.1/tests/test_client.py +287 -0
- fsq_mac-0.1.1/tests/test_codegen.py +227 -0
- fsq_mac-0.1.1/tests/test_core.py +476 -0
- fsq_mac-0.1.1/tests/test_daemon.py +387 -0
- fsq_mac-0.1.1/tests/test_daemon_lock.py +37 -0
- fsq_mac-0.1.1/tests/test_doctor.py +182 -0
- fsq_mac-0.1.1/tests/test_error_propagation.py +62 -0
- fsq_mac-0.1.1/tests/test_find.py +42 -0
- fsq_mac-0.1.1/tests/test_formatters.py +137 -0
- fsq_mac-0.1.1/tests/test_inspect_refs.py +51 -0
- fsq_mac-0.1.1/tests/test_models.py +167 -0
- fsq_mac-0.1.1/tests/test_plugin_system.py +89 -0
- fsq_mac-0.1.1/tests/test_protocol.py +32 -0
- fsq_mac-0.1.1/tests/test_routes.py +162 -0
- fsq_mac-0.1.1/tests/test_screenshot.py +74 -0
- fsq_mac-0.1.1/tests/test_semantics.py +92 -0
- fsq_mac-0.1.1/tests/test_session.py +43 -0
- fsq_mac-0.1.1/tests/test_timeout.py +41 -0
- fsq_mac-0.1.1/tests/test_trace.py +171 -0
- fsq_mac-0.1.1/tests/test_tree_cache.py +171 -0
- fsq_mac-0.1.1/tests/test_type_core.py +65 -0
- fsq_mac-0.1.1/tests/test_type_verify.py +53 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# macOS Automation with fsq-mac
|
|
2
|
+
|
|
3
|
+
Use when the user asks to automate, interact with, test, or control a macOS native application. Triggers: mac automation, click button, UI testing, accessibility, element inspect, screenshot, app launch, macOS app, native app automation.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Appium server running (`appium` in a terminal)
|
|
8
|
+
- Mac2 driver installed (`appium driver install mac2`)
|
|
9
|
+
- macOS Accessibility permissions granted
|
|
10
|
+
- Run `mac doctor` to verify setup
|
|
11
|
+
|
|
12
|
+
## Core Workflow
|
|
13
|
+
|
|
14
|
+
Every automation session follows this pattern:
|
|
15
|
+
|
|
16
|
+
1. **Start session** — `mac session start`
|
|
17
|
+
2. **Launch/activate app** — `mac app launch <bundle_id>`
|
|
18
|
+
3. **Inspect UI** — `mac element inspect` to discover elements
|
|
19
|
+
4. **Act** — click, type, scroll, etc. using element refs (e0, e1, ...)
|
|
20
|
+
5. **Verify** — `mac capture screenshot` or re-inspect to confirm result
|
|
21
|
+
6. **End session** — `mac session end`
|
|
22
|
+
|
|
23
|
+
## Command Reference
|
|
24
|
+
|
|
25
|
+
### Session
|
|
26
|
+
| Command | Description |
|
|
27
|
+
|---------|-------------|
|
|
28
|
+
| `mac session start` | Start a new automation session |
|
|
29
|
+
| `mac session get` | Get current session info |
|
|
30
|
+
| `mac session list` | List all sessions |
|
|
31
|
+
| `mac session end` | End current session |
|
|
32
|
+
|
|
33
|
+
### Application
|
|
34
|
+
| Command | Description |
|
|
35
|
+
|---------|-------------|
|
|
36
|
+
| `mac app launch <bundle_id>` | Launch app (e.g. `com.apple.calculator`) |
|
|
37
|
+
| `mac app activate <bundle_id>` | Bring app to front |
|
|
38
|
+
| `mac app current` | Get frontmost app info |
|
|
39
|
+
| `mac app list` | List running apps |
|
|
40
|
+
| `mac app terminate <bundle_id>` | Terminate app (requires `--allow-dangerous`) |
|
|
41
|
+
|
|
42
|
+
### Element
|
|
43
|
+
| Command | Description |
|
|
44
|
+
|---------|-------------|
|
|
45
|
+
| `mac element inspect` | List all visible UI elements with refs (e0, e1, ...) |
|
|
46
|
+
| `mac element find <locator>` | Find element by locator value |
|
|
47
|
+
| `mac element click <ref>` | Click element (e.g. `e5`) |
|
|
48
|
+
| `mac element right-click <ref>` | Right-click element |
|
|
49
|
+
| `mac element double-click <ref>` | Double-click element |
|
|
50
|
+
| `mac element type <ref> <text>` | Type text into element |
|
|
51
|
+
| `mac element scroll <ref> [up|down|left|right]` | Scroll element |
|
|
52
|
+
| `mac element hover <ref>` | Hover over element |
|
|
53
|
+
| `mac element drag <source> <target>` | Drag from source to target element |
|
|
54
|
+
|
|
55
|
+
### Input (no element target)
|
|
56
|
+
| Command | Description |
|
|
57
|
+
|---------|-------------|
|
|
58
|
+
| `mac input key <key>` | Press key (return, space, tab, escape, etc.) |
|
|
59
|
+
| `mac input hotkey <combo>` | Key combo (e.g. `command+c`, `command+shift+s`) |
|
|
60
|
+
| `mac input text <text>` | Type text to focused element |
|
|
61
|
+
|
|
62
|
+
### Capture
|
|
63
|
+
| Command | Description |
|
|
64
|
+
|---------|-------------|
|
|
65
|
+
| `mac capture screenshot [path]` | Take screenshot (default: ./screenshot.png) |
|
|
66
|
+
| `mac capture screenshot --element <ref> [path]` | Screenshot a specific element |
|
|
67
|
+
| `mac capture screenshot --rect x,y,w,h [path]` | Screenshot a region |
|
|
68
|
+
| `mac capture ui-tree` | Get raw UI element tree (XML) |
|
|
69
|
+
|
|
70
|
+
### Window
|
|
71
|
+
| Command | Description |
|
|
72
|
+
|---------|-------------|
|
|
73
|
+
| `mac window current` | Get frontmost window info |
|
|
74
|
+
| `mac window list` | List windows of managed app |
|
|
75
|
+
| `mac window focus <index>` | Focus window by index |
|
|
76
|
+
|
|
77
|
+
### Wait
|
|
78
|
+
| Command | Description |
|
|
79
|
+
|---------|-------------|
|
|
80
|
+
| `mac wait element <locator>` | Wait for element to appear |
|
|
81
|
+
| `mac wait window <title>` | Wait for window to appear |
|
|
82
|
+
| `mac wait app <bundle_id>` | Wait for app to start |
|
|
83
|
+
|
|
84
|
+
### Doctor
|
|
85
|
+
| Command | Description |
|
|
86
|
+
|---------|-------------|
|
|
87
|
+
| `mac doctor` | Run all diagnostics |
|
|
88
|
+
| `mac doctor permissions` | Check Accessibility permissions |
|
|
89
|
+
| `mac doctor backend` | Check Appium server and Mac2 driver |
|
|
90
|
+
|
|
91
|
+
## Global Options
|
|
92
|
+
|
|
93
|
+
| Option | Description |
|
|
94
|
+
|--------|-------------|
|
|
95
|
+
| `--session <id>` | Target a specific session (default: most recent) |
|
|
96
|
+
| `--strategy <strategy>` | Locator strategy: accessibility_id, name, xpath, class_name, ios_predicate |
|
|
97
|
+
| `--timeout <ms>` | Timeout in milliseconds (default: 120000) |
|
|
98
|
+
| `--pretty` | Human-friendly output (default: compact JSON for agents) |
|
|
99
|
+
| `--allow-dangerous` | Allow dangerous operations (e.g. app terminate) |
|
|
100
|
+
| `--version` | Show version |
|
|
101
|
+
|
|
102
|
+
## Response Format
|
|
103
|
+
|
|
104
|
+
Every command returns a JSON envelope:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"ok": true,
|
|
109
|
+
"command": "element.click",
|
|
110
|
+
"session_id": "s1",
|
|
111
|
+
"data": {},
|
|
112
|
+
"error": null,
|
|
113
|
+
"meta": {
|
|
114
|
+
"backend": "appium_mac2",
|
|
115
|
+
"duration_ms": 1234,
|
|
116
|
+
"timestamp": "2026-04-03T10:00:00Z",
|
|
117
|
+
"frontmost_app": "com.apple.calculator",
|
|
118
|
+
"frontmost_window": "Calculator"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
On error:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"ok": false,
|
|
128
|
+
"command": "element.click",
|
|
129
|
+
"error": {
|
|
130
|
+
"code": "ELEMENT_NOT_FOUND",
|
|
131
|
+
"message": "Element 'e5' not found",
|
|
132
|
+
"retryable": true,
|
|
133
|
+
"suggested_next_action": "mac element inspect",
|
|
134
|
+
"doctor_hint": null
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Error Recovery
|
|
140
|
+
|
|
141
|
+
When a command fails, check `error.code` and follow this recovery strategy:
|
|
142
|
+
|
|
143
|
+
| Error Code | Recovery |
|
|
144
|
+
|------------|----------|
|
|
145
|
+
| `SESSION_NOT_FOUND` | Run `mac session start` |
|
|
146
|
+
| `BACKEND_UNAVAILABLE` | Run `mac doctor backend`, ensure Appium is running |
|
|
147
|
+
| `ELEMENT_NOT_FOUND` | Run `mac element inspect` to refresh elements, find correct ref |
|
|
148
|
+
| `ELEMENT_REFERENCE_STALE` | Run `mac element inspect` — refs are invalidated after mutations |
|
|
149
|
+
| `ELEMENT_AMBIGUOUS` | Use `--first-match` or refine the locator strategy |
|
|
150
|
+
| `TYPE_VERIFICATION_FAILED` | Typed value didn't match; re-type or verify the target field |
|
|
151
|
+
| `ACTION_BLOCKED` | Add `--allow-dangerous` flag for dangerous operations |
|
|
152
|
+
| `TIMEOUT` | Increase `--timeout` or check if the target exists |
|
|
153
|
+
| `PERMISSION_DENIED` | Run `mac doctor permissions` and grant Accessibility access |
|
|
154
|
+
|
|
155
|
+
## Element References
|
|
156
|
+
|
|
157
|
+
- `mac element inspect` assigns refs: `e0`, `e1`, `e2`, ...
|
|
158
|
+
- Refs are **scoped to the last inspect/find** — a new inspect invalidates all previous refs
|
|
159
|
+
- After any mutation (click, type, etc.), refs may become stale
|
|
160
|
+
- Pattern: **inspect -> act -> re-inspect** for multi-step flows
|
|
161
|
+
|
|
162
|
+
## Safety Model
|
|
163
|
+
|
|
164
|
+
Commands are classified into three safety levels:
|
|
165
|
+
|
|
166
|
+
- **SAFE** — read-only operations (inspect, screenshot, list, wait, doctor)
|
|
167
|
+
- **GUARDED** — operations that modify state (click, type, launch, activate)
|
|
168
|
+
- **DANGEROUS** — destructive operations (terminate) — requires `--allow-dangerous`
|
|
169
|
+
|
|
170
|
+
## Common Patterns
|
|
171
|
+
|
|
172
|
+
### Calculator: 5 + 3
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
mac session start
|
|
176
|
+
mac app launch com.apple.calculator
|
|
177
|
+
mac element inspect # discover elements
|
|
178
|
+
mac element click e5 # click "5"
|
|
179
|
+
mac element inspect # re-inspect after mutation
|
|
180
|
+
mac element click e2 # click "+"
|
|
181
|
+
mac element inspect
|
|
182
|
+
mac element click e3 # click "3"
|
|
183
|
+
mac element inspect
|
|
184
|
+
mac element click e8 # click "="
|
|
185
|
+
mac capture screenshot ./result.png # verify result
|
|
186
|
+
mac session end
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Type text into a search field
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
mac session start
|
|
193
|
+
mac app activate com.apple.safari
|
|
194
|
+
mac element inspect # find the search field
|
|
195
|
+
mac element type e12 "hello world" # type into it
|
|
196
|
+
mac input hotkey command+a # select all
|
|
197
|
+
mac capture screenshot ./typed.png
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Tips
|
|
201
|
+
|
|
202
|
+
- Always run `mac element inspect` before interacting with elements
|
|
203
|
+
- After any click/type, run `mac element inspect` again — refs are invalidated
|
|
204
|
+
- Use `mac capture screenshot` to visually verify results between steps
|
|
205
|
+
- Use `--pretty` when debugging interactively, omit for agent consumption
|
|
206
|
+
- Common bundle IDs: `com.apple.calculator`, `com.apple.Safari`, `com.apple.finder`, `com.apple.TextEdit`, `com.apple.systempreferences`
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ['3.10', '3.11', '3.12']
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: uv sync --group dev
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: uv run pytest tests/ --junitxml=test-results/junit.xml --cov=fsq_mac --cov-report=xml:test-results/coverage.xml --cov-report=term-missing
|
|
34
|
+
|
|
35
|
+
- name: Upload JUnit XML
|
|
36
|
+
if: always()
|
|
37
|
+
uses: actions/upload-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: junit-results-py${{ matrix.python-version }}
|
|
40
|
+
path: test-results/junit.xml
|
|
41
|
+
if-no-files-found: error
|
|
42
|
+
|
|
43
|
+
- name: Upload coverage report
|
|
44
|
+
if: always()
|
|
45
|
+
uses: actions/upload-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: coverage-py${{ matrix.python-version }}
|
|
48
|
+
path: test-results/coverage.xml
|
|
49
|
+
if-no-files-found: ignore
|
|
50
|
+
|
|
51
|
+
- name: Upload runtime artifacts
|
|
52
|
+
if: always()
|
|
53
|
+
uses: actions/upload-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: runtime-artifacts-py${{ matrix.python-version }}
|
|
56
|
+
path: artifacts/
|
|
57
|
+
if-no-files-found: ignore
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ['3.10', '3.11', '3.12']
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v5
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --group dev
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: uv run pytest tests/ --junitxml=test-results/junit.xml --cov=fsq_mac --cov-report=xml:test-results/coverage.xml --cov-report=term-missing
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
needs: test
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
permissions:
|
|
37
|
+
contents: read
|
|
38
|
+
id-token: write
|
|
39
|
+
environment:
|
|
40
|
+
name: pypi
|
|
41
|
+
steps:
|
|
42
|
+
- name: Checkout
|
|
43
|
+
uses: actions/checkout@v4
|
|
44
|
+
|
|
45
|
+
- name: Set up Python
|
|
46
|
+
uses: actions/setup-python@v5
|
|
47
|
+
with:
|
|
48
|
+
python-version: '3.12'
|
|
49
|
+
|
|
50
|
+
- name: Install uv
|
|
51
|
+
uses: astral-sh/setup-uv@v5
|
|
52
|
+
|
|
53
|
+
- name: Build distributions
|
|
54
|
+
run: uv build
|
|
55
|
+
|
|
56
|
+
- name: Publish to PyPI
|
|
57
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
fsq_mac-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
|
|
14
|
+
# IDE
|
|
15
|
+
.idea/
|
|
16
|
+
.vscode/
|
|
17
|
+
*.swp
|
|
18
|
+
*.swo
|
|
19
|
+
|
|
20
|
+
# State directory (local)
|
|
21
|
+
.fsq-mac/
|
|
22
|
+
|
|
23
|
+
# Config (user-specific)
|
|
24
|
+
src/fsq_mac/conf/appium_conf.json
|
|
25
|
+
|
|
26
|
+
# OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
Thumbs.db
|
|
29
|
+
|
|
30
|
+
# Test artifacts
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
.coverage
|
|
33
|
+
htmlcov/
|
|
34
|
+
|
|
35
|
+
# uv
|
|
36
|
+
uv.lock
|
|
37
|
+
|
|
38
|
+
# Runtime artifacts
|
|
39
|
+
artifacts/
|
fsq_mac-0.1.1/CLAUDE.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
fsq-mac is an agent-first macOS native application automation CLI. It uses a daemon/client architecture where the CLI communicates over HTTP with a Starlette/Uvicorn daemon that drives Appium Mac2.
|
|
6
|
+
|
|
7
|
+
## Build & Run
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Install dependencies
|
|
11
|
+
uv sync
|
|
12
|
+
|
|
13
|
+
# Install with dev dependencies
|
|
14
|
+
uv sync --group dev
|
|
15
|
+
|
|
16
|
+
# Run tests
|
|
17
|
+
uv run pytest tests/ -v
|
|
18
|
+
|
|
19
|
+
# Run the CLI
|
|
20
|
+
uv run mac session start
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
src/fsq_mac/
|
|
27
|
+
├── cli.py # CLI entry point (argparse)
|
|
28
|
+
├── client.py # HTTP client → daemon
|
|
29
|
+
├── daemon.py # Starlette HTTP daemon
|
|
30
|
+
├── core.py # Product semantics layer
|
|
31
|
+
├── session.py # Multi-session management
|
|
32
|
+
├── models.py # Data models, ErrorCode enum, response helpers
|
|
33
|
+
├── formatters.py # Output formatters (JSON, pretty, table)
|
|
34
|
+
├── doctor.py # Environment diagnostics
|
|
35
|
+
└── adapters/
|
|
36
|
+
└── appium_mac2.py # Appium Mac2 WebDriver adapter
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `cli.py` → `client.py` → HTTP → `daemon.py` → `core.py` → `session.py` → `adapters/appium_mac2.py`
|
|
40
|
+
- State directory: `~/.fsq-mac/`
|
|
41
|
+
- Daemon auto-starts on first CLI call, auto-exits after 30 min idle
|
|
42
|
+
|
|
43
|
+
## Key Conventions
|
|
44
|
+
|
|
45
|
+
- **Python 3.10+**, managed with `uv`
|
|
46
|
+
- **src layout**: all source under `src/fsq_mac/`, imports use `from fsq_mac.module import ...`
|
|
47
|
+
- **Response envelope**: Every command returns `{"ok": bool, "data": {...}, "error": {"code": "...", "message": "...", "retryable": bool, "suggested_next_action": "..."}, "meta": {...}}`
|
|
48
|
+
- **Safety classification**: SAFE / GUARDED / DANGEROUS — dangerous commands require `--allow-dangerous`
|
|
49
|
+
- **Element refs**: Static refs (`e0`, `e1`, ...) bound during `element inspect`, invalidated on mutation
|
|
50
|
+
- **AppleScript**: Used for frontmost app/window queries; use `_safe_applescript_str()` to prevent injection
|
|
51
|
+
- **Commit messages**: Present-tense verb, first line under 50 chars
|
fsq_mac-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Microsoft Corporation
|
|
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.
|
fsq_mac-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fsq-mac
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Agent-first macOS automation CLI
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: appium-python-client>=2.0.0
|
|
8
|
+
Requires-Dist: httpx>=0.27.0
|
|
9
|
+
Requires-Dist: psutil>=5.9.0
|
|
10
|
+
Requires-Dist: selenium>=4.0.0
|
|
11
|
+
Requires-Dist: starlette>=0.38.0
|
|
12
|
+
Requires-Dist: uvicorn>=0.30.0
|
fsq_mac-0.1.1/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# fsq-mac
|
|
2
|
+
|
|
3
|
+
Agent-first macOS native application automation CLI.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`fsq-mac` is a daemon/client CLI tool that drives macOS native applications via Appium Mac2. It is designed for AI agent consumption — every command returns a structured JSON envelope with `ok`, `error.code`, `retryable`, and `suggested_next_action` fields.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
CLI (cli.py) → HTTP Client → Daemon (Starlette/Uvicorn) → Core → Adapter (Appium Mac2)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- **Daemon auto-lifecycle**: Starts on first CLI call, auto-exits after 30 min idle
|
|
16
|
+
- **Session management**: Monotonic IDs, stale cleanup on restart
|
|
17
|
+
- **Safety classification**: Commands are SAFE / GUARDED / DANGEROUS (gated by `--allow-dangerous`)
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
- Python 3.10+
|
|
22
|
+
- [Appium](https://appium.io/) with [mac2 driver](https://github.com/nicedoc/appium-mac2-driver)
|
|
23
|
+
- macOS Accessibility permissions granted to the terminal
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv sync
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Start a session (daemon auto-starts)
|
|
35
|
+
mac session start
|
|
36
|
+
|
|
37
|
+
# Inspect the frontmost app UI tree
|
|
38
|
+
mac element inspect --pretty
|
|
39
|
+
|
|
40
|
+
# Click an element by ref
|
|
41
|
+
mac element click e3
|
|
42
|
+
|
|
43
|
+
# Type text into an element
|
|
44
|
+
mac element type e5 "hello world"
|
|
45
|
+
|
|
46
|
+
# Get frontmost application info
|
|
47
|
+
mac app current --pretty
|
|
48
|
+
|
|
49
|
+
# Get frontmost window info (title, position, size)
|
|
50
|
+
mac window current --pretty
|
|
51
|
+
|
|
52
|
+
# Wait for an element
|
|
53
|
+
mac element wait "Submit" accessibility_id --timeout 5000
|
|
54
|
+
|
|
55
|
+
# End session
|
|
56
|
+
mac session end s1
|
|
57
|
+
|
|
58
|
+
# Check environment
|
|
59
|
+
mac doctor
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Commands
|
|
63
|
+
|
|
64
|
+
| Command | Description |
|
|
65
|
+
|---------|-------------|
|
|
66
|
+
| `session start` | Start automation session |
|
|
67
|
+
| `session end <id>` | End a session |
|
|
68
|
+
| `session list` | List active sessions |
|
|
69
|
+
| `element inspect` | Inspect UI tree, bind element refs |
|
|
70
|
+
| `element find <query> <by>` | Find elements by locator |
|
|
71
|
+
| `element click <ref>` | Click an element |
|
|
72
|
+
| `element type <ref> <text>` | Type text into an element (with verification) |
|
|
73
|
+
| `element attr <ref> <name>` | Get element attribute |
|
|
74
|
+
| `element scroll <ref> <dir>` | Scroll an element |
|
|
75
|
+
| `element wait <query> <by>` | Wait for element to appear |
|
|
76
|
+
| `app current` | Get frontmost application info |
|
|
77
|
+
| `app list` | List running applications |
|
|
78
|
+
| `app launch <bundle_id>` | Launch an application |
|
|
79
|
+
| `app terminate <bundle_id>` | Terminate an application |
|
|
80
|
+
| `app activate <bundle_id>` | Activate (bring to front) an application |
|
|
81
|
+
| `window current` | Get frontmost window info (title, position, size) |
|
|
82
|
+
| `window list` | List windows of frontmost app |
|
|
83
|
+
| `window switch <index>` | Switch to a window by index |
|
|
84
|
+
| `screenshot` | Take a screenshot |
|
|
85
|
+
| `doctor` | Check environment and dependencies |
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## CI
|
|
89
|
+
|
|
90
|
+
GitHub Actions runs the repository test suite from `.github/workflows/ci.yml`.
|
|
91
|
+
The CI test command is `uv run pytest tests/ --junitxml=test-results/junit.xml`.
|
|
92
|
+
CI uploads `test-results/junit.xml` and any generated `artifacts/` directory as workflow artifacts.
|
|
93
|
+
|
|
94
|
+
## Release
|
|
95
|
+
|
|
96
|
+
Package publishing is handled by `.github/workflows/publish.yml`.
|
|
97
|
+
Pushing a tag matching `v*` runs the full test matrix first, then builds the wheel and sdist, and finally publishes to PyPI via Trusted Publishing if all tests pass.
|
|
98
|
+
|
|
99
|
+
See [docs/releasing.md](docs/releasing.md) for the full release procedure and PyPI Trusted Publishing setup.
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git tag v0.1.1
|
|
105
|
+
git push origin v0.1.1
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Install with dev dependencies
|
|
112
|
+
uv sync --group dev
|
|
113
|
+
|
|
114
|
+
# Run tests
|
|
115
|
+
uv run pytest tests/ -v
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## State Directory
|
|
119
|
+
|
|
120
|
+
Session state is stored in `~/.fsq-mac/`. The daemon writes PID and port files there for lifecycle management.
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT
|