@kivel.io/cli 0.9.0 → 0.10.0
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/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/dist/listen.js +73 -16
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @kivel.io/cli
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ad5805c: Add live event streams to the SDK and use them in the webhook listener.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- ad5805c: Reconnect webhook listeners after transient network failures with backoff and resume from the last processed event cursor.
|
|
12
|
+
- Updated dependencies [ad5805c]
|
|
13
|
+
- @kivel.io/sdk@0.10.0
|
|
14
|
+
|
|
3
15
|
## 0.9.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -39,8 +39,8 @@ requests time out after 90 seconds. Mutations are never automatically retried.
|
|
|
39
39
|
|
|
40
40
|
`job`, `status-page`, `smtp`, `phone` and `event wait` use the same owned APIs. Operator-dependent services stay disabled until configured; phone sending and calls are not implemented. Run `kivel --help` for their current commands.
|
|
41
41
|
|
|
42
|
-
`listen` streams webhook events and can forward them to a local server.
|
|
43
|
-
your machine
|
|
42
|
+
`listen` streams webhook events and can forward them to a local server. On older
|
|
43
|
+
servers, it uses the webhook wait endpoint. It runs on your machine and stops on Ctrl+C. The optional server SSH gateway supports bounded
|
|
44
44
|
commands; it does not provide this continuous listener or a shell/SCP/SFTP service.
|
|
45
45
|
|
|
46
46
|
[Command reference](https://kivel.io/docs/cli/) ·
|
package/dist/listen.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { setTimeout as pause } from 'node:timers/promises';
|
|
2
3
|
import { KivelError } from '@kivel.io/sdk';
|
|
3
4
|
// Hop-by-hop and proxy headers describe the trip to Kivel, not the original sender.
|
|
4
5
|
const dropped = /^(host|content-length|connection|keep-alive|transfer-encoding|upgrade|te|trailer|proxy-.*|x-forwarded-.*|x-real-ip|cf-.*|cdn-loop|true-client-ip|x-access-token|authorization)$/i;
|
|
@@ -24,27 +25,41 @@ export async function listen(client, inboxId, options, write, signal) {
|
|
|
24
25
|
if (options.forward)
|
|
25
26
|
new URL(options.forward);
|
|
26
27
|
let after;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
let delay = 200;
|
|
29
|
+
async function backoff(seconds = 0) {
|
|
30
|
+
await pause(Math.max(delay, seconds * 1000), undefined, { signal }).catch(() => { });
|
|
31
|
+
delay = Math.min(delay * 2, 10000);
|
|
30
32
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
async function retry(error) {
|
|
34
|
+
if (signal?.aborted)
|
|
35
|
+
return;
|
|
36
|
+
if (error instanceof KivelError && error.status === 429) {
|
|
37
|
+
await backoff(error.retryAfter ?? 2);
|
|
38
|
+
}
|
|
39
|
+
else if (error instanceof TypeError ||
|
|
40
|
+
(error instanceof DOMException &&
|
|
41
|
+
['AbortError', 'NetworkError', 'TimeoutError'].includes(error.name))) {
|
|
42
|
+
await backoff();
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
while (!options.all && !signal?.aborted) {
|
|
34
49
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
50
|
+
after = (await client.inspectInbox(inboxId, options.token)).requests.at(-1)?.id;
|
|
51
|
+
delay = 200;
|
|
52
|
+
break;
|
|
39
53
|
}
|
|
40
54
|
catch (error) {
|
|
41
|
-
|
|
42
|
-
await new Promise((resolve) => setTimeout(resolve, (error.retryAfter ?? 2) * 1000));
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
throw error;
|
|
55
|
+
await retry(error);
|
|
46
56
|
}
|
|
47
|
-
|
|
57
|
+
}
|
|
58
|
+
if (signal?.aborted)
|
|
59
|
+
return;
|
|
60
|
+
write({ event: 'listening', inboxId, forward: options.forward ?? null });
|
|
61
|
+
async function report(requests) {
|
|
62
|
+
for (const request of requests) {
|
|
48
63
|
after = request.id;
|
|
49
64
|
write({ event: 'request', request });
|
|
50
65
|
if (!options.forward)
|
|
@@ -78,4 +93,46 @@ export async function listen(client, inboxId, options, write, signal) {
|
|
|
78
93
|
}
|
|
79
94
|
}
|
|
80
95
|
}
|
|
96
|
+
let cursor;
|
|
97
|
+
while (!signal?.aborted) {
|
|
98
|
+
try {
|
|
99
|
+
for await (const change of client.stream([`webhook:${inboxId}`], {
|
|
100
|
+
...(options.token ? { token: options.token } : {}),
|
|
101
|
+
...(cursor ? { lastEventId: cursor } : {}),
|
|
102
|
+
...(signal ? { signal } : {}),
|
|
103
|
+
})) {
|
|
104
|
+
const { requests } = await client.waitForRequests(inboxId, {
|
|
105
|
+
...(options.token ? { token: options.token } : {}),
|
|
106
|
+
...(after ? { after } : {}),
|
|
107
|
+
timeoutSeconds: 1,
|
|
108
|
+
});
|
|
109
|
+
await report(requests);
|
|
110
|
+
cursor = change.cursor;
|
|
111
|
+
delay = 200;
|
|
112
|
+
}
|
|
113
|
+
await backoff();
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (signal?.aborted)
|
|
117
|
+
return;
|
|
118
|
+
if (error instanceof KivelError && [404, 501, 503].includes(error.status))
|
|
119
|
+
break;
|
|
120
|
+
await retry(error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
while (!signal?.aborted) {
|
|
124
|
+
try {
|
|
125
|
+
const batch = await client.waitForRequests(inboxId, {
|
|
126
|
+
...(options.token ? { token: options.token } : {}),
|
|
127
|
+
...(after ? { after } : {}),
|
|
128
|
+
});
|
|
129
|
+
await report(batch.requests);
|
|
130
|
+
delay = 200;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (signal?.aborted)
|
|
134
|
+
return;
|
|
135
|
+
await retry(error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
81
138
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kivel.io/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kivel": "./bin/kivel.mjs"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"CHANGELOG.md"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@kivel.io/sdk": "0.
|
|
21
|
+
"@kivel.io/sdk": "0.10.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^26.6.5",
|