quickstarted 0.2.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.
- quickstarted/__init__.py +24 -0
- quickstarted/_version.py +7 -0
- quickstarted/agents/__init__.py +4 -0
- quickstarted/agents/base.py +145 -0
- quickstarted/agents/claude.py +242 -0
- quickstarted/agents/gemini_agent.py +138 -0
- quickstarted/agents/openai_agent.py +167 -0
- quickstarted/agents/prompt.py +61 -0
- quickstarted/agents/registry.py +31 -0
- quickstarted/agents/replay.py +39 -0
- quickstarted/cli.py +250 -0
- quickstarted/docs.py +263 -0
- quickstarted/exec/__init__.py +89 -0
- quickstarted/exec/base.py +131 -0
- quickstarted/exec/docker.py +216 -0
- quickstarted/exec/local.py +16 -0
- quickstarted/exec/seatbelt.py +83 -0
- quickstarted/journey.py +182 -0
- quickstarted/net/__init__.py +5 -0
- quickstarted/net/proxy.py +274 -0
- quickstarted/net/proxy_main.py +53 -0
- quickstarted/pricing.py +93 -0
- quickstarted/report.py +206 -0
- quickstarted/results.py +166 -0
- quickstarted/run.py +240 -0
- quickstarted/sandbox.py +15 -0
- quickstarted/suite.py +203 -0
- quickstarted/trace.py +53 -0
- quickstarted/transport.py +88 -0
- quickstarted-0.2.0.dist-info/METADATA +256 -0
- quickstarted-0.2.0.dist-info/RECORD +34 -0
- quickstarted-0.2.0.dist-info/WHEEL +4 -0
- quickstarted-0.2.0.dist-info/entry_points.txt +3 -0
- quickstarted-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quickstarted
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Test whether an AI agent can actually complete your quickstart by following your docs
|
|
5
|
+
Project-URL: Homepage, https://github.com/snehankekre/quickstarted
|
|
6
|
+
Project-URL: Issues, https://github.com/snehankekre/quickstarted/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/snehankekre/quickstarted/blob/main/CHANGELOG.md
|
|
8
|
+
Author-email: snehan <snehan.minerva@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent-experience,agents,ci,devrel,docs,documentation,testing
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Provides-Extra: all-agents
|
|
25
|
+
Requires-Dist: anthropic>=0.40; extra == 'all-agents'
|
|
26
|
+
Requires-Dist: google-genai>=1.0; extra == 'all-agents'
|
|
27
|
+
Requires-Dist: openai>=1.40; extra == 'all-agents'
|
|
28
|
+
Provides-Extra: claude
|
|
29
|
+
Requires-Dist: anthropic>=0.40; extra == 'claude'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: anthropic>=0.40; extra == 'dev'
|
|
32
|
+
Requires-Dist: google-genai>=1.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
34
|
+
Requires-Dist: openai>=1.40; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
38
|
+
Provides-Extra: docs
|
|
39
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
40
|
+
Provides-Extra: gemini
|
|
41
|
+
Requires-Dist: google-genai>=1.0; extra == 'gemini'
|
|
42
|
+
Provides-Extra: openai
|
|
43
|
+
Requires-Dist: openai>=1.40; extra == 'openai'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# quickstarted
|
|
47
|
+
|
|
48
|
+
Test whether an AI agent can actually complete your quickstart by following
|
|
49
|
+
your docs.
|
|
50
|
+
|
|
51
|
+
[](https://github.com/snehankekre/quickstarted/actions/workflows/ci.yml)
|
|
52
|
+
[](https://pypi.org/project/quickstarted/)
|
|
53
|
+
[](https://pypi.org/project/quickstarted/)
|
|
54
|
+
[](LICENSE)
|
|
55
|
+
|
|
56
|
+
Roughly half the traffic to documentation sites is now AI agents and
|
|
57
|
+
AI-assisted workflows ([Mintlify's measurement](https://www.mintlify.com/blog/ai-traffic)).
|
|
58
|
+
When an agent reads your quickstart and fails, you lose the developer behind
|
|
59
|
+
it and you never find out. quickstarted turns that failure into a CI signal: a
|
|
60
|
+
sandboxed agent gets your goal, your docs, and nothing else, and a script you
|
|
61
|
+
wrote decides whether it succeeded.
|
|
62
|
+
|
|
63
|
+
Full documentation: **https://snehankekre.com/quickstarted/**
|
|
64
|
+
|
|
65
|
+
**Status: early. The interface will change.**
|
|
66
|
+
|
|
67
|
+
## How it works
|
|
68
|
+
|
|
69
|
+
You write a journey, in YAML:
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
name: pnf-quickstart
|
|
73
|
+
goal: >
|
|
74
|
+
Install the pnf package from PyPI into a virtualenv and write a runnable
|
|
75
|
+
snippet that imports pnf and identifies the decode function.
|
|
76
|
+
docs:
|
|
77
|
+
entrypoint: https://raw.githubusercontent.com/snehankekre/pnf/main/README.md
|
|
78
|
+
allow:
|
|
79
|
+
- raw.githubusercontent.com
|
|
80
|
+
setup:
|
|
81
|
+
- python3 -m venv .venv
|
|
82
|
+
success:
|
|
83
|
+
script: |
|
|
84
|
+
set -e
|
|
85
|
+
.venv/bin/python -c "import pnf; assert callable(pnf.decode)"
|
|
86
|
+
.venv/bin/python demo.py
|
|
87
|
+
replay:
|
|
88
|
+
- .venv/bin/pip install pnf
|
|
89
|
+
- printf 'import pnf\nprint(pnf.decode.__name__)\n' > demo.py
|
|
90
|
+
- .venv/bin/python demo.py
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The harness runs an agent in a throwaway workspace with two tools: a sandboxed
|
|
94
|
+
`bash`, and `read_docs`, which is the only way it can read documentation.
|
|
95
|
+
|
|
96
|
+
The harness enforces that. All shell traffic leaves through a proxy it owns,
|
|
97
|
+
and documentation hosts are unreachable from the shell, so an agent that tries
|
|
98
|
+
`curl https://docs.example.com/...` is refused and the attempt is recorded.
|
|
99
|
+
The pages listed in a report are therefore the pages the agent really read.
|
|
100
|
+
When a run fails, the last page it read is a fact you can act on.
|
|
101
|
+
|
|
102
|
+
When the agent stops, the harness runs your `success.script` in the workspace.
|
|
103
|
+
Exit code 0 is a pass. Nothing else is. The agent's own claim of success is
|
|
104
|
+
recorded and never trusted; there is no LLM judge anywhere in scoring.
|
|
105
|
+
|
|
106
|
+
## Two modes
|
|
107
|
+
|
|
108
|
+
**Replay mode** (free, no LLM, no API key) runs your `replay` commands, the
|
|
109
|
+
literal commands your docs tell users to type, and stops at the first failure.
|
|
110
|
+
Treat it as a precondition. If the documented commands break, no reader stands
|
|
111
|
+
a chance, and agent mode only adds noise.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
quickstarted run journeys/pnf-quickstart.yaml --agent replay
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Agent mode** has an LLM follow your docs to the goal. This catches what
|
|
118
|
+
replay cannot: ambiguity, missing steps, wrong ordering, docs that assume
|
|
119
|
+
context a newcomer does not have.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
pip install "quickstarted[claude]"
|
|
123
|
+
export QUICKSTARTED_ANTHROPIC_API_KEY=...
|
|
124
|
+
quickstarted run journeys/pnf-quickstart.yaml --agent claude
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Keys are read from `QUICKSTARTED_*` names first so they can sit in your shell
|
|
128
|
+
without other tooling on the same machine picking them up and spending them.
|
|
129
|
+
The vendor-standard names still work as a fallback, which is what CI sets.
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
[PASS] pnf-quickstart (claude:claude-opus-5)
|
|
133
|
+
classification: passed
|
|
134
|
+
turns: 12, duration: 70.7s
|
|
135
|
+
backend: seatbelt
|
|
136
|
+
tokens: 24 in / 4616 out, cache 16812 written / 104148 read
|
|
137
|
+
docs pages read: 2
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Pass rates
|
|
141
|
+
|
|
142
|
+
The same docs and the same model can pass at 10:00 and fail at 10:05. One run
|
|
143
|
+
is a sample. Use `--repeat` and read the rate:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
quickstarted run journeys/*.yaml --agent claude --repeat 5 --workers 3
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Every run is classified, and only two classifications say anything about your
|
|
150
|
+
documentation:
|
|
151
|
+
|
|
152
|
+
| Classification | Meaning | Counts toward pass rate |
|
|
153
|
+
| --- | --- | --- |
|
|
154
|
+
| `passed` | success script exited 0 | yes |
|
|
155
|
+
| `docs_gap` | the agent finished, the check failed | yes |
|
|
156
|
+
| `budget_exhausted` | out of turns, time, or tokens | no |
|
|
157
|
+
| `infra_error` | rate limit, upstream 5xx, network failure | no |
|
|
158
|
+
| `harness_error` | misconfigured journey, our bug | no |
|
|
159
|
+
| `agent_refusal` | model declined | no |
|
|
160
|
+
|
|
161
|
+
Runs that produced no evidence are excluded from the numerator *and* the
|
|
162
|
+
denominator, and reported separately. When nothing produced evidence the suite
|
|
163
|
+
says so. Reporting 0% there would be a different claim, and a false one.
|
|
164
|
+
|
|
165
|
+
## Does llms.txt actually help?
|
|
166
|
+
|
|
167
|
+
Whether a project ships `llms.txt` is a checklist item anyone can `curl`, and
|
|
168
|
+
scoring it would be the proxy metric this tool exists to replace. So
|
|
169
|
+
quickstarted never scores affordances. It measures them:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
quickstarted run journeys/streamlit-quickstart.yaml --agent claude --repeat 10
|
|
173
|
+
quickstarted run journeys/streamlit-quickstart.yaml --agent claude --repeat 10 --affordances none
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Same prompt, same journey, same model. The only difference is whether the
|
|
177
|
+
agent could read `llms.txt` and `.md` variants. The difference in pass rate is
|
|
178
|
+
a measurement of the affordance itself.
|
|
179
|
+
|
|
180
|
+
`--probe-affordances` records what exists without changing anything, including
|
|
181
|
+
the size. Presence and usefulness come apart quickly: Streamlit's `llms.txt` is
|
|
182
|
+
67 KB, while its `llms-full.txt` is 1.8 MB, which is more than most agents can
|
|
183
|
+
read in one go.
|
|
184
|
+
|
|
185
|
+
## Sandboxing
|
|
186
|
+
|
|
187
|
+
Agent-authored commands from somebody else's quickstart are untrusted code.
|
|
188
|
+
|
|
189
|
+
| Backend | Filesystem | Network |
|
|
190
|
+
| --- | --- | --- |
|
|
191
|
+
| `docker` | container | internal network; the proxy sidecar is the only route out |
|
|
192
|
+
| `seatbelt` (macOS) | home directory unreadable, writes confined to the workspace | all egress denied except the harness proxy |
|
|
193
|
+
| `local` | none | none; proxy variables are advisory |
|
|
194
|
+
|
|
195
|
+
`auto` picks the best available. `quickstarted run` refuses `local` unless you
|
|
196
|
+
pass `--allow-unenforced`. Run `quickstarted doctor` to see what your machine
|
|
197
|
+
can enforce. Details in [SECURITY.md](SECURITY.md).
|
|
198
|
+
|
|
199
|
+
## Install
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
pip install quickstarted # replay mode only
|
|
203
|
+
pip install "quickstarted[claude]" # + Claude
|
|
204
|
+
pip install "quickstarted[all-agents]" # + OpenAI and Gemini
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Python 3.9+. One runtime dependency: PyYAML. Every adapter is a plain tool-use
|
|
208
|
+
loop against the same two harness-owned tools and the same shared prompt, so
|
|
209
|
+
cross-model numbers compare like with like.
|
|
210
|
+
|
|
211
|
+
## CI
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
- uses: actions/checkout@v4
|
|
215
|
+
- uses: actions/setup-python@v5
|
|
216
|
+
with:
|
|
217
|
+
python-version: "3.12"
|
|
218
|
+
- run: pip install quickstarted
|
|
219
|
+
- run: quickstarted run journeys/*.yaml --agent replay --out results --junit junit.xml
|
|
220
|
+
- uses: actions/upload-artifact@v4
|
|
221
|
+
if: always()
|
|
222
|
+
with:
|
|
223
|
+
name: quickstarted-results
|
|
224
|
+
path: results/
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`quickstarted run` exits 1 when any journey fails, so the job gates merges.
|
|
228
|
+
`results.json` is versioned (schema 1.0). JUnit XML reports a docs gap as a
|
|
229
|
+
failure and infrastructure trouble as an error, so a rate limit does not read
|
|
230
|
+
as a broken quickstart. Run agent mode on a schedule, or when a model ships.
|
|
231
|
+
Every push is too often, because agent runs cost real tokens.
|
|
232
|
+
|
|
233
|
+
## Fetching other people's docs
|
|
234
|
+
|
|
235
|
+
Benchmarking means requesting pages from organisations who did not ask to be
|
|
236
|
+
measured. quickstarted sends a truthful User-Agent, honours `robots.txt`, and
|
|
237
|
+
waits a second between requests to the same host. `--cache-dir` makes reruns
|
|
238
|
+
read the same bytes. `--refresh` flags pages whose content changed since the
|
|
239
|
+
last run, which is a finding in its own right.
|
|
240
|
+
|
|
241
|
+
## What this does and does not tell you
|
|
242
|
+
|
|
243
|
+
It tells you whether a given model, on a given day, could get from your docs to
|
|
244
|
+
a working result, and where it was reading when it could not. That is a lower
|
|
245
|
+
bound on documentation quality. Nothing here measures whether your docs are
|
|
246
|
+
pleasant, complete, or accurate beyond the journey you wrote, and there is no
|
|
247
|
+
UI testing at all: if your product's button moved, this will not notice.
|
|
248
|
+
[Doc Detective](https://github.com/doc-detective/doc-detective) is the tool for
|
|
249
|
+
that, and the two answer different questions.
|
|
250
|
+
|
|
251
|
+
Write success scripts that assert local, deterministic facts. A pass means the
|
|
252
|
+
floor holds, and says nothing about the ceiling.
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
MIT.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
quickstarted/__init__.py,sha256=zE7VMSKaKjIjIgpq5MHmMUXZE4doWiGjd9a3zHsTaSc,683
|
|
2
|
+
quickstarted/_version.py,sha256=ZpoPRLhDyc9ZHTe7Y2O3firGVKq_NToRzOP6ipjnJic,252
|
|
3
|
+
quickstarted/cli.py,sha256=qf_O9U69WhF0rLaNQTY1kVU-qx5xLlFWjLeNg9TCKKg,9285
|
|
4
|
+
quickstarted/docs.py,sha256=0DvjgNm1c05WBXFnR7qUZ5zsJjLkvTvC5nrPWRTCGs0,9617
|
|
5
|
+
quickstarted/journey.py,sha256=SJWg0ot_G90bWzfA-a5c-23bO1Wt9GEnFQaX4SVIe60,6648
|
|
6
|
+
quickstarted/pricing.py,sha256=15lJtO89LfrXcBv76ys9_U4zR5aDATQQeCh_iYiz_sE,2866
|
|
7
|
+
quickstarted/report.py,sha256=2btaNRoetILElaNGQsZelFP-VrJrU_dNRfsXyiM3sEY,7710
|
|
8
|
+
quickstarted/results.py,sha256=_TOmf5_GLTosG3CLFxyfAGhfcnC4_hD91unOdMC8moQ,5914
|
|
9
|
+
quickstarted/run.py,sha256=8lTNYj0rFSgBeuTQalGI2_Y2XM7PcUlDVanrYtPw47k,7656
|
|
10
|
+
quickstarted/sandbox.py,sha256=8ahNP_DFZaZnmHQzSM2w8WJDYiAqeDOYcrqXrAjwlpA,476
|
|
11
|
+
quickstarted/suite.py,sha256=SFGUivQx1xfo2xHY7zz7zdBBM8CFNfb-IQcMpTPEufg,6727
|
|
12
|
+
quickstarted/trace.py,sha256=pGwQCxS8-3rcWgT6MJcHS84itWAlV9fkmUC28Smg_R8,1627
|
|
13
|
+
quickstarted/transport.py,sha256=ZqLV9lK3NWk11T5NaKlHrDsrrXL6LyRHhRwV3q_W3LM,2617
|
|
14
|
+
quickstarted/agents/__init__.py,sha256=awU5gb2_55DaS4tF4N8hCgZz3tWIfHGFEY1LCCzzPUA,144
|
|
15
|
+
quickstarted/agents/base.py,sha256=MnQWFFc3O28KCaaQsJJBaWguVwoZYQ7B1Sk3dvtPpro,5163
|
|
16
|
+
quickstarted/agents/claude.py,sha256=rf3KH1YdQaV_FZzoTkMb0_gvNMCxhACIQM1W3hRocL0,9526
|
|
17
|
+
quickstarted/agents/gemini_agent.py,sha256=RF8pC_PtoS-wafBmtB1Eki5gSxnOQdnocHMKmmtw0Cg,5076
|
|
18
|
+
quickstarted/agents/openai_agent.py,sha256=Ut3FBLsngx1tv2mHgWCVqZXJR62pA0Ccp9Pw5ty_iK8,6365
|
|
19
|
+
quickstarted/agents/prompt.py,sha256=wPjGjzHv-8an618TPkZI9MqG5RMymGipqqJDM6gxoYM,2716
|
|
20
|
+
quickstarted/agents/registry.py,sha256=-YR4e-pWvAQ5DWX8mgih36unDDMRZ-saRDN7BOa3Gko,981
|
|
21
|
+
quickstarted/agents/replay.py,sha256=bgj9uUDpJfh8Zp8F7e9O8yrkfgcejYm8AcUOoI1PNug,1521
|
|
22
|
+
quickstarted/exec/__init__.py,sha256=XAkOtDpk6KZ1hXp3ZLu6SSUXDzbXhlmNFo0CJ2lkdSU,2381
|
|
23
|
+
quickstarted/exec/base.py,sha256=dMUKsHNGM218SNOT4sn5tlUcRdm4thtpuMIsA-sub-8,4289
|
|
24
|
+
quickstarted/exec/docker.py,sha256=K5ALzTLQipo5nB6zBcrea2VFc_IvxSIeSj4U8VVxNqs,7985
|
|
25
|
+
quickstarted/exec/local.py,sha256=cek0HwhbDejSQIelNry92FmR8q1UVHoOL1T7FQHG5sQ,501
|
|
26
|
+
quickstarted/exec/seatbelt.py,sha256=w_Nts9GHu9_onmSQWdBBbTcIdNAvR91gg7ilHWUde_U,3351
|
|
27
|
+
quickstarted/net/__init__.py,sha256=mXh5SeQhmnxJTyjKKsQ9gLduv2z2rP40qBXL8Ruz3s8,191
|
|
28
|
+
quickstarted/net/proxy.py,sha256=3PGsnY1yx-QUdC75q3inxpn7-ZgocyiPA83Ef1dqqkA,10121
|
|
29
|
+
quickstarted/net/proxy_main.py,sha256=x2KoLZwkMLc1b9NhrFaepBeg9v3pqQ7rzmQJV3xJQPM,1563
|
|
30
|
+
quickstarted-0.2.0.dist-info/METADATA,sha256=dTzXpwUQrmPmO1FyV69zJ-3JxecIZHEVmuv9zH3Ogx8,10220
|
|
31
|
+
quickstarted-0.2.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
32
|
+
quickstarted-0.2.0.dist-info/entry_points.txt,sha256=SqkqLK38ifHlJz5aw6kDioF6IGWz-qXwiyCAZgX-LPU,86
|
|
33
|
+
quickstarted-0.2.0.dist-info/licenses/LICENSE,sha256=_f2myDwEsLzmhmXj7WgwvwPYAta6LE3HxDVa31Mq_18,1069
|
|
34
|
+
quickstarted-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Snehan Kekre
|
|
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.
|