Hardening rollup: net-contract compliance, Chatwoot echo race fix, full i18n, and tooling#46
Merged
Merged
Conversation
…t gates Backfill manifest i18n (name, description, config titles) for http-action, typebot-connector, and voice-transcription across all eight dashboard locales, and declare sessionScoped explicitly in after-hours and faq-bot. catalog.mjs now hard-fails on a missing sessionScoped field and on a stable plugin without testedOpenWAVersion, and warns on missing/partial i18n. The catalog test asserts every stable plugin ships a complete locale set.
scripts/run-tests.mjs finds every plugin directory by its manifest.json (the same rule as the catalog) and runs its tests, so a new plugin is covered by npm test without edits to package.json. scripts/*.test.mjs now runs too — the catalog test was previously never executed. A guard test fails if a plugin directory ships without tests. tsconfig include/exclude is now glob-based for the same reason, and package.json declares engines.node >= 22 (the toolchain target) plus a test:coverage script using Node's built-in coverage.
Replace the external zip CLI in package.mjs with a small STORE-method writer (scripts/zip-store.mjs) so packaging works on any platform without extra tooling. Archives are byte-deterministic (fixed timestamps) and covered by round-trip parse tests; output verified against system unzip.
chat-lock, multipart, and the LRU cooldown helper existed as near-verbatim copies across plugins (plugins ship as self-contained zips, so copying is deliberate). The copies are now identical and pinned by scripts/shared-copies.test.mjs, which fails the suite if they ever drift apart — a fix in one copy must be applied to all. The multipart copy in voice-transcription gains the CR/LF header sanitization the other copies already had, and the cooldown helper moves to a canonical cooldown.ts imported by after-hours, faq-bot, and http-action.
….2.0) The OTP target chat id was built straight from the phone number (<digits>@c.us), which can miss contacts addressed by a privacy id. The handler now resolves the canonical chat id via engine.canonicalChatId when the host provides it (engine:read), bounded by a 2 s timeout so a wedged engine bridge falls back to the phone-derived id instead of stalling the ingress job into a retry and a duplicate OTP.
SheetsClient now takes an injected NetFetch bound to ctx.net.fetch (host-proxied, SSRF-guarded) instead of the raw worker fetch, bringing the plugin inside the v0.7 net contract: permissions [net:fetch] with net.allow pinned to the two fixed Google hosts, minOpenWAVersion 0.7.0. Responses are read via res.body per the sandbox contract. Client tests now cover token caching, re-authentication after a 401, and malformed token responses.
… (v0.5.6) The adapter marked its own outgoing Chatwoot posts as seen only after the POST returned, but the message_created echo webhook could be processed inside that window — and the inbound/backfill paths hold a different per-chat lock than the webhook dedup, so nothing serialized the two. An echo landing in the gap was relayed to WhatsApp as a duplicate of a backfilled or mirrored message. The post and its marker write now hold a conversation-scoped lock that the webhook dedup also takes (innermost on every path, so no lock-order cycle). Includes a regression test that drives the echo into the window deterministically, plus README notes on mapping-storage growth.
PLUGIN-STANDARD.md gains a runtime contract (observed) section covering the res.body-only fetch response, the ~5 s hook budget, allowConfigHosts fail-fast, and the boot-time INSTALLED reset, plus the manifest gates now enforced by the catalog. PluginNetResponse.text()/json()/arrayBuffer() are marked @deprecated since the sandbox runtime does not provide them. The after-hours, faq-bot, and chat-flow READMEs document that per-chat state keyed by chat id can be orphaned when a contact migrates between @lid and @c.us addressing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A cross-repo hardening and tooling pass. Each commit is scoped to one concern; the highlights:
ctx.net.fetch(host-proxied, SSRF-guarded) instead of the raw worker fetch, withnet:fetch+net.allowpinned to the two fixed Google hosts andminOpenWAVersion0.7.0. Client tests now cover token caching, 401 re-authentication, and malformed token responses.message_createdwebhook could be processed between the adapter's outgoing post and its echo-marker write (inbound/backfill and the webhook path hold different per-chat locks), re-sending a backfilled or mirrored message to WhatsApp. The post + marker write now hold a conversation-scoped lock that the webhook dedup also takes. Deterministic regression test included.engine.canonicalChatIdbefore sending (bounded by a 2 s timeout, falling back to the phone-derived id), so OTPs land correctly for contacts under privacy-id addressing.catalog.mjsgains manifest gates (explicitsessionScoped,testedOpenWAVersionrequired for stable) and the catalog test asserts stable plugins carry complete i18n.npm testauto-discovers plugin test files (new plugins no longer need package.json edits, andscripts/*.test.mjsfinally runs), tsconfig includes are glob-based,engines.node >= 22is declared, atest:coveragescript uses Node's built-in coverage, and packaging no longer needs the externalzipCLI (internal zero-dependency STORE writer, byte-deterministic).PluginNetResponse.text()/json()/arrayBuffer()are marked@deprecated; READMEs document @lid-related known limitations and Chatwoot mapping-storage growth.Test plan
npm test— 422/422 passing (was 406 before this branch; new coverage for the echo race, Sheets client, canonical-id resolution incl. timeout, zip writer round-trip, drift guard, and discovery guard)npm run typecheck— cleannpm run catalog:check— catalog in syncnpm run build— all 10 plugins package under the 5 MB limit with the internal zip writer; output verified against systemunzip -tNote: gsheets-logger v0.3.0 changes its network path — smoke-install on a live instance before tagging the release.