@yawlabs/lemonsqueezy-mcp 0.10.7 → 0.10.8
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 +18 -1
- package/dist/index.js +32 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@yawlabs/lemonsqueezy-mcp` are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and versioning follows [SEMVER.md](./SEMVER.md).
|
|
4
4
|
|
|
5
|
+
## [0.10.8] -- 2026-05-19
|
|
6
|
+
|
|
7
|
+
### Security
|
|
8
|
+
|
|
9
|
+
- **Sink response body size guard extended to the error branch.** `sinkRequest` in `src/tools/sink.ts` previously only size-checked 2xx response bodies; a misbehaving sink returning a giant 4xx/5xx body could still buffer the whole thing into memory before any limit fired. A new `readBodyOrSizeError` helper pre-checks `Content-Length` against the 10 MB cap and is applied to both the error and success branches. The post-read length check on the 2xx path is retained as belt-and-braces against a lying `Content-Length`.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **2xx body-read mid-stream failures now collapse to the uniform `{ ok: false, error }` shape.** Previously a socket reset partway through reading the body propagated as an exception out of `sinkRequest`, surfacing as a less-informative error via the wrapper's catch-all. The 2xx body read is now wrapped in a try/catch that returns `Sink response body read failed: <message>` -- consistent with every other failure mode in the function.
|
|
14
|
+
- **`src/secret.ts` cache-hit branch tightened.** The test-mode cache-hit branch previously called `announceTestModeOnce()` redundantly (the flag is set on the first miss, so the cache-hit call was dead). Removed the call and the now-obsolete defensive comment.
|
|
15
|
+
|
|
16
|
+
### Tests
|
|
17
|
+
|
|
18
|
+
- **Sink coverage expanded** to pin `authorityClass` per tool (`read` for `ls_sink_events_list` / `ls_sink_stats`, `mutate` for `ls_sink_event_mark_processed`), 4xx-oversized-Content-Length, 2xx-lying-Content-Length, and 2xx mid-stream body-read failures. `stubFetch` gained an optional `responseHeaders` field so error-branch tests no longer override `globalThis.fetch` inline.
|
|
19
|
+
- **`parseCommand` contract pinned** in `src/secret.test.ts` for the four tokenizer edges that the rest of the suite only touched by accident: unterminated quote, all-quotes-collapse-to-empty, quoted-args-with-spaces (both quote styles), and quote-then-bare-word concatenation. Also added a 64 KB `maxBuffer` overflow test that exercises the `execFile` `ERR_CHILD_PROCESS_STDIO_MAXBUFFER` rewrap path.
|
|
20
|
+
|
|
5
21
|
## [0.10.7] -- 2026-05-16
|
|
6
22
|
|
|
7
23
|
### Security
|
|
@@ -343,7 +359,8 @@ Hardening pass for unattended automation against live billing flows.
|
|
|
343
359
|
|
|
344
360
|
Initial release. 59 tools covering all 17 LemonSqueezy API resources.
|
|
345
361
|
|
|
346
|
-
[Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.
|
|
362
|
+
[Unreleased]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.8...HEAD
|
|
363
|
+
[0.10.8]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.7...v0.10.8
|
|
347
364
|
[0.10.7]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.6...v0.10.7
|
|
348
365
|
[0.10.6]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.5...v0.10.6
|
|
349
366
|
[0.10.5]: https://github.com/YawLabs/lemonsqueezy-mcp/compare/v0.10.4...v0.10.5
|
package/dist/index.js
CHANGED
|
@@ -30653,10 +30653,7 @@ async function loadApiKey() {
|
|
|
30653
30653
|
if (testRaw && testRaw.trim() !== "") {
|
|
30654
30654
|
const fingerprint2 = fingerprintFor("test", testRaw);
|
|
30655
30655
|
const hit2 = fromCache(fingerprint2);
|
|
30656
|
-
if (hit2 !== null)
|
|
30657
|
-
announceTestModeOnce();
|
|
30658
|
-
return hit2;
|
|
30659
|
-
}
|
|
30656
|
+
if (hit2 !== null) return hit2;
|
|
30660
30657
|
announceTestModeOnce();
|
|
30661
30658
|
intoCache(fingerprint2, testRaw);
|
|
30662
30659
|
return testRaw;
|
|
@@ -31863,6 +31860,16 @@ function loadSinkConfig() {
|
|
|
31863
31860
|
function isToolHandlerResponse(value) {
|
|
31864
31861
|
return "ok" in value;
|
|
31865
31862
|
}
|
|
31863
|
+
async function readBodyOrSizeError(res) {
|
|
31864
|
+
const contentLength = res.headers.get("content-length");
|
|
31865
|
+
if (contentLength) {
|
|
31866
|
+
const declared = Number.parseInt(contentLength, 10);
|
|
31867
|
+
if (Number.isFinite(declared) && declared > MAX_BODY_SIZE_BYTES) {
|
|
31868
|
+
return { ok: false, declared };
|
|
31869
|
+
}
|
|
31870
|
+
}
|
|
31871
|
+
return { ok: true, text: await res.text() };
|
|
31872
|
+
}
|
|
31866
31873
|
function buildSinkPath(path, params) {
|
|
31867
31874
|
const parts = [];
|
|
31868
31875
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -31897,7 +31904,12 @@ async function sinkRequest(config2, method, pathAndQuery) {
|
|
|
31897
31904
|
if (!res.ok) {
|
|
31898
31905
|
let body = "";
|
|
31899
31906
|
try {
|
|
31900
|
-
|
|
31907
|
+
const readResult2 = await readBodyOrSizeError(res);
|
|
31908
|
+
if (readResult2.ok) {
|
|
31909
|
+
body = readResult2.text;
|
|
31910
|
+
} else {
|
|
31911
|
+
body = `[body too large: ${readResult2.declared} bytes exceeds ${MAX_BODY_SIZE_BYTES} byte limit]`;
|
|
31912
|
+
}
|
|
31901
31913
|
} catch {
|
|
31902
31914
|
}
|
|
31903
31915
|
let detail = body;
|
|
@@ -31923,7 +31935,20 @@ async function sinkRequest(config2, method, pathAndQuery) {
|
|
|
31923
31935
|
error: `Sink returned ${res.status}: ${detail || res.statusText}`
|
|
31924
31936
|
};
|
|
31925
31937
|
}
|
|
31926
|
-
|
|
31938
|
+
let readResult;
|
|
31939
|
+
try {
|
|
31940
|
+
readResult = await readBodyOrSizeError(res);
|
|
31941
|
+
} catch (err) {
|
|
31942
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
31943
|
+
return { ok: false, error: `Sink response body read failed: ${message}` };
|
|
31944
|
+
}
|
|
31945
|
+
if (!readResult.ok) {
|
|
31946
|
+
return {
|
|
31947
|
+
ok: false,
|
|
31948
|
+
error: `Sink response body too large: ${readResult.declared} bytes exceeds ${MAX_BODY_SIZE_BYTES} byte limit`
|
|
31949
|
+
};
|
|
31950
|
+
}
|
|
31951
|
+
const text = readResult.text;
|
|
31927
31952
|
if (!text.trim()) return { ok: true, data: {} };
|
|
31928
31953
|
if (text.length > MAX_BODY_SIZE_BYTES) {
|
|
31929
31954
|
return {
|
|
@@ -32806,7 +32831,7 @@ function readAuditLogResource(uri) {
|
|
|
32806
32831
|
}
|
|
32807
32832
|
|
|
32808
32833
|
// src/index.ts
|
|
32809
|
-
var version2 = true ? "0.10.
|
|
32834
|
+
var version2 = true ? "0.10.8" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
32810
32835
|
var subcommand = process.argv[2];
|
|
32811
32836
|
if (subcommand === "version" || subcommand === "--version") {
|
|
32812
32837
|
console.log(version2);
|
package/package.json
CHANGED