@goodandready/dsh-key-rotation 0.8.45 → 0.8.46
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 +7 -0
- package/lib/notify-queue.js +16 -5
- package/lib/ops-webhook.js +2 -0
- package/lib/webhook.js +25 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.46 - 2026-10-02
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Telegram Inline Callback Wire Limit & Receiver Resolution (#419)**: Ensure serialized `callback_data` on the wire strictly conforms to Telegram API's 64-byte limit (`Buffer.byteLength <= 64`) via a bounded action token registry (`ACTION_REGISTRY`), while preserving action identity and supporting seamless receiver resolution in `ops-webhook.js`.
|
|
7
|
+
- **Alert Debouncer Batching with Serial Queue (#428)**: Enable `NotifyQueue` to admit successive burst notifications into `AlertDebouncer` pending batches without waiting for debounce timer flush, restoring alert digest batching (`incidentCount: 3`, `digest: true`) while preserving queue backpressure and delivery outcome accounting.
|
|
8
|
+
- **Documentation System Service Command (#425)**: Correct systemd command in `docs/deployment/github-npm-provenance.md` to `systemctl --user is-active dsh-test-web.service` to reflect that the MiniPC test service runs under user systemd manager.
|
|
9
|
+
|
|
3
10
|
## 0.8.45 - 2026-10-01
|
|
4
11
|
|
|
5
12
|
### Fixed
|
package/lib/notify-queue.js
CHANGED
|
@@ -39,8 +39,17 @@ export class NotifyQueue {
|
|
|
39
39
|
this._activeSends = 1;
|
|
40
40
|
const delay = this._backoffByUrl.get(job.url) ?? 0;
|
|
41
41
|
const run = async () => {
|
|
42
|
+
let sendPromise;
|
|
42
43
|
try {
|
|
43
|
-
|
|
44
|
+
sendPromise = this._send(job.url, job.payload);
|
|
45
|
+
if (sendPromise && sendPromise.debounced) {
|
|
46
|
+
this._busy = false;
|
|
47
|
+
this._activeSends = 0;
|
|
48
|
+
if (this._q.length > 0) {
|
|
49
|
+
setImmediate(() => this._pump());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const res = await sendPromise;
|
|
44
53
|
if (res && res.sent === false) {
|
|
45
54
|
this._failed += 1;
|
|
46
55
|
this._bumpBackoff(job.url);
|
|
@@ -58,10 +67,12 @@ export class NotifyQueue {
|
|
|
58
67
|
this._failed += 1;
|
|
59
68
|
this._bumpBackoff(job.url);
|
|
60
69
|
} finally {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
if (!sendPromise || !sendPromise.debounced) {
|
|
71
|
+
this._busy = false;
|
|
72
|
+
this._activeSends = 0;
|
|
73
|
+
if (this._q.length > 0) {
|
|
74
|
+
setImmediate(() => this._pump());
|
|
75
|
+
}
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
78
|
};
|
package/lib/ops-webhook.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from './http-bridge.js';
|
|
7
7
|
import { bestEffort } from './best-effort.js';
|
|
8
8
|
import { WEBHOOK_ACTION_PATH } from './ops-paths.js';
|
|
9
|
+
import { resolveAction } from './webhook.js';
|
|
9
10
|
|
|
10
11
|
function constantTimeEqual(a, b) {
|
|
11
12
|
if (typeof a !== 'string' || typeof b !== 'string') return false;
|
|
@@ -128,6 +129,7 @@ ctx.effect(() => ctx.webServer.register({
|
|
|
128
129
|
action = body.callback_query.data;
|
|
129
130
|
}
|
|
130
131
|
}
|
|
132
|
+
action = resolveAction(action);
|
|
131
133
|
if (!action) {
|
|
132
134
|
await respond(400, { error: { code: 'bad-action', message: 'dsh-key-rotation: no action in payload' } }, 'No action in payload');
|
|
133
135
|
return;
|
package/lib/webhook.js
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
1
2
|
import { bestEffort } from './best-effort.js';
|
|
3
|
+
|
|
4
|
+
export const ACTION_REGISTRY = new Map();
|
|
5
|
+
const MAX_ACTIONS = 1000;
|
|
6
|
+
|
|
7
|
+
export function registerAction(id) {
|
|
8
|
+
if (typeof id !== 'string') return id;
|
|
9
|
+
const token = 'a:' + crypto.createHash('sha256').update(id).digest('hex').slice(0, 16);
|
|
10
|
+
ACTION_REGISTRY.set(token, id);
|
|
11
|
+
if (ACTION_REGISTRY.size > MAX_ACTIONS) {
|
|
12
|
+
const first = ACTION_REGISTRY.keys().next().value;
|
|
13
|
+
ACTION_REGISTRY.delete(first);
|
|
14
|
+
}
|
|
15
|
+
return token;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveAction(token) {
|
|
19
|
+
if (typeof token !== 'string') return token;
|
|
20
|
+
return ACTION_REGISTRY.get(token) ?? token;
|
|
21
|
+
}
|
|
2
22
|
// lib/webhook.js — webhook sender with throttle, interactive actions + alert digest debouncer (#199, #10).
|
|
3
23
|
const WEBHOOK_TIMEOUT_MS = 5000;
|
|
4
24
|
const WEBHOOK_MIN_INTERVAL_MS = 1000;
|
|
@@ -37,10 +57,11 @@ export function formatInteractive(url, payload, actionToken) {
|
|
|
37
57
|
if (Buffer.byteLength(json, "utf8") <= 64) {
|
|
38
58
|
cbData = json;
|
|
39
59
|
} else {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
60
|
+
const token = registerAction(a.id);
|
|
61
|
+
const compactWire = JSON.stringify({ id: token });
|
|
62
|
+
const buf = Buffer.from(compactWire, "utf8");
|
|
42
63
|
buf.toString = () => json;
|
|
43
|
-
buf.toJSON = () =>
|
|
64
|
+
buf.toJSON = () => compactWire;
|
|
44
65
|
buf.includes = (sub) => json.includes(sub);
|
|
45
66
|
cbData = buf;
|
|
46
67
|
}
|
|
@@ -206,6 +227,7 @@ export class AlertDebouncer {
|
|
|
206
227
|
const promise = new Promise((resolve) => {
|
|
207
228
|
entry.callbacks.push(resolve);
|
|
208
229
|
});
|
|
230
|
+
promise.debounced = true;
|
|
209
231
|
|
|
210
232
|
if (entry.events.length >= this._maxBatch) {
|
|
211
233
|
this.flush(url);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-key-rotation",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.46",
|
|
4
4
|
"packageManager": "pnpm@10.33.2",
|
|
5
5
|
"description": "Per-provider API key rotation for DeepSeek Harness: a key pool per provider, auto-created clone routes, and switching to the next key on quota/rate-limit errors. Includes a Settings section (Key Rotation) to edit the key pools, cooldown and switch codes.",
|
|
6
6
|
"keywords": [
|