@link-assistant/agent 0.7.0 → 0.8.2

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.
@@ -42,6 +42,22 @@ export namespace MessageV2 {
42
42
  );
43
43
  export type APIError = z.infer<typeof APIError.Schema>;
44
44
 
45
+ /**
46
+ * Socket connection error - typically caused by Bun's 10-second idle timeout
47
+ * in Bun.serve() context. These errors are transient and should be retried.
48
+ * See: https://github.com/oven-sh/bun/issues/14439
49
+ */
50
+ export const SocketConnectionError = NamedError.create(
51
+ 'SocketConnectionError',
52
+ z.object({
53
+ message: z.string(),
54
+ isRetryable: z.literal(true),
55
+ })
56
+ );
57
+ export type SocketConnectionError = z.infer<
58
+ typeof SocketConnectionError.Schema
59
+ >;
60
+
45
61
  const PartBase = z.object({
46
62
  id: z.string(),
47
63
  sessionID: z.string(),
@@ -787,11 +803,24 @@ export namespace MessageV2 {
787
803
  },
788
804
  { cause: e }
789
805
  ).toObject();
790
- case e instanceof Error:
806
+ case e instanceof Error: {
807
+ const message = e.message || e.toString();
808
+ // Detect Bun socket connection errors (known Bun issue with 10s idle timeout)
809
+ // See: https://github.com/oven-sh/bun/issues/14439
810
+ const isSocketError =
811
+ message.includes('socket connection was closed') ||
812
+ message.includes('closed unexpectedly');
813
+ if (isSocketError) {
814
+ return new MessageV2.SocketConnectionError(
815
+ { message, isRetryable: true },
816
+ { cause: e }
817
+ ).toObject();
818
+ }
791
819
  return new NamedError.Unknown(
792
820
  { message: e.toString() },
793
821
  { cause: e }
794
822
  ).toObject();
823
+ }
795
824
  default:
796
825
  return new NamedError.Unknown(
797
826
  { message: JSON.stringify(e) },
@@ -314,9 +314,28 @@ export namespace SessionProcessor {
314
314
  const error = MessageV2.fromError(e, {
315
315
  providerID: input.providerID,
316
316
  });
317
- if (error?.name === 'APIError' && error.data.isRetryable) {
317
+
318
+ // Check if error is retryable (APIError or SocketConnectionError)
319
+ const isRetryableAPIError =
320
+ error?.name === 'APIError' && error.data.isRetryable;
321
+ const isRetryableSocketError =
322
+ error?.name === 'SocketConnectionError' &&
323
+ error.data.isRetryable &&
324
+ attempt < SessionRetry.SOCKET_ERROR_MAX_RETRIES;
325
+
326
+ if (isRetryableAPIError || isRetryableSocketError) {
318
327
  attempt++;
319
- const delay = SessionRetry.delay(error, attempt);
328
+ // Use socket-specific delay for socket errors
329
+ const delay =
330
+ error?.name === 'SocketConnectionError'
331
+ ? SessionRetry.socketErrorDelay(attempt)
332
+ : SessionRetry.delay(error, attempt);
333
+ log.info(() => ({
334
+ message: 'retrying',
335
+ errorType: error?.name,
336
+ attempt,
337
+ delay,
338
+ }));
320
339
  SessionStatus.set(input.sessionID, {
321
340
  type: 'retry',
322
341
  attempt,
@@ -34,6 +34,7 @@ import { mergeDeep, pipe } from 'remeda';
34
34
  import { ToolRegistry } from '../tool/registry';
35
35
  import { Wildcard } from '../util/wildcard';
36
36
  import { MCP } from '../mcp';
37
+ import { withTimeout } from '../util/timeout';
37
38
  import { ReadTool } from '../tool/read';
38
39
  import { ListTool } from '../tool/ls';
39
40
  import { FileTime } from '../file/time';
@@ -877,7 +878,28 @@ export namespace SessionPrompt {
877
878
  const execute = item.execute;
878
879
  if (!execute) continue;
879
880
  item.execute = async (args, opts) => {
880
- const result = await execute(args, opts);
881
+ // Get timeout for this specific tool
882
+ const timeout = await MCP.getToolTimeout(key);
883
+
884
+ // Wrap the execute call with timeout to prevent indefinite hangs
885
+ let result;
886
+ try {
887
+ result = await withTimeout(execute(args, opts), timeout);
888
+ } catch (error) {
889
+ // Check if it's a timeout error
890
+ if (
891
+ error instanceof Error &&
892
+ error.message.includes('timed out after')
893
+ ) {
894
+ const timeoutSec = Math.round(timeout / 1000);
895
+ throw new Error(
896
+ `MCP tool "${key}" timed out after ${timeoutSec} seconds. ` +
897
+ `The operation did not complete within the configured timeout. ` +
898
+ `You can increase the timeout in the MCP server configuration using tool_call_timeout or tool_timeouts.`
899
+ );
900
+ }
901
+ throw error;
902
+ }
881
903
 
882
904
  const textParts: string[] = [];
883
905
  const attachments: MessageV2.FilePart[] = [];
@@ -6,6 +6,13 @@ export namespace SessionRetry {
6
6
  export const RETRY_BACKOFF_FACTOR = 2;
7
7
  export const RETRY_MAX_DELAY_NO_HEADERS = 30_000; // 30 seconds
8
8
 
9
+ // Socket connection error retry configuration
10
+ // Bun's fetch() has a known 10-second idle timeout issue
11
+ // See: https://github.com/oven-sh/bun/issues/14439
12
+ export const SOCKET_ERROR_MAX_RETRIES = 3;
13
+ export const SOCKET_ERROR_INITIAL_DELAY = 1000; // 1 second
14
+ export const SOCKET_ERROR_BACKOFF_FACTOR = 2;
15
+
9
16
  export async function sleep(ms: number, signal: AbortSignal): Promise<void> {
10
17
  return new Promise((resolve, reject) => {
11
18
  const timeout = setTimeout(resolve, ms);
@@ -53,4 +60,15 @@ export namespace SessionRetry {
53
60
  RETRY_MAX_DELAY_NO_HEADERS
54
61
  );
55
62
  }
63
+
64
+ /**
65
+ * Calculate delay for socket connection error retries.
66
+ * Uses exponential backoff: 1s, 2s, 4s, etc.
67
+ */
68
+ export function socketErrorDelay(attempt: number): number {
69
+ return (
70
+ SOCKET_ERROR_INITIAL_DELAY *
71
+ Math.pow(SOCKET_ERROR_BACKOFF_FACTOR, attempt - 1)
72
+ );
73
+ }
56
74
  }
package/EXAMPLES.md DELETED
@@ -1,462 +0,0 @@
1
- # Usage Examples
2
-
3
- This document provides practical examples for using each tool with both `@link-assistant/agent` and `opencode` commands.
4
-
5
- > ⚠️ **Bun-only** - `@link-assistant/agent` requires [Bun](https://bun.sh) and does NOT support Node.js or Deno.
6
-
7
- ## Table of Contents
8
-
9
- - [Basic Usage](#basic-usage)
10
- - [File Operations](#file-operations)
11
- - [Search Tools](#search-tools)
12
- - [Execution Tools](#execution-tools)
13
- - [Utility Tools](#utility-tools)
14
-
15
- ## Basic Usage
16
-
17
- ### Simplest Examples - Start Here!
18
-
19
- **Plain text (@link-assistant/agent only, easiest!):**
20
-
21
- ```bash
22
- echo "hi" | agent
23
- ```
24
-
25
- **Simple JSON message (both @link-assistant/agent and opencode):**
26
-
27
- @link-assistant/agent:
28
-
29
- ```bash
30
- echo '{"message":"hi"}' | agent
31
- ```
32
-
33
- opencode:
34
-
35
- ```bash
36
- echo '{"message":"hi"}' | opencode run --format json --model opencode/grok-code
37
- ```
38
-
39
- ### Plain Text Input (@link-assistant/agent only)
40
-
41
- ```bash
42
- # Simple message
43
- echo "hello world" | agent
44
-
45
- # Ask a question
46
- echo "what is TypeScript?" | agent
47
-
48
- # Request web search
49
- echo "search the web for latest React news" | agent
50
- ```
51
-
52
- ### JSON Input Examples
53
-
54
- **@link-assistant/agent:**
55
-
56
- ```bash
57
- echo '{"message":"hello world"}' | agent
58
- ```
59
-
60
- **opencode:**
61
-
62
- ```bash
63
- echo '{"message":"hello world"}' | opencode run --format json --model opencode/grok-code
64
- ```
65
-
66
- ## File Operations
67
-
68
- ### bash Tool
69
-
70
- Execute shell commands.
71
-
72
- **@link-assistant/agent:**
73
-
74
- ```bash
75
- echo '{"message":"run command","tools":[{"name":"bash","params":{"command":"echo hello world"}}]}' | agent
76
- ```
77
-
78
- **opencode:**
79
-
80
- ```bash
81
- echo '{"message":"run command","tools":[{"name":"bash","params":{"command":"echo hello world"}}]}' | opencode run --format json --model opencode/grok-code
82
- ```
83
-
84
- **Example with description:**
85
-
86
- ```bash
87
- echo '{"message":"list files","tools":[{"name":"bash","params":{"command":"ls -la","description":"List all files in current directory"}}]}' | agent
88
- ```
89
-
90
- ### read Tool
91
-
92
- Read file contents.
93
-
94
- **@link-assistant/agent:**
95
-
96
- ```bash
97
- echo '{"message":"read file","tools":[{"name":"read","params":{"file_path":"/path/to/file.txt"}}]}' | agent
98
- ```
99
-
100
- **opencode:**
101
-
102
- ```bash
103
- echo '{"message":"read file","tools":[{"name":"read","params":{"file_path":"/path/to/file.txt"}}]}' | opencode run --format json --model opencode/grok-code
104
- ```
105
-
106
- ### write Tool
107
-
108
- Write content to a file.
109
-
110
- **@link-assistant/agent:**
111
-
112
- ```bash
113
- echo '{"message":"write file","tools":[{"name":"write","params":{"file_path":"/tmp/test.txt","content":"Hello World"}}]}' | agent
114
- ```
115
-
116
- **opencode:**
117
-
118
- ```bash
119
- echo '{"message":"write file","tools":[{"name":"write","params":{"file_path":"/tmp/test.txt","content":"Hello World"}}]}' | opencode run --format json --model opencode/grok-code
120
- ```
121
-
122
- ### edit Tool
123
-
124
- Edit file with string replacement.
125
-
126
- **@link-assistant/agent:**
127
-
128
- ```bash
129
- echo '{"message":"edit file","tools":[{"name":"edit","params":{"file_path":"/tmp/test.txt","old_string":"Hello","new_string":"Hi"}}]}' | agent
130
- ```
131
-
132
- **opencode:**
133
-
134
- ```bash
135
- echo '{"message":"edit file","tools":[{"name":"edit","params":{"file_path":"/tmp/test.txt","old_string":"Hello","new_string":"Hi"}}]}' | opencode run --format json --model opencode/grok-code
136
- ```
137
-
138
- ### list Tool
139
-
140
- List directory contents.
141
-
142
- **@link-assistant/agent:**
143
-
144
- ```bash
145
- echo '{"message":"list directory","tools":[{"name":"list","params":{"path":"."}}]}' | agent
146
- ```
147
-
148
- **opencode:**
149
-
150
- ```bash
151
- echo '{"message":"list directory","tools":[{"name":"list","params":{"path":"."}}]}' | opencode run --format json --model opencode/grok-code
152
- ```
153
-
154
- ## Search Tools
155
-
156
- ### glob Tool
157
-
158
- Find files using glob patterns.
159
-
160
- **@link-assistant/agent:**
161
-
162
- ```bash
163
- # Find all JavaScript files
164
- echo '{"message":"find js files","tools":[{"name":"glob","params":{"pattern":"**/*.js"}}]}' | agent
165
-
166
- # Find TypeScript files in src directory
167
- echo '{"message":"find ts files","tools":[{"name":"glob","params":{"pattern":"src/**/*.ts"}}]}' | agent
168
- ```
169
-
170
- **opencode:**
171
-
172
- ```bash
173
- echo '{"message":"find js files","tools":[{"name":"glob","params":{"pattern":"**/*.js"}}]}' | opencode run --format json --model opencode/grok-code
174
- ```
175
-
176
- ### grep Tool
177
-
178
- Search text in files with regex.
179
-
180
- **@link-assistant/agent:**
181
-
182
- ```bash
183
- # Search for pattern in files
184
- echo '{"message":"search pattern","tools":[{"name":"grep","params":{"pattern":"function","output_mode":"files_with_matches"}}]}' | agent
185
-
186
- # Search with content display
187
- echo '{"message":"search TODO","tools":[{"name":"grep","params":{"pattern":"TODO","output_mode":"content"}}]}' | agent
188
-
189
- # Case-insensitive search in JavaScript files
190
- echo '{"message":"search error","tools":[{"name":"grep","params":{"pattern":"error","-i":true,"type":"js","output_mode":"content"}}]}' | agent
191
- ```
192
-
193
- **opencode:**
194
-
195
- ```bash
196
- echo '{"message":"search pattern","tools":[{"name":"grep","params":{"pattern":"TODO","output_mode":"content"}}]}' | opencode run --format json --model opencode/grok-code
197
- ```
198
-
199
- ### websearch Tool
200
-
201
- Search the web using Exa API.
202
-
203
- **@link-assistant/agent (no environment variable needed!):**
204
-
205
- ```bash
206
- echo '{"message":"search web","tools":[{"name":"websearch","params":{"query":"TypeScript latest features"}}]}' | agent
207
-
208
- echo '{"message":"search web","tools":[{"name":"websearch","params":{"query":"React hooks best practices"}}]}' | agent
209
- ```
210
-
211
- **opencode (requires OPENCODE_EXPERIMENTAL_EXA=true):**
212
-
213
- ```bash
214
- echo '{"message":"search web","tools":[{"name":"websearch","params":{"query":"TypeScript latest features"}}]}' | opencode run --format json --model opencode/grok-code
215
- ```
216
-
217
- ### codesearch Tool
218
-
219
- Search code repositories and documentation.
220
-
221
- **@link-assistant/agent (no environment variable needed!):**
222
-
223
- ```bash
224
- echo '{"message":"search code","tools":[{"name":"codesearch","params":{"query":"React hooks implementation"}}]}' | agent
225
-
226
- echo '{"message":"search code","tools":[{"name":"codesearch","params":{"query":"async/await patterns"}}]}' | agent
227
- ```
228
-
229
- **opencode (requires OPENCODE_EXPERIMENTAL_EXA=true):**
230
-
231
- ```bash
232
- echo '{"message":"search code","tools":[{"name":"codesearch","params":{"query":"React hooks implementation"}}]}' | opencode run --format json --model opencode/grok-code
233
- ```
234
-
235
- ## Execution Tools
236
-
237
- ### batch Tool
238
-
239
- Batch multiple tool calls together for optimal performance.
240
-
241
- **@link-assistant/agent (no configuration needed!):**
242
-
243
- ```bash
244
- echo '{"message":"run batch","tools":[{"name":"batch","params":{"tool_calls":[{"tool":"bash","parameters":{"command":"echo hello"}},{"tool":"bash","parameters":{"command":"echo world"}}]}}]}' | agent
245
- ```
246
-
247
- **opencode (requires experimental config):**
248
-
249
- ```bash
250
- # Create config file first
251
- mkdir -p .link-assistant-agent
252
- echo '{"experimental":{"batch_tool":true}}' > .link-assistant-agent/opencode.json
253
-
254
- # Then run
255
- echo '{"message":"run batch","tools":[{"name":"batch","params":{"tool_calls":[{"tool":"bash","parameters":{"command":"echo hello"}},{"tool":"bash","parameters":{"command":"echo world"}}]}}]}' | opencode run --format json --model opencode/grok-code
256
- ```
257
-
258
- ### task Tool
259
-
260
- Launch specialized agents for complex tasks.
261
-
262
- **@link-assistant/agent:**
263
-
264
- ```bash
265
- echo '{"message":"launch task","tools":[{"name":"task","params":{"description":"Analyze codebase","prompt":"Find all TODO comments in JavaScript files","subagent_type":"general-purpose"}}]}' | agent
266
- ```
267
-
268
- **opencode:**
269
-
270
- ```bash
271
- echo '{"message":"launch task","tools":[{"name":"task","params":{"description":"Analyze codebase","prompt":"Find all TODO comments in JavaScript files","subagent_type":"general-purpose"}}]}' | opencode run --format json --model opencode/grok-code
272
- ```
273
-
274
- ## Utility Tools
275
-
276
- ### todo Tool
277
-
278
- Read and write TODO items for task tracking.
279
-
280
- **@link-assistant/agent:**
281
-
282
- ```bash
283
- # Write todos
284
- echo '{"message":"add todos","tools":[{"name":"todowrite","params":{"todos":[{"content":"Implement feature X","status":"pending","activeForm":"Implementing feature X"},{"content":"Write tests","status":"pending","activeForm":"Writing tests"}]}}]}' | agent
285
-
286
- # Read todos
287
- echo '{"message":"read todos","tools":[{"name":"todoread","params":{}}]}' | agent
288
- ```
289
-
290
- **opencode:**
291
-
292
- ```bash
293
- echo '{"message":"add todos","tools":[{"name":"todowrite","params":{"todos":[{"content":"Implement feature X","status":"pending","activeForm":"Implementing feature X"}]}}]}' | opencode run --format json --model opencode/grok-code
294
- ```
295
-
296
- ### webfetch Tool
297
-
298
- Fetch and process web content.
299
-
300
- **@link-assistant/agent:**
301
-
302
- ```bash
303
- echo '{"message":"fetch url","tools":[{"name":"webfetch","params":{"url":"https://example.com","prompt":"Summarize the content"}}]}' | agent
304
- ```
305
-
306
- **opencode:**
307
-
308
- ```bash
309
- echo '{"message":"fetch url","tools":[{"name":"webfetch","params":{"url":"https://example.com","prompt":"Summarize the content"}}]}' | opencode run --format json --model opencode/grok-code
310
- ```
311
-
312
- ## Output Format
313
-
314
- ### JSON Standards
315
-
316
- @link-assistant/agent supports two JSON output format standards via the `--json-standard` option:
317
-
318
- #### OpenCode Standard (default)
319
-
320
- ```bash
321
- # Default - same as --json-standard opencode
322
- echo "hi" | agent
323
-
324
- # Explicit opencode standard
325
- echo "hi" | agent --json-standard opencode
326
- ```
327
-
328
- #### Claude Standard (experimental)
329
-
330
- ```bash
331
- # Claude CLI compatible format (NDJSON)
332
- echo "hi" | agent --json-standard claude
333
- ```
334
-
335
- ### JSON Event Streaming (Pretty-Printed) - OpenCode Standard
336
-
337
- @link-assistant/agent outputs JSON events in pretty-printed streaming format for easy readability, 100% compatible with OpenCode's event structure:
338
-
339
- ```bash
340
- echo "hi" | agent
341
- ```
342
-
343
- Output (pretty-printed JSON events):
344
-
345
- ```json
346
- {
347
- "type": "step_start",
348
- "timestamp": 1763618628840,
349
- "sessionID": "ses_560236487ffe3ROK1ThWvPwTEF",
350
- "part": {
351
- "id": "prt_a9fdca4e8001APEs6AriJx67me",
352
- "type": "step-start",
353
- ...
354
- }
355
- }
356
- {
357
- "type": "text",
358
- "timestamp": 1763618629886,
359
- "sessionID": "ses_560236487ffe3ROK1ThWvPwTEF",
360
- "part": {
361
- "type": "text",
362
- "text": "Hi! How can I help with your coding tasks today?",
363
- ...
364
- }
365
- }
366
- {
367
- "type": "step_finish",
368
- "timestamp": 1763618629916,
369
- "sessionID": "ses_560236487ffe3ROK1ThWvPwTEF",
370
- "part": {
371
- "type": "step-finish",
372
- "reason": "stop",
373
- ...
374
- }
375
- }
376
- ```
377
-
378
- This format is designed for:
379
-
380
- - **Readability**: Pretty-printed JSON is easy to read and debug
381
- - **Streaming**: Events output in real-time as they occur
382
- - **Compatibility**: 100% compatible with OpenCode's event structure
383
- - **Automation**: Can be parsed using standard JSON tools (see filtering examples below)
384
-
385
- ### Claude Stream-JSON Output (NDJSON)
386
-
387
- When using `--json-standard claude`, output is in NDJSON (Newline-Delimited JSON) format, compatible with Claude CLI:
388
-
389
- ```bash
390
- echo "hi" | agent --json-standard claude
391
- ```
392
-
393
- Output (compact NDJSON):
394
-
395
- ```json
396
- {"type":"init","timestamp":"2025-01-01T00:00:00.000Z","session_id":"ses_560236487ffe3ROK1ThWvPwTEF"}
397
- {"type":"message","timestamp":"2025-01-01T00:00:01.000Z","session_id":"ses_560236487ffe3ROK1ThWvPwTEF","role":"assistant","content":[{"type":"text","text":"Hi! How can I help with your coding tasks today?"}]}
398
- {"type":"result","timestamp":"2025-01-01T00:00:01.100Z","session_id":"ses_560236487ffe3ROK1ThWvPwTEF","status":"success","duration_ms":1100}
399
- ```
400
-
401
- Key differences from OpenCode format:
402
-
403
- - **Compact**: One JSON per line (no pretty-printing)
404
- - **Event Types**: `init`, `message`, `tool_use`, `tool_result`, `result`
405
- - **Timestamps**: ISO 8601 strings instead of Unix milliseconds
406
- - **Session ID**: `session_id` (snake_case) instead of `sessionID` (camelCase)
407
- - **Content**: Message content in array format with `{type, text}` objects
408
-
409
- ### Filtering Output
410
-
411
- Extract specific event types using `jq`:
412
-
413
- ```bash
414
- # Get only text responses
415
- echo '{"message":"hello"}' | agent | jq -r 'select(.type=="text") | .part.text'
416
-
417
- # Get tool use events
418
- echo '{"message":"run","tools":[{"name":"bash","params":{"command":"ls"}}]}' | agent | jq 'select(.type=="tool_use")'
419
-
420
- # Get bash tool output
421
- echo '{"message":"run","tools":[{"name":"bash","params":{"command":"echo test"}}]}' | agent | jq -r 'select(.type=="tool_use" and .part.tool=="bash") | .part.state.output'
422
-
423
- # Pretty print all events
424
- echo "hello" | agent | jq
425
- ```
426
-
427
- ## Tips
428
-
429
- ### @link-assistant/agent Advantages
430
-
431
- 1. **No Configuration**: WebSearch, CodeSearch, and Batch tools work without any setup
432
- 2. **Plain Text Input**: Can use simple text instead of JSON
433
- 3. **Always Enabled**: All tools available by default
434
- 4. **Bun-only**: Optimized for Bun runtime (no Node.js/Deno overhead)
435
-
436
- ### Working with JSON
437
-
438
- Use single quotes for the outer shell command and double quotes inside JSON:
439
-
440
- ```bash
441
- echo '{"message":"test","tools":[{"name":"bash","params":{"command":"echo hello"}}]}' | agent
442
- ```
443
-
444
- ### Debugging
445
-
446
- Add `| jq` to prettify JSON output:
447
-
448
- ```bash
449
- echo "hello" | agent | jq
450
- ```
451
-
452
- ### Chaining Commands
453
-
454
- Process output with standard Unix tools:
455
-
456
- ```bash
457
- # Count events
458
- echo "hello" | agent | wc -l
459
-
460
- # Filter and format
461
- echo "hello" | agent | jq -r 'select(.type=="text") | .part.text'
462
- ```
package/LICENSE DELETED
@@ -1,24 +0,0 @@
1
- This is free and unencumbered software released into the public domain.
2
-
3
- Anyone is free to copy, modify, publish, use, compile, sell, or
4
- distribute this software, either in source code form or as a compiled
5
- binary, for any purpose, commercial or non-commercial, and by any
6
- means.
7
-
8
- In jurisdictions that recognize copyright laws, the author or authors
9
- of this software dedicate any and all copyright interest in the
10
- software to the public domain. We make this dedication for the benefit
11
- of the public at large and to the detriment of our heirs and
12
- successors. We intend this dedication to be an overt act of
13
- relinquishment in perpetuity of all present and future rights to this
14
- software under copyright law.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
- IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
- OTHER DEALINGS IN THE SOFTWARE.
23
-
24
- For more information, please refer to <https://unlicense.org>