opencode-reviewer 0.0.0-stage → 0.4.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dionys Kaloulis
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.
package/README.md CHANGED
@@ -1,3 +1,224 @@
1
- # Temporary Holding Version
1
+ # Opencode request reviewer
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Review pending OpenCode permissions in the sidebar, with explanations, safety ratings, and optional one-time auto-approval.
4
+
5
+ Reviews native shell execution and edit/write/apply-patch changes by default. Independent opt-in switches add **MCP operations**, **custom-tool permission checks**, and **external-directory access from any linked tool**. Targets **OpenCode 1.18.35, local Linux TUI**, with local-fixture verification; other versions and clients are unverified.
6
+
7
+ ## Install
8
+
9
+ Add the [npm package](https://www.npmjs.com/package/opencode-reviewer) to `~/.config/opencode/tui.json` or `.opencode/tui.json`, preserving existing entries. OpenCode downloads the prebuilt package and loads its TUI entry automatically. No archive extraction, separate `npm install`, or absolute plugin file path is needed.
10
+
11
+ Minimal configuration:
12
+
13
+ ```json
14
+ {
15
+ "$schema": "https://opencode.ai/tui.json",
16
+ "plugin": [
17
+ ["opencode-reviewer@0.4.1", {
18
+ "baseURL": "https://api.example.com/v1",
19
+ "model": "your-model",
20
+ "apiKeyEnv": "OPENCODE_REVIEWER_API_KEY"
21
+ }]
22
+ ]
23
+ }
24
+ ```
25
+
26
+ Set `OPENCODE_REVIEWER_API_KEY` in your environment before launching OpenCode. Use your endpoint's model identifier. For OpenRouter, set `baseURL` to `https://openrouter.ai/api/v1`.
27
+
28
+ The following expanded example shows **every configuration option**, with all five review categories explicitly enabled. Replace endpoint/model/authentication values and the optional custom prompt-directory path; omit options you do not use:
29
+
30
+ ```json
31
+ {
32
+ "$schema": "https://opencode.ai/tui.json",
33
+ "plugin": [
34
+ [
35
+ "opencode-reviewer@0.4.1",
36
+ {
37
+ "baseURL": "https://api.example.com/v1",
38
+ "model": "your-model",
39
+ "apiKey": "your-api-key",
40
+ "apiKeyEnv": "OPENCODE_REVIEWER_API_KEY",
41
+ "instructions": "/absolute/path/to/reviewer-prompts",
42
+ "stream": false,
43
+ "reviewBash": true,
44
+ "reviewEdits": true,
45
+ "reviewMcp": true,
46
+ "reviewCustomTools": true,
47
+ "reviewExternalDirectories": true,
48
+ "autoApprove": false,
49
+ "autoApproveDelaySeconds": 15,
50
+ "formatRetries": 1,
51
+ "timeoutMs": 30000,
52
+ "maxFiles": 6,
53
+ "maxEvidenceBytes": 131072
54
+ }
55
+ ]
56
+ ]
57
+ }
58
+ ```
59
+
60
+ This example explicitly enables all five review categories; the three new categories default to **off** when omitted. Use a local or hosted **OpenAI-compatible Chat Completions endpoint**. The plugin appends `/chat/completions`. `apiKey` takes precedence over `apiKeyEnv`; remove `apiKey` to use the environment variable, and set it before launch. Remove both for an unauthenticated endpoint. Remove `instructions` to use built-in prompts, or point it at an existing custom prompt directory.
61
+
62
+ **Restart OpenCode.** The plugin reviews requests that OpenCode asks about; configure permission rules in `opencode.json`, for example `"permission": { "bash": "ask", "edit": "ask" }`.
63
+
64
+ ### Updates and migration to npm
65
+
66
+ To update, replace `opencode-reviewer@0.4.1` with the desired published version and restart OpenCode. Exact versions keep upgrades intentional; see [npm versions](https://www.npmjs.com/package/opencode-reviewer?activeTab=versions) and [release notes](https://github.com/mightykatun/opencode-reviewer/releases).
67
+
68
+ If you previously registered an extracted bundle, a `file://` URL, or a manually installed `dist/tui.js`, replace that plugin entry with the npm package specifier and preserve its options. Do not keep both entries. Usage history and saved conversation modes remain under OpenCode's state directory, independent of how the plugin is installed. Published npm packages begin with v0.4.0; older releases were distributed as GitHub assets.
69
+
70
+ **Upgrading from `opencode-command-reviewer`:** replace the old plugin entry rather than adding a second one. The new plugin ID is `opencode-reviewer`; reapply any saved enable/disable preference if needed. Use documentation matching your installed release.
71
+
72
+ **Migrating from v0.2.x:** set `reviewExternalDirectories: true` to retain shell-associated directory reviews and enable the additional directory origins. `reviewBash` now controls shell execution only. MCP and custom-tool reviews require their own switches. Remove any `*-REVIEW-CORRECTION.md` files from your custom prompt directory: correction is now a fixed contract, and legacy overrides produce an explicit configuration error. Restart OpenCode after updating the bundle and configuration.
73
+
74
+ ## Configuration
75
+
76
+ Put options beside the npm package specifier in `tui.json`. Only **`baseURL` and `model`** are required. Omitted options use these defaults:
77
+
78
+ | Option | Default | Meaning / accepted values |
79
+ | --- | --- | --- |
80
+ | `baseURL` | required | HTTP(S) API base URL, without embedded credentials, query, or fragment. Trailing slashes are removed. |
81
+ | `model` | required | Nonempty model identifier accepted by your endpoint. |
82
+ | `apiKey` | omitted | Nonempty Bearer API key; takes precedence over `apiKeyEnv`. |
83
+ | `apiKeyEnv` | omitted | Environment-variable name, e.g. `OPENCODE_REVIEWER_API_KEY`. Letters, digits, underscores; cannot start with a digit. Its value must be nonempty unless `apiKey` is supplied. |
84
+ | `instructions` | built-in prompts | Absolute custom prompt-directory path; no relative paths, `~/`, or inline instructions. See [Custom prompts](#custom-prompts). |
85
+ | `stream` | `false` | Enable SSE Chat Completions transport and progressive rating/explanation display together. `false` waits for the complete JSON response. |
86
+ | `reviewBash` | `true` | Review native shell execution. Directory access has its own switch. |
87
+ | `reviewEdits` | `true` | Review native edit, write, and apply-patch requests. |
88
+ | `reviewMcp` | `false` | Review identifiable MCP tool and resource permissions, including MCP resource requests using `read`. |
89
+ | `reviewCustomTools` | `false` | Review permission checks raised by identifiable registered custom/plugin tools. |
90
+ | `reviewExternalDirectories` | `false` | Review directory-access requests from shell, read, search, edit, MCP, or custom tools independently of their operation-review switches. |
91
+ | `autoApprove` | `false` | Allow Safe requests once after a visible countdown. |
92
+ | `autoApproveDelaySeconds` | `15` | Integer **0–3600** seconds. `0` approves once visible and verified. |
93
+ | `formatRetries` | `1` | Integer **0–100** additional attempts to correct invalid assessment JSON; no HTTP/network retries. |
94
+ | `timeoutMs` | `30000` | Integer **1–3,600,000** ms for context, evidence, and review/corrections together; separately bounds startup prompt loading. |
95
+ | `maxFiles` | `6` | Integer **1–1,000** distinct candidate files per review, shared counting rules for shell and edit tools. Repeated paths and resolved symlink aliases count once; unavailable candidates also count. |
96
+ | `maxEvidenceBytes` | `131072` | Integer **1–16,777,216** UTF-8 bytes. Shell: command + captured files. Edit: included diffs. MCP/custom/directory: variable operation JSON, including action arguments, permission scope/metadata, and optional definition text/schema. Common user/session context and generated notices are outside this budget. |
97
+
98
+ Booleans must be `true`/`false`, numbers must be integers, and strings are trimmed and must be nonempty. Unknown keys, explicit `null`, and invalid supplied values are rejected even for disabled features. Omit both API-key options for unauthenticated endpoints.
99
+
100
+ Disable review categories independently; disabling all five leaves all decisions manual. Disabled kinds skip evidence/model work, although identifying an unknown tool's category may require a minimal host lookup. Oversized sources or whole diffs are omitted with reasons; an oversized mandatory command, tool payload, or permission scope fails review. Optional definitions/metadata can be omitted explicitly. New JSON payloads are bounded to 32 nesting levels and 16,384 values. **Restart after configuration changes.**
101
+
102
+ For progressive display, set `"stream": true`. For a temporary conversation-level switch, use `/reviewer-disable` or `/reviewer-enable`; the saved conversation mode is not a configuration option.
103
+
104
+ Shell evidence includes literal Python/shell sources and supported `cat`/`head` file operands. Each permission gets a fresh file/byte budget; repeated edit entries retain their separate diffs. Omitted files carry `[!] File "path" not included in context.`; omitted edits also carry `[Δ] "path": +N −M lines` when a valid diff or host counts provide the numbers. Paths are JSON-quoted, and unknown counts are never invented. No omitted file content is sent.
105
+
106
+ ### Coverage and evidence limits
107
+
108
+ - **MCP:** uses the pending tool's exact recorded arguments and scope. Registry membership and connected-server identity are cross-checked; ambiguous sanitized names or collisions remain manual. OpenCode 1.18.35 does not expose a complete MCP definition catalog through the public SDK, so unavailable descriptions/schemas are reported explicitly. Resource server names and URIs are matched verbatim, including whitespace. Resource URIs are identifiers, not assumed local paths. The reviewer does not call MCP tools or fetch their resource contents.
109
+ - **Custom tools:** uses public registry identity, available definition, arguments, and the exact permission raised. A custom tool may request `bash`, `edit`, or an arbitrary permission name; its tool identity determines the evidence format. Earlier custom-tool code may already have run before its permission check. Tools that never ask permission cannot be reviewed by this plugin.
110
+ - **Directory access:** uses host-declared scope and associated operation context without reading target contents or canonicalizing targets. Native patch summaries retain add/update/delete/move operations, declared paths and move destinations. Edit bodies are omitted and coverage is marked partial; a subsequent edit request supplies its own diffs. Patch-header scanning is bounded to 16 MiB of input and 65,536 scanned lines, with summaries subject to the operation JSON budget. Missing, oversized or incomplete mandatory summaries fail analysis rather than silently dropping operations. Approving directory access can resume an operation immediately when other permissions are already allowed, so another prompt is not guaranteed.
111
+ - **Other native permissions:** standalone native reads, searches, web requests, subagent launches and similar requests remain manual and unreviewed. Experimental code-mode MCP calls whose originating tool is `execute` also remain outside this classification. Unsupported or unidentified requests still block later panels in native order.
112
+
113
+ The plugin sees **pending permission requests**, not every invocation. Existing `allow` rules or remembered grants may bypass the approval stage. The review switches do not change native permission rules.
114
+
115
+ ### Missing files and timeouts
116
+
117
+ Missing/unreadable files normally become evidence omissions, not a failed review. SSH/container commands are not executed to obtain remote sources. Network-mounted local paths can still stall filesystem operations.
118
+
119
+ Optional filesystem enrichment gets at most **5 seconds and one third of the remaining review time**, shared by the request. Path lookups are bounded to **500 ms** and file captures to **1.5 seconds**, within that shared budget. At most two evidence I/O transactions remain outstanding per plugin instance; busy probes are omitted rather than queued indefinitely. A timed-out OS operation may finish later, with its handle still owned for cleanup. These limits cannot force the kernel to cancel blocked I/O.
120
+
121
+ Stalled optional canonicalization does not discard host-provided edit diffs. Unresolved aliases count conservatively as separate files, with a limitation. Optional conversation/project lookups are also bounded and finish before filesystem enrichment starts; missing context remains explicit. The overall `timeoutMs` still covers all preparation, reviewer responses, and format corrections. Timeout messages distinguish the active stage. The reviewer decides whether incomplete evidence supports a rating; the plugin does not manufacture one.
122
+
123
+ ## Using the reviewer
124
+
125
+ The sidebar shows **Safe** (green), **Unsafe** (red), or **Analysis unavailable** (orange), using the active theme. Explanations support Markdown and mouse-wheel/scrollbar scrolling. Only the assessment description is shown, not separate provider reasoning fields.
126
+
127
+ The panel follows the first pending permission in the root session or its direct children. Directory access and command execution may require separate reviews. If the sidebar is hidden, use OpenCode's **Show sidebar** command (default: `Ctrl+X`, then `B`).
128
+
129
+ **Data sent:** the current request, latest genuine root-user prompt, project context, and applicable bounded file snapshots, proposed diffs, tool arguments, permission metadata and available definitions go to your reviewer endpoint **before approval**. Tool arguments can themselves contain private data. MCP connection credentials, host environment/configuration and unrelated messages are not copied into evidence. `cat`/`head` operands supply full bounded snapshots, not command output. Shell file capture can follow symlinks outside the project. HTTP is supported but unencrypted. Ratings reflect the supplied evidence, not a safety guarantee; implementations, dependencies, remote contents and omitted content are not fully inspected.
130
+
131
+ ### Streaming and pending reviews
132
+
133
+ With the default `stream: false`, the scanner shows **Evaluating** while waiting for the complete response, or **Retrying** during a format correction. Set `"stream": true` and restart OpenCode to request SSE streaming and show the rating as soon as it is parsed, followed by incremental Markdown. This changes both transport and display; the endpoint must support streaming Chat Completions. Model and provider selection remain configurable.
134
+
135
+ An early **Safe** or **Unsafe** is provisional. The pending indicator stays visible while the response is still being received or validated, even after a rating or apparently complete explanation appears. A format correction clears the earlier rating and text and shows **Retrying** until new streamed content arrives. Transport failures show **Analysis unavailable** and clear the preview; they are not automatically retried. With animations disabled, a static indicator retains the same labels.
136
+
137
+ Only a completed, validated **Safe** assessment whose final explanation has rendered can start auto-approval. A preview cannot start a countdown or enable the plugin's approval button. Native **Allow once**, **Allow always**, and **Reject** controls remain available throughout, with their existing OpenCode meanings.
138
+
139
+ ### Enable or disable a conversation
140
+
141
+ Use `/reviewer-disable` or `/reviewer-enable`, or open the command palette (`Ctrl+P`) and choose **Reviewer: Disable for conversation** or **Reviewer: Enable for conversation**. These are local controls: they do not send assistant prompts or add conversation messages. They apply to the current root conversation and all its descendants, including when invoked from a descendant. They are unavailable without an active session.
142
+
143
+ Reviewing is enabled by default. Disabling aborts current analysis and countdowns, hides reports, and leaves native permission requests pending for manual action. Enabling freshly reviews pending requests for the configured review categories. A countdown already canceled or interrupted stays manual for that request in the running plugin, including after re-enabling. This cancellation history is not persisted across restarts.
144
+
145
+ The mode is saved for resume within the same host directory scope. Other conversations and the configured review-category switches remain independent. Changes take effect immediately in this instance; other running instances do not live-sync mode changes. These controls do not alter native permission rules or what native approval buttons mean.
146
+
147
+ Mode records live in `opencode-reviewer/session-mode-v1/` under OpenCode's state directory, normally `~/.local/state/opencode/`. Records contain only a version and enabled flag, with filenames keyed by host directory and root-session identity. No evidence or partial explanation is saved there. A success toast confirms **Saved for resume**. If saving fails, the toast says the change applied locally but resume may use the previous setting. If ancestry or saved mode cannot be read, review work stays suspended and native controls remain usable; a failed command reports **Reviewer setting unavailable** rather than claiming success.
148
+
149
+ ### Usage and lifetime history
150
+
151
+ A dimmed footer shows three separate lines when usage and pricing are available:
152
+
153
+ ```text
154
+ tokens in/out: 1000/100
155
+ cost: $0.0045
156
+ lifetime: $0.1234
157
+ ```
158
+
159
+ Report totals include format-correction requests. Token counts and cost are independent: each line appears only when that component is available across the complete review, so cost-only and token-only reports are possible. Missing usage in an attempt can leave the report without stats. These are reviewer-request statistics.
160
+
161
+ - For `baseURL: "https://openrouter.ai/api/v1"`, cost is the endpoint's valid, nonnegative `usage.cost`, including reported zero or cost without token counts. The plugin does not add upstream inference costs or substitute a catalog estimate when reported cost is absent.
162
+ - Other endpoints use estimates from OpenCode's configured model catalog, matched by endpoint and model, including cache and context-tier rates. Unknown pricing omits the cost line. Model choice is not restricted to OpenRouter or any particular model.
163
+
164
+ **Lifetime usage** is saved automatically across projects and restarts. Its inline line appears **only alongside valid current-request usage**, including cost-only or token-only reports. It stays hidden while loading, on review failure, or when no usage component covers the complete review. Open **Reviewer: Lifetime usage** from the command palette for cumulative tokens, request count, and separate token/pricing coverage, even with no review open.
165
+
166
+ Lifetime accounting records valid received usage once per HTTP attempt, including format corrections and invalid assessments. Usage received before later failure or cancellation still counts; repeated cumulative stream frames do not count as extra requests. A request with no received valid tokens or cost adds no entry. These totals cover **received usage only**: unreported charges remain unknown, and the plugin makes no billing follow-up calls. Unknown cost is not treated as free, and partial token/pricing coverage is labeled.
167
+
168
+ New totals live in `opencode-reviewer/usage-v2/` under OpenCode's state directory. Existing `opencode-reviewer/usage-v1/` snapshots are read alongside them without being rewritten or copied. Keep both directories to preserve history. Legacy amounts retain their original estimates; cumulative cost can combine those estimates with newer reported costs and generic catalog estimates. Earlier unrecorded usage cannot be recovered.
169
+
170
+ Concurrent instances save separate atomic snapshots containing only numeric totals and timestamps. Totals refresh on locally recorded usage and when opening the stats command. Persistence failures leave reviews usable and show **lifetime: usage unavailable** alongside valid request stats, or an unavailable message in the palette dialog; stored data is never silently reset. There is no `oc-stats` or native session-accounting integration.
171
+
172
+ ### Auto-approval
173
+
174
+ Enable it with `"autoApprove": true`; optionally change `"autoApproveDelaySeconds": 15`.
175
+
176
+ - Each completed, validated **Safe** assessment gets a full countdown once its final explanation is rendered and visible. Streamed previews never qualify. **Unsafe** and **Analysis unavailable** stay manual. A Safe result with incomplete evidence still qualifies.
177
+ - The fixed footer shows **Allowed in Xs** and **Cancel**. Click the countdown to allow once immediately; Cancel leaves that request manual.
178
+ - After starting, hiding or covering the panel, switching sessions, or narrowing the terminal enough to hide it cancels that request's automation for the running plugin. Returning does not restart it; scrolling does not cancel it.
179
+ - Native controls remain available. **Click Cancel before deliberating in native Allow always or rejection forms**. Opening those forms alone does not cancel the timer. Once **Allowing…** begins, approval cannot be undone.
180
+ - The plugin rechecks the pending request and sends only **Allow once**. OpenCode performs the operation. Failed/uncertain submissions remain manual if still pending; writes are never automatically retried.
181
+
182
+ Auto mode applies to every explicitly enabled review category and adds extra-careful reviewer guidance without announcing automation or adding plugin notices to the generating agent's conversation. Built-in prompts assess **Allow once**, not hypothetical Allow always grants. Bash execution reports end with brief guidance to prefer Allow once because only the current request was reviewed; all other categories omit that closing line.
183
+
184
+ ## Custom prompts
185
+
186
+ Set `instructions` to an absolute directory containing any of these files:
187
+
188
+ | Filename | Purpose |
189
+ | --- | --- |
190
+ | `PERMISSION-REVIEW-PROMPT.md` | Shell assessment |
191
+ | `EDIT-REVIEW-PROMPT.md` | Edit assessment |
192
+ | `MCP-REVIEW-PROMPT.md` | MCP tool/resource assessment |
193
+ | `CUSTOM-TOOL-REVIEW-PROMPT.md` | Custom/plugin-tool permission assessment |
194
+ | `EXTERNAL-DIRECTORY-REVIEW-PROMPT.md` | Directory-access assessment |
195
+ | `EXTRA-CAREFUL-REVIEW-PROMPT.md` | Shared extra guidance, included only in auto mode |
196
+
197
+ Copy templates from this repository's `prompts/` directory for your installed version. Missing files fall back individually to built-ins. Supplied files must be readable, stable, nonempty regular UTF-8 text, without NUL bytes, up to **64 KiB each**. Invalid files fail configuration, including when auto mode is off.
198
+
199
+ The evidence/output and format-correction contracts live in `contracts/` and cannot be overridden. Custom filenames ending in `-CONTRACT.md` or `-REVIEW-CORRECTION.md` are rejected. Remove old correction overrides when upgrading; the fixed correction preserves the selected category's guidance. Output remains exactly `{"safe": boolean, "desc": "nonempty text"}`.
200
+
201
+ **Restart after changing custom prompts.** Editing built-in source prompts also requires rebuilding. Existing overrides replace the named built-ins; update them to adopt new guidance. Older inline `instructions` must be moved into the appropriate prompt file.
202
+
203
+ ## Development
204
+
205
+ From the repository root, with **Node.js 22+** and npm:
206
+
207
+ ```sh
208
+ npm ci --ignore-scripts
209
+ npm run check # Typecheck, tests, build
210
+ npm run test:runtime # Linux; Git, Python 3, tmux, OpenCode on PATH
211
+ npm run test:runtime-cleanup # Fast tmux interruption/parallel-isolation checks; no model
212
+ npm run test:runtime-permissions # Focused MCP/custom/directory and stats fixtures
213
+ npm run check:package # Reproducible build and exact package contents
214
+ ```
215
+
216
+ Use `OPENCODE_BIN` to select a test binary. Build output is `dist/tui.js`; `npm pack` creates `opencode-reviewer-VERSION.tgz`. See [AGENTS.md](https://github.com/mightykatun/opencode-reviewer/blob/main/AGENTS.md) for maintainer and release details.
217
+
218
+ ### Releasing
219
+
220
+ Update both manifests with `npm version X.Y.Z --no-git-tag-version`, commit the release changes, then create and push the matching `vX.Y.Z` tag. A `v*` tag push runs verification and packaging, automatically creates a GitHub Release with generated notes and the archive, and publishes that same archive to npm. No separate GitHub Release creation step is required.
221
+
222
+ Stable versions publish to npm's `latest` tag. Versions such as `X.Y.Z-beta.1` create GitHub prereleases and publish to npm's `next` tag. Package and lockfile versions must match the Git tag; published npm versions are immutable.
223
+
224
+ For an existing tag containing this workflow, use **Actions → Release package → Run workflow**, select `main`, and supply the tag. Equivalent CLI command: `gh workflow run release.yml --ref main -f tag=vX.Y.Z`. Reruns reuse an existing release, and skip npm publication only when the existing package has identical archive integrity.
@@ -0,0 +1,28 @@
1
+ # Bundled dependency: shell-quote 1.12.0
2
+
3
+ https://github.com/ljharb/shell-quote
4
+
5
+ The MIT License
6
+
7
+ Copyright (c) 2013 James Halliday (mail@substack.net)
8
+
9
+ Permission is hereby granted, free of charge,
10
+ to any person obtaining a copy of this software and
11
+ associated documentation files (the "Software"), to
12
+ deal in the Software without restriction, including
13
+ without limitation the rights to use, copy, modify,
14
+ merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom
16
+ the Software is furnished to do so,
17
+ subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice
20
+ shall be included in all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
27
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
28
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.