android-watcher 1.0.0__py3-none-any.whl

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 (44) hide show
  1. android_watcher/__init__.py +10 -0
  2. android_watcher/catalog/__init__.py +32 -0
  3. android_watcher/catalog/catalog.toml +531 -0
  4. android_watcher/cli.py +161 -0
  5. android_watcher/config.py +262 -0
  6. android_watcher/detect/__init__.py +1 -0
  7. android_watcher/detect/_normalize.py +192 -0
  8. android_watcher/detect/android_sitemap.py +540 -0
  9. android_watcher/detect/base.py +14 -0
  10. android_watcher/detect/content.py +99 -0
  11. android_watcher/detect/feed.py +135 -0
  12. android_watcher/detect/sitemap.py +203 -0
  13. android_watcher/doctor.py +125 -0
  14. android_watcher/fetch.py +162 -0
  15. android_watcher/group.py +79 -0
  16. android_watcher/lock.py +32 -0
  17. android_watcher/models.py +156 -0
  18. android_watcher/notify/__init__.py +1 -0
  19. android_watcher/notify/base.py +21 -0
  20. android_watcher/notify/email.py +52 -0
  21. android_watcher/notify/html.py +114 -0
  22. android_watcher/notify/render.py +239 -0
  23. android_watcher/notify/slack.py +124 -0
  24. android_watcher/notify/telegram.py +46 -0
  25. android_watcher/rank.py +84 -0
  26. android_watcher/registry.py +38 -0
  27. android_watcher/run.py +283 -0
  28. android_watcher/schedule.py +488 -0
  29. android_watcher/seed/__init__.py +45 -0
  30. android_watcher/seed/seed.sql.gz +0 -0
  31. android_watcher/store.py +492 -0
  32. android_watcher/triage/__init__.py +1 -0
  33. android_watcher/triage/base.py +25 -0
  34. android_watcher/triage/claude_cli.py +185 -0
  35. android_watcher/triage/noop.py +24 -0
  36. android_watcher/tui/__init__.py +1 -0
  37. android_watcher/tui/app.py +163 -0
  38. android_watcher/tui/configio.py +215 -0
  39. android_watcher/tui/screens.py +927 -0
  40. android_watcher-1.0.0.dist-info/METADATA +310 -0
  41. android_watcher-1.0.0.dist-info/RECORD +44 -0
  42. android_watcher-1.0.0.dist-info/WHEEL +4 -0
  43. android_watcher-1.0.0.dist-info/entry_points.txt +2 -0
  44. android_watcher-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,310 @@
1
+ Metadata-Version: 2.4
2
+ Name: android-watcher
3
+ Version: 1.0.0
4
+ Summary: Self-hosted CLI that watches Google's official Android sites and delivers AI-triaged change digests.
5
+ Project-URL: Homepage, https://github.com/krayong/android-watcher
6
+ Project-URL: Repository, https://github.com/krayong/android-watcher
7
+ Project-URL: Issues, https://github.com/krayong/android-watcher/issues
8
+ Author-email: Karan Gourisaria <androidwatcher@krayong.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: android,changelog,digest,monitoring,rss,sitemap
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: defusedxml==0.7.1
22
+ Requires-Dist: httpx==0.28.1
23
+ Requires-Dist: platformdirs==4.10.0
24
+ Requires-Dist: textual==8.2.7
25
+ Description-Content-Type: text/markdown
26
+
27
+ <p align="center">
28
+ <img src="assets/logo.svg" alt="Android Watcher" width="160">
29
+ </p>
30
+
31
+ <h1 align="center">Android Watcher</h1>
32
+
33
+ <p align="center">
34
+ Watch every official Google site for Android and Android developers, and get a
35
+ ranked, AI-triaged digest when something actually changes.
36
+ </p>
37
+
38
+ <p align="center">
39
+ <a href="https://github.com/krayong/android-watcher/actions/workflows/ci.yml"><img src="https://github.com/krayong/android-watcher/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
40
+ <a href="https://pypi.org/project/android-watcher/"><img src="https://img.shields.io/pypi/v/android-watcher.svg" alt="PyPI"></a>
41
+ <img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+">
42
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
43
+ <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
44
+ <a href="https://github.com/pre-commit/pre-commit"><img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit" alt="pre-commit"></a>
45
+ </p>
46
+
47
+ <p align="center">
48
+ Self-hosted CLI. No cloud subscription. MIT.
49
+ </p>
50
+
51
+ ---
52
+
53
+ ## What it does
54
+
55
+ Google ships Android news across dozens of properties: platform and AOSP release
56
+ notes, Android Studio and Gradle Plugin changelogs, the AndroidX feeds, the
57
+ Developers Blog, Material Design, YouTube, and many sections of
58
+ `developer.android.com`. Keeping up means babysitting a pile of RSS readers and
59
+ bookmarks, and most of what changes is a typo fix or a template reflow.
60
+
61
+ `android-watcher` checks a curated catalog of official sources on a schedule,
62
+ detects real changes (not cosmetic churn), uses Claude to decide what is worth
63
+ your attention and write a short description, ranks the result, and delivers a
64
+ digest to email, Slack, or Telegram. When nothing substantive changed, it says so
65
+ instead of padding a digest with noise.
66
+
67
+ Install it once, run the interactive setup wizard, and receive a daily (or
68
+ hourly, or weekly) digest.
69
+
70
+ ---
71
+
72
+ ## Install
73
+
74
+ **Homebrew** (recommended):
75
+
76
+ ```sh
77
+ brew tap krayong/android-watcher https://github.com/krayong/android-watcher
78
+ brew install android-watcher
79
+ ```
80
+
81
+ The tap points straight at this repo's `Formula/`, so `brew upgrade` tracks new
82
+ releases. (A bare `brew install android-watcher` without the tap only works once a
83
+ formula is accepted into homebrew-core.)
84
+
85
+ **PyPI** (via uv or pipx):
86
+
87
+ ```sh
88
+ uv tool install android-watcher # via uv
89
+ pipx install android-watcher # via pipx
90
+ ```
91
+
92
+ **From source:**
93
+
94
+ ```sh
95
+ git clone https://github.com/krayong/android-watcher
96
+ cd android-watcher
97
+ uv tool install .
98
+ ```
99
+
100
+ Requires **Python 3.11+**.
101
+
102
+ **AI prerequisite:** The triage backend calls `claude -p` (the Claude CLI). No
103
+ API key is required; the CLI handles authentication. Install and authenticate
104
+ `claude` separately if you want AI triage. To skip it, set
105
+ `[ai] mode = "off"` and android-watcher still delivers a digest (every detected
106
+ change is marked substantive, with no description).
107
+
108
+ ---
109
+
110
+ ## Quickstart
111
+
112
+ ```sh
113
+ android-watcher # open the Textual setup wizard
114
+ android-watcher test # dry run: render the digest to stdout, send nothing
115
+ android-watcher run # one detection-triage-notify pass
116
+ android-watcher schedule install # install the native launchd / systemd / cron entry
117
+ android-watcher schedule status # confirm the entry is loaded and active
118
+ android-watcher schedule remove # uninstall the scheduled job
119
+ android-watcher doctor # health checks: prefixes, AI reachable, schedule active
120
+ android-watcher catalog # list and inspect the shipped source catalog
121
+ android-watcher --help # full flag reference
122
+ ```
123
+
124
+ Bare `android-watcher` opens the Textual setup wizard. It walks you through config
125
+ and writes a native scheduled job. The first pipeline run baselines each source
126
+ silently. It does not send a digest claiming every page is new.
127
+
128
+ ---
129
+
130
+ ## Configuration
131
+
132
+ Config lives at `~/.config/android-watcher/config.toml` (honoring
133
+ `$XDG_CONFIG_HOME`) and is written with `0600` permissions. State (snapshots,
134
+ change history, delivery ledger) lives in a SQLite database under
135
+ `~/.local/share/android-watcher/`. Override the config path with `--config PATH`.
136
+
137
+ The setup wizard writes the file for you. To edit manually:
138
+
139
+ ```toml
140
+ [schedule]
141
+ interval = "daily" # hourly | daily | weekly | cron
142
+ at = "09:00" # local time; ignored when interval = "cron"
143
+ cron = "" # raw cron expression; only used when interval = "cron"
144
+
145
+ [ai]
146
+ mode = "claude_cli" # claude_cli | off
147
+ model = "claude-sonnet-4-6"
148
+
149
+ [digest]
150
+ max_items = 10 # cap on change-groups shown in the message; the rest collapse into the full digest page
151
+ empty = "send" # send | skip (what to do when nothing substantive changed)
152
+
153
+ [sort]
154
+ # Optional per-source or per-category priority overrides (higher = ranked first).
155
+ # "android-developers-blog" = 90
156
+
157
+ [channels.email]
158
+ enabled = true
159
+ smtp_host = "smtp.example.com"
160
+ smtp_port = 465 # TLS required: implicit (465) or STARTTLS
161
+ username = "you@example.com"
162
+ password = "${ANDROID_WATCH_SMTP_PASSWORD}" # env-var ref recommended; see Security
163
+ from = "you@example.com"
164
+ to = "you@example.com"
165
+
166
+ [channels.slack]
167
+ enabled = true
168
+ bot_token = "${ANDROID_WATCH_SLACK_TOKEN}" # env-var ref recommended (bot token is a secret)
169
+ channel = "C0123456789" # channel ID (or #channel-name)
170
+
171
+ [channels.telegram]
172
+ enabled = false
173
+ bot_token = "${ANDROID_WATCH_TELEGRAM_TOKEN}" # env-var ref recommended (bot token is a secret)
174
+ chat_id = "123456789"
175
+
176
+ # Add your own URLs (same shape as catalog entries):
177
+ # [[custom_source]]
178
+ # id = "my-blog"
179
+ # name = "My Blog"
180
+ # category = "dev-blog"
181
+ # detector = "feed"
182
+ # url = "https://example.com"
183
+ # feed_url = "https://example.com/feed.xml"
184
+ # enabled = true
185
+ ```
186
+
187
+ ### Secrets
188
+
189
+ The secrets android-watcher uses are the SMTP password, the Slack bot token, and
190
+ the Telegram bot token. Use **environment-variable references** so those values
191
+ are never written into the config file:
192
+
193
+ ```sh
194
+ export ANDROID_WATCH_SMTP_PASSWORD="hunter2"
195
+ export ANDROID_WATCH_SLACK_TOKEN="xoxb-..."
196
+ export ANDROID_WATCH_TELEGRAM_TOKEN="1234567890:AAF..."
197
+ ```
198
+
199
+ The config stores `${ANDROID_WATCH_SMTP_PASSWORD}` literally; the value is
200
+ resolved at runtime. Inline plaintext works but is discouraged.
201
+
202
+ ---
203
+
204
+ ## The catalog
205
+
206
+ A curated catalog of 41 official Android and Android-developer sources ships
207
+ inside the package. `android-watcher catalog` lists it. The setup wizard lets you
208
+ enable or disable catalog entries and add custom sources.
209
+
210
+ To propose a new official source, edit `src/android_watcher/catalog/catalog.toml`
211
+ and open a pull request (see [CONTRIBUTING.md](CONTRIBUTING.md)).
212
+
213
+ ---
214
+
215
+ ## Scheduling
216
+
217
+ After the wizard completes, install the native scheduled job:
218
+
219
+ ```sh
220
+ android-watcher schedule install
221
+ ```
222
+
223
+ On macOS this writes a launchd plist to `~/Library/LaunchAgents/` and loads it
224
+ via `launchctl`. On Linux with systemd it writes a user timer and enables it;
225
+ run `loginctl enable-linger $USER` once so the timer fires after logout. On
226
+ Linux without systemd it writes a marked crontab block.
227
+
228
+ Confirm the job is active:
229
+
230
+ ```sh
231
+ android-watcher schedule status
232
+ ```
233
+
234
+ If the machine was asleep during a scheduled cycle, android-watcher detects the
235
+ missed run on the next wake and catches up automatically.
236
+
237
+ ---
238
+
239
+ ## Security and privacy
240
+
241
+ **Secrets.** The secrets are the SMTP password, the Slack bot token, and the
242
+ Telegram bot token. The config file is written `0600`. Prefer environment-variable
243
+ references (`password = "${ANDROID_WATCH_SMTP_PASSWORD}"`) so plaintext values
244
+ are never written to disk.
245
+
246
+ **Keep config out of git.** If you keep your config under version control, never
247
+ commit a file with inline secrets. Add to `.gitignore`:
248
+
249
+ ```gitignore
250
+ # android-watcher
251
+ config.toml
252
+ *.android-watcher.toml
253
+ ```
254
+
255
+ The TUI and `--config` warn when the config path is inside a git work tree,
256
+ because an accidental commit would expose your SMTP password, Slack bot token, or
257
+ Telegram bot token.
258
+
259
+ **AI data egress.** When `[ai] mode = "claude_cli"`, the **content of changed
260
+ pages is sent to the `claude` CLI** for triage and description. For the shipped
261
+ catalog this is public Google documentation. If you add **custom sources** (such
262
+ as an internal wiki), that page content is also sent to `claude`. Set
263
+ `[ai] mode = "off"` for the no-egress path: no triage, no descriptions, no
264
+ page content leaves your machine.
265
+
266
+ **SMTP transport.** SMTP enforces TLS (implicit on port 465 or mandatory
267
+ STARTTLS) with certificate verification. It fails closed rather than downgrading
268
+ to plaintext.
269
+
270
+ **Slack and Telegram.** The Slack bot token and Telegram bot token are treated
271
+ as bearer secrets and are never logged.
272
+
273
+ ---
274
+
275
+ ## Commands
276
+
277
+ | Command | What it does |
278
+ |------------------------------------|--------------------------------------------------------|
279
+ | `android-watcher` | Open the Textual setup wizard |
280
+ | `android-watcher run [--force]` | One detection-triage-notify pass |
281
+ | `android-watcher test` | Dry run: render digest to stdout, send nothing |
282
+ | `android-watcher doctor` | Health checks: prefixes, AI reachable, schedule active |
283
+ | `android-watcher catalog` | List and inspect the shipped source catalog |
284
+ | `android-watcher schedule install` | Install the native scheduled job |
285
+ | `android-watcher schedule status` | Show whether the job is loaded and active |
286
+ | `android-watcher schedule remove` | Uninstall the scheduled job |
287
+ | `android-watcher --config PATH` | Use a specific config file |
288
+ | `android-watcher --version` | Print version |
289
+
290
+ ---
291
+
292
+ ## Contributing
293
+
294
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Short version: add a source by editing
295
+ `catalog.toml`; add a detector, triager, or channel by implementing the
296
+ protocol, registering the name, and adding a fixture-backed test. TDD, `ruff`,
297
+ and `pytest` must pass.
298
+
299
+ ---
300
+
301
+ ## Contact
302
+
303
+ For questions or support, open an issue on [GitHub](https://github.com/krayong/android-watcher/issues)
304
+ or email **androidwatcher@krayong.com**. Security reports: see [SECURITY.md](SECURITY.md).
305
+
306
+ ---
307
+
308
+ ## License
309
+
310
+ [MIT](LICENSE).
@@ -0,0 +1,44 @@
1
+ android_watcher/__init__.py,sha256=7EbyCBwwpIxwKyVfqzPJSEbHmj4moQbEgJRy86tQTkg,343
2
+ android_watcher/cli.py,sha256=AnpXdZ1ZaVo6HqBmHzfuRFhFH1aZujJFAc0m2GEWh_s,4851
3
+ android_watcher/config.py,sha256=Zj95Zyt3sgsesgYf-B2v9o0qzAS14wJPFtF4Fon36Lw,7417
4
+ android_watcher/doctor.py,sha256=MrxPlSXc7k9OT3KoUepvYF2kio2zA_a5U6wsEce7fj0,3976
5
+ android_watcher/fetch.py,sha256=9LLAGWIKQZ-TdeLp1QnuQeGbvbAnZdMmhqtOPSYJsQ8,5076
6
+ android_watcher/group.py,sha256=hO24nHlTxlaGrnD7OsRVYhZA3PsfxlvwFhVIj-pMEnc,2695
7
+ android_watcher/lock.py,sha256=qgHLHhFMhHMlS6f1Vl1ZwtJEas6A1rOWgS1O72RTiXg,845
8
+ android_watcher/models.py,sha256=CpYqXKHIkMsA1hqoApNtlUuuRnb9G7xmXZuUT1UQD9E,4881
9
+ android_watcher/rank.py,sha256=PQiYmcDoXRg7hWcVZzdFUsUHRER1DXiB187ERC__OBg,2844
10
+ android_watcher/registry.py,sha256=4bLpNy6Zr4BB8gtSigZulSh02aP8G8MzKlM5MkOa05c,1105
11
+ android_watcher/run.py,sha256=Q5ZWoZBq97UNDxDvYcP3NV0nTACXUX6oe4SB8i7j7js,11149
12
+ android_watcher/schedule.py,sha256=s0qtKYZDM7288gucNV0h1XgxVU_mUSYQEOHai-wdJNw,15782
13
+ android_watcher/store.py,sha256=dERDSa_lRuJLveyYEfu4sZ36gNUrFXEeiyeUw382Eug,17232
14
+ android_watcher/catalog/__init__.py,sha256=FNk48zIH-OByNJkFkX3ByOytrcCVPM7R2NkmpZtvxqw,911
15
+ android_watcher/detect/__init__.py,sha256=2vIf9slnYHzhURSzjMRgYG9BnRm-W3T_oav4cB9b3YU,68
16
+ android_watcher/detect/_normalize.py,sha256=C2WsXXiYCws6Qe9OSG6wI6WoKy0cDjwAF07K5CGN29Y,6034
17
+ android_watcher/detect/android_sitemap.py,sha256=P75azyaVVFfFXFnsEBjCbykNQpcZdlM1LiuWeUu2tto,17941
18
+ android_watcher/detect/base.py,sha256=VR045hE8XwE7HRTQMkrXswgjvNi3nrfFoetfT28u6f4,331
19
+ android_watcher/detect/content.py,sha256=f_2MSINprpg4tPm4Po4w17aF4HNrRp-gKTVMu-YchUQ,2540
20
+ android_watcher/detect/feed.py,sha256=BDwh11nlpjxWmFoT2zWlDQo8lfHPHQJpzfwOsxn6PHo,3816
21
+ android_watcher/detect/sitemap.py,sha256=Ip0hrLfCf-Nmw1G0AV6Jg2xNOfmT_FFsGVZYKP_ttCg,6260
22
+ android_watcher/notify/__init__.py,sha256=u2CarGIQ-cnO-eb3EnArGWOO938a4Z0f_aAOHd6abn4,51
23
+ android_watcher/notify/base.py,sha256=uQkNQ2YxJBosFDkBWaaJGJvkuWbhhHtLdC5j0-pHqpE,575
24
+ android_watcher/notify/email.py,sha256=faDVRShLrFLpUrYekRjMhR93m041tVu2c-FCZ0xrqDU,1598
25
+ android_watcher/notify/html.py,sha256=Y9q_FcYWrOuSCvnMTFhKilzsH0v0wK7vwskFsKjOxCQ,6275
26
+ android_watcher/notify/render.py,sha256=FmxaFPMiSdhv7lgigtS1RqLTjlCVAhuS1Cyek1YSxDE,7585
27
+ android_watcher/notify/slack.py,sha256=KPDmyNz_8RN5vIJ-q4FQqd0buu3G1lbWbzUNd_Gt1rs,4579
28
+ android_watcher/notify/telegram.py,sha256=wFnA5-_J3Vj-6ZYiMJW588CXB3ea_iIJgkit6Q9n2WM,1477
29
+ android_watcher/seed/__init__.py,sha256=huw7-hRmTpaABjQ47v5y0AaiC-s5rMRnojSbzT3IolQ,1478
30
+ android_watcher/seed/seed.sql.gz,sha256=Xb8Xfq6StzFfiyQp2JmsB6BDXaxNR1Tyw0iZdZqXbxE,1359156
31
+ android_watcher/triage/__init__.py,sha256=2gW7caI9axsnc1HL52bojYY3ns2T-Bb0svUL2gn5bbI,45
32
+ android_watcher/triage/base.py,sha256=JqoopBh-M3uMvJ41TGRu9fpOEGNEh5XpVk_-sgEDm7c,613
33
+ android_watcher/triage/claude_cli.py,sha256=aRUkUrDM6O5CMJIHE6QnReqz4AzlnRnr5vB9-LixlhE,6920
34
+ android_watcher/triage/noop.py,sha256=57RliCDLxg_TxnN3inbpOeCl-4_3apffgpfOmSNBmLs,920
35
+ android_watcher/tui/__init__.py,sha256=Ii18IyVP_b-8_kPr3DB5HxdF2D_FCiPdJm3o6S_7AWg,60
36
+ android_watcher/tui/app.py,sha256=pCgnm2A1CDLxIhyKycCxomNU6ZapFVBCpQqlbZ1LTAg,5253
37
+ android_watcher/tui/configio.py,sha256=gpK1nB8SIHEQJwJnzma4zaLuR_mG6XNFNpbF93heZwU,6915
38
+ android_watcher/tui/screens.py,sha256=bjQQz2Q0JyJQbYIWAC0Svy1HShkypCaVRW3uz1fxim8,26741
39
+ android_watcher/catalog/catalog.toml,sha256=y3BYlTWw6Y-bAg9pSz1FP2K2EB-9sqRDCWaIUpBVKYc,13653
40
+ android_watcher-1.0.0.dist-info/METADATA,sha256=sx-SqGVsmV7JZem6UtP6rdp8Eub3yLl8GT2PtesN7pA,11723
41
+ android_watcher-1.0.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
42
+ android_watcher-1.0.0.dist-info/entry_points.txt,sha256=7romTFQ-SEcKyhBa1C-59p775lRL_j-N5up5p43DcbE,61
43
+ android_watcher-1.0.0.dist-info/licenses/LICENSE,sha256=zfqiug1DwqwO-97fRFMtZi6SKtEwPs0FqyKX6zVPBXg,1113
44
+ android_watcher-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ android-watcher = android_watcher.cli:main
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Karan Gourisaria (https://github.com/krayong/)
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
13
+ all 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
21
+ THE SOFTWARE.