Skip to content

Foss main#26

Closed
aznszn wants to merge 289 commits into
main-oldfrom
foss-main
Closed

Foss main#26
aznszn wants to merge 289 commits into
main-oldfrom
foss-main

Conversation

@aznszn

@aznszn aznszn commented May 20, 2026

Copy link
Copy Markdown

Diff comparison purposes only – do not merge

tommoor and others added 30 commits March 30, 2026 19:42
* feat: Add support for Docker Swarm style secrets

* fix: Handle empty-string env values and bare _FILE key in resolveFileSecrets

Use undefined check instead of truthiness so empty-string values are
treated as "already set" and not overridden by _FILE variants. Skip
processing when the key is exactly "_FILE" to avoid creating an
empty-key entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Preload share popover data on hover with useShareDataLoader hook

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Route programmatic closes through handleOpenChange and fix race conditions

- closePopover now calls handleOpenChange(false) so reset() fires on all
  close paths, including programmatic closes via onRequestClose
- Reset requestedRef when entity id changes so preload fires for new targets
- Use request counter to prevent stale loading state when reset() is called
  during an in-flight request

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Wrap collection and document names in the header breadcrumb with
ContextMenu components, enabling right-click menus with relevant
actions. Each breadcrumb item type has its own component to scope
hooks. Breadcrumb links prevent navigation when a context menu is open.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…11913)

* fix: Integrations list missing when language is not English

The group filter on the Integrations settings page compared against the
hardcoded string "Integrations" instead of the translated value from
t("Integrations"), causing no integrations to appear in non-English locales.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Sidebar shows unconnected integrations in non-English locales

Same hardcoded "Integrations" string comparison issue as the main
integrations page — the sidebar filter skipped the connected-check
when the translated group name didn't match the English literal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Initial plan

* chore: upgrade Node.js base image from 22.21.0 to 24.14.1 (LTS)

* chore: include node version in node_modules cache keys

* Add canary build for docker changes

* fix: Try docker driver

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Initial plan

* Fix mention trigger to work after CJK characters without preceding space

Agent-Logs-Url: https://github.com/outline/outline/sessions/b34bba3f-fe94-408c-bf09-794f8e3d05ff

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Re-highlight result context client-side using the current search query
instead of relying on server-generated <b> tags. This prevents stale
highlights when results from a previous query are still displayed while
a newer search is in-flight. Also guards against setting stale results
by checking the query ref before updating state.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
)

* Initial plan

* fix: allow Tab to indent list items inside toggle blocks

When the cursor is inside a list within a toggle block, the indentBlock
command was consuming the Tab key event before the list's sinkListItem
handler could run. This happened because indentBlock found a previous
container_toggle sibling at the ancestor level and returned true.

Fix: return false early in indentBlock when the cursor is inside a list,
allowing the list's Tab handler to handle indentation correctly.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
* Add Node LTS auto-update script

* fix: Validate LTS version and update CI step name in Node update workflow

Add semver validation for the fetched LTS version to prevent creating
PRs with invalid node versions (e.g. null) if the upstream API changes.
Also update the human-readable step name in ci.yml during major bumps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ine#11929)

* Use CSS highlights instead of editor decorations when available

* Fix scroll target for non-HTML elements and refresh highlights on toggle fold

- Use `Element` instead of `HTMLElement` for scroll target so SVG/MathML
  elements are handled correctly
- Bump highlight generation on toggle fold/unfold transactions so
  newly visible matches get proper highlight ranges
- Cache decoration getter result to avoid redundant mapping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Add email subscriptions to public docs
* Improve scoping of public share subscriptions

* fix: Add missing transaction, includeChildDocuments check, and test documentId

- Pass { transaction } to ShareSubscription.create in the subscribe handler
  so the insert runs atomically with the duplicate-check findOne/lock
- Skip ancestor-scoped subscription notifications when the share has
  includeChildDocuments=false, preventing notifications for inaccessible docs
- Add required documentId field to all share subscription tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Resolve type error for nullable share.documentId in tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* JSDoc

* Hide subscription option for logged-in users

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: Move welcome email to processor

* fix: Restore welcome email on invite acceptance
* fix: move redirect logic one component up

* fix: use <Redirect>
…e#11942) (outline#11948)

Move document icons out of the ContextMenu trigger span and into the
action's icon property, consistent with how collection icons work. Block-level
icon elements (FontAwesome, custom emoji) inside the inline span were taking
a full line and pushing text below the overflow clip. Also remove display:flex
from breadcrumb Item so text-overflow:ellipsis works correctly.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Remove share pre-creation

* Disable toggle while saving

* cleanup unused methods
* Apply suggested fix to shared/editor/plugins/SuggestionsMenuPlugin.ts from Copilot Autofix

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Apply suggested fix to shared/editor/plugins/SuggestionsMenuPlugin.ts from Copilot Autofix

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Apply suggested fix to shared/editor/plugins/SuggestionsMenuPlugin.ts from Copilot Autofix

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: Last active timestamp should always read as now for current user

* Shorten language on members table
* Display keyboard shortcuts in menus where available

* feat: Display keyboard shortcuts in action menus

Pass shortcut data from Action definitions through to menu items and
render formatted key symbols on the right side of menu entries. Handles
platform differences via normalizeKeyDisplay. Also adds Control key
display support and uppercase formatting for single-letter shortcuts.


Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
aznszn and others added 24 commits April 30, 2026 21:07
* fix: Autofocus inside lazy-loaded modal/popover

* use wrapperRef

* remove unused import
* chore: resolve no-explicit-any lint warnings in plugins

Replaces uses of `any` in the plugins directory with concrete types,
`unknown`, or structured type assertions, addressing the remaining
typescript-eslint(no-explicit-any) warnings flagged by oxlint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: address review feedback in GitLabIssueProvider

Drop trailing semicolon from log string and add early return in
`destroyNamespace` when neither `user_id` nor `full_path` is present
to avoid an unnecessary full-scan transaction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: Add breadcrumb to MCP responses

* test: Update MCP test expectations for new response envelope

Tests were reading the old flat document shape; update them to read
through the new { document, breadcrumb } envelope.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* perf: Batch collection lookups when building breadcrumbs

Add getBreadcrumbsForDocuments helper that loads all referenced
collections in one query (with the user's memberships) and resolves
breadcrumbs from the per-collection cached documentStructure. Use it
in list_documents and move_document, replacing the per-document
Collection.findByPk that produced an N+1 query pattern.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test: Add coverage for getBreadcrumbsForDocuments and parallelize doc + breadcrumb loads

Run presentDocument and getDocumentBreadcrumb concurrently in fetch,
create_document, and update_document so the breadcrumb lookup no
longer adds latency on top of the presenter.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…#12243)

Replaces `as any` casts when constructing OAuth2Server Request/Response
with explicit objects containing the fields the library actually
consumes, and switches BaseStorage's manual header spread to a
node-fetch `Headers` instance to avoid the no-misused-spread warning.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* Add title guidance for MCP

* Scope H1 guidance to documents only
* chore: promote no-explicit-any from warn to error and resolve violations

Upgrades the oxlint rule severity and removes all 40 existing
`no-explicit-any` warnings across the codebase. Most call sites gained
proper types (SharedEditor refs, JSONNode/JSONMark for ProseMirror JSON
walking, DocumentsStore, dd-trace `Span` parameter inference, prosemirror
Fragment public API in place of internal `(fragment as any).content`).
A few load-bearing `any` uses were preserved with scoped disable
comments where changing the type would cascade widely (Sequelize JSONB
columns on `Event`, the `withTracing` higher-order function generic,
`Extension.options` consumed by many subclasses, dd-trace's `req`
patching).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…e#12245)

* feat: Add delete_document and delete_collection MCP tools

Adds MCP tools for deleting (or archiving) documents and collections.
Refactors Document#delete into destroyWithCtx and extracts collection
archive logic into Collection#archiveWithCtx so the same code paths can
be shared between the REST API and MCP entry points.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: Wrap MCP delete tools in DB transaction

Ensures delete/archive of documents and collections via MCP is atomic
and that row locks (transaction.LOCK.UPDATE) inside *WithCtx methods
actually apply, matching the pattern used by move_document.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: Clarify delete_collection MCP tool description

Reflects that collection deletion only soft-deletes non-archived
documents via the BeforeDestroy hook.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: Update editor generics

* fix: Address PR review on editor generics

- Restore null-guard on Link click handler so anchors aren't inert when no onClickLink is provided
- Mark onClickLink optional in LinkOptions and openLink command to match runtime
- Remove dead `collapsed` option from HeadingOptions
- Make ToggleBlock dictionary optional and restore optional-chained access for server-side schema instantiation

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* Refactor of OAuth account linking flows

* PR feedback
* fix: strip first subdomain for portal redirect on signout

Replace hardcoded "moneta." prefix with an empty string so the regex
strips just the leading subdomain (e.g. app.moneta.askii.ai →
moneta.askii.ai) regardless of the deployment domain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: use lookahead regex to safely strip leading subdomain

Switch to /^[^.]+\.(?=[^.]*\.[^.]*\.)/ so the subdomain is only
stripped when at least two dot-separated parts remain, preventing
over-stripping on bare domains.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Outline's accessToken cookie was hardcoded to 3 months
(addMonths(new Date(), 3)) at three call sites — outliving the rest of
the foss-server-bundle's 7-day session window. After a stack-wide
session expiry users would stay signed in to Outline alone.

Changes:
- Export JWT_COOKIE_TTL_DAYS = 7 from server/utils/authentication.ts as
  a single source of truth (with rationale in a docstring).
- Use addDays(new Date(), JWT_COOKIE_TTL_DAYS) at all three mint sites:
  - server/middlewares/authentication.ts (FORWARDAUTH login)
  - server/routes/auth/index.ts (/auth/redirect)
  - server/utils/authentication.ts (signIn callback)
- Fix a pre-existing upstream issue at /auth/redirect: getJwtToken was
  called with no expiresAt arg, producing a JWT the validator at
  utils/jwt.ts:47 never rejects (claim missing → check skipped). Now
  passes `expires` into both getJwtToken and the cookie set, matching
  the pattern the other two paths already use.
- Tests cover both halves:
  - middlewares/authentication.test.ts asserts the FORWARDAUTH cookie's
    expires is ~now + JWT_COOKIE_TTL_DAYS (±60s).
  - routes/auth/index.test.ts asserts /auth/redirect mints a JWT whose
    expiresAt claim is set and ~now + JWT_COOKIE_TTL_DAYS (±60s).

In future, lift JWT_COOKIE_TTL_DAYS to an env var if deployment-specific
control is needed.
Outline's accessToken cookie was hardcoded to 3 months
(addMonths(new Date(), 3)) at three call sites — outliving the rest of
the foss-server-bundle's 7-day session window. After a stack-wide
session expiry users would stay signed in to Outline alone.

Changes:
- Export JWT_COOKIE_TTL_DAYS = 7 from server/utils/authentication.ts as
  a single source of truth (with rationale in a docstring).
- Use addDays(new Date(), JWT_COOKIE_TTL_DAYS) at all three mint sites:
  - server/middlewares/authentication.ts (FORWARDAUTH login)
  - server/routes/auth/index.ts (/auth/redirect)
  - server/utils/authentication.ts (signIn callback)
- Fix a pre-existing upstream issue at /auth/redirect: getJwtToken was
  called with no expiresAt arg, producing a JWT the validator at
  utils/jwt.ts:47 never rejects (claim missing → check skipped). Now
  passes `expires` into both getJwtToken and the cookie set, matching
  the pattern the other two paths already use.
- Tests cover both halves:
  - middlewares/authentication.test.ts asserts the FORWARDAUTH cookie's
    expires is ~now + JWT_COOKIE_TTL_DAYS (±60s).
  - routes/auth/index.test.ts asserts /auth/redirect mints a JWT whose
    expiresAt claim is set and ~now + JWT_COOKIE_TTL_DAYS (±60s).

In future, lift JWT_COOKIE_TTL_DAYS to an env var if deployment-specific
control is needed.
The ForwardAuth (AUTH_TYPE=SSO) branch of validateAuthentication looked
users up with User.findOne({ where: { email: { [Op.iLike]: email } } }).
ILIKE treats % and _ in the supplied value as wildcards, so a request
with "x-auth-request-email: %@%.%" produces ILIKE '%@%.%' — which
matches every row. The first matched user (typically the bootstrap
admin) was then issued a 3-month JWT cookie.

Switching to exact match (where: { email }) strips all special meaning
from those characters. Email is already .toLowerCase().trim()'d before
the lookup, matching the existing User.findByEmail pattern; emails are
stored canonically lowercased so case-insensitive matching is not
required.

Not exploitable in production: oauth2-proxy intercepts unauthenticated
requests before they reach Outline and overwrites x-auth-request-*
headers from the verified OIDC identity. Defense-in-depth fix at the
code level in case backend reachability ever changes.

Adds a regression test asserting wildcard input does not impersonate
an existing user.
)

* fix(auth): re-key session on proxy-vs-JWT identity mismatch

When the bundle's portal "Log out of all apps" clears the shared
oauth2-proxy cookie + Cognito SSO session but a different user then
logs in, Outline's per-subdomain accessToken JWT survives — every
subsequent request keeps serving the previous user's session. Fixed
in two layers:

Server-side (validateAuthentication):
- JWT branch detects mismatch when SSO mode + cookie transport +
  x-auth-request-email header email != user.email (lowercase + trim
  on both sides; required by openspec). Throws StaleSessionRedirect
  (302 → /home + Set-Cookie clearing accessToken) so the followed
  request re-enters the fwd: header path, provisions the new user,
  and issues a fresh JWT. 302 chosen over 401 to avoid SPA "no
  access" toasts on doc URLs the new user can't see.
- fwd: provisioning serialised behind pg_advisory_xact_lock keyed on
  hashtext(email). Outline's users table has no unique email
  constraint (multi-team support), so parallel first-login requests
  would race to create duplicate rows — production hit 5 dupes
  before audit caught it. Lock is per-email so distinct users don't
  contend.
- normalizeProxyEmail rewritten to use indexOf chain instead of regex
  to close a polynomial-backtracking CodeQL finding; same semantics.

Client-side (userContinuity, ApiClient, AuthStore):
- checkUserContinuity at SPA boot compares accessToken JWT id to a
  localStorage marker; on mismatch wipes localStorage + IndexedDB +
  Cache API + unregisters service workers before window.location
  .replace("/home") so AuthStore rehydrates from clean state.
- ApiClient.fetch detects 302→HTML bounces on /api/* (either via
  response.redirected off the /api namespace or text/html
  Content-Type) and triggers the same wipeAndReload. Belt-and-
  suspenders fallback in AuthStore.fetchAuth wipes if /auth.info
  returns without data.user. Required because Outline's auth
  middleware only runs on /api/*, not on / — so at first SPA mount
  after switch the accessToken cookie hasn't rotated yet and the
  boot-time check can't see the mismatch.

Tests cover: server-side mismatch + cookie clearing, case- and
whitespace-variant emails, SQL-LIKE wildcard rejection (uses
findOne with exact-equality, not LIKE), advisory lock acquisition
before User.create.

* fix(auth): correct Logger.warn call signature in AuthStore.ts

Agent-Logs-Url: https://github.com/Pressingly/outline/sessions/2112e912-6aad-49c0-b0d5-8e0bdc465145

Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>

* fix(auth): clear remembered redirect paths on user switch

The 302 → wipe-and-reload flow already clears localStorage on user
switch, but `postLoginRedirectPath` lives in sessionStorage AND a
cookie, so it survived the wipe. AuthenticatedLayout reads that key
on mount via `usePostLoginPath` and issues `<Redirect to={path} />`,
sending the new user to the previous user's URL (e.g. a Collection
they have no access to). Net effect: even after the wipe lands the
SPA on /home, layout immediately bounces it back to the stale URL.

Three changes:

- `userContinuity.clearAuxiliaryUserState`: drops
  `sessionStorage.postLoginRedirectPath`, the `postLoginRedirectPath`
  cookie, and the `lastSignedIn` cookie. Called from all wipe paths
  in `userContinuity.ts` (cookie-present mismatch, cookie-absent +
  marker-present, `wipeAndReload`).

- `userContinuity.checkUserContinuity`: also wipes when the
  accessToken cookie is absent but a `LAST_USER_KEY` marker exists —
  the portal-logout + re-login-as-different-user path, where
  oauth2-proxy clears the cookie but the SPA still has the previous
  user's `AUTH_STORE` rehydrated.

- `useLastVisitedPath.clearLastVisitedPath` + call from
  `AuthStore.logout`: belt-and-suspenders so an explicit in-app
  logout also resets the remembered path. Without this, a same-
  browser sign-out + sign-in-as-different-user via the in-app
  button would still surface the previous path.

- `ApiClient.fetch`: tightened the `text/html` fallback to fire
  only on success (2xx) responses, so a transient Traefik 502 or
  oauth2-proxy login-redirect HTML page no longer triggers an
  unnecessary state wipe.

* fix(auth): force /home after user switch via one-shot sessionStorage flag

After the wipe + reload to /home, `AuthStore.fetchAuth` may still
redirect away (team domain change, default collection, postLogin
redirects in AuthenticatedLayout) before the previous fix's cleanup
takes hold. Add a one-shot sessionStorage flag
`outline_post_switch_redirect_home_once` set immediately before each
wipe-path navigation. `fetchAuth` consumes it after data is loaded
and forces `window.location.replace(homePath())`, overriding any
subsequent redirect logic. Flag is sessionStorage-scoped so it
naturally clears on tab close and doesn't leak across switches.

* fix(auth): back the post-switch redirect-home flag with a cookie

Adds a 1-hour cookie fallback alongside the sessionStorage entry for
the one-shot post-switch redirect flag. sessionStorage is per-tab
and may be unavailable in private-browsing or quota-exhausted modes;
the cookie ensures `consumePostSwitchRedirectHome` still returns
true on the post-wipe boot in those environments. Consume path
clears both surfaces.

* fix(auth): skip force-replace when already on /home

If the post-switch wipe lands the SPA on /home and AuthStore.fetchAuth
consumes the redirect flag, calling window.location.replace(homePath())
on a page already at homePath() causes a redundant reload. Guard the
replace with a pathname comparison so the flag still gets consumed
(clearing the marker) but the navigation is a no-op when unnecessary.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: awais786 <445320+awais786@users.noreply.github.com>
@hunzlahmalik hunzlahmalik marked this pull request as draft May 20, 2026 08:37
aznszn and others added 2 commits May 20, 2026 13:43
Resolved conflict in server/routes/auth/index.ts:
- Adopted upstream's AuthenticationType.APP middleware type
- Retained foss-main's JWT_COOKIE_TTL_DAYS 7-day TTL for token/cookie binding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lint: authentication.test.ts used `any` in several places
(createCtx overrides, error catch variables, ctx type annotations).
Added CtxOverrides and MiddlewareError interfaces; replaced all
explicit `any` with proper types so oxlint no-explicit-any passes.

Audit: three new high-severity advisories flagged by yarn npm audit:
- @babel/plugin-transform-modules-systemjs <=7.29.3 (GHSA-fv7c-fp4j-7gwp):
  arbitrary code generation. Fixed by bumping @babel/preset-env to
  ^7.29.5 (matching upstream) so the transitive dep resolves to 7.29.4,
  then deduping the ^7.11.0 range into the same entry.
- axios <1.15.2 (GHSA-pmwg-cvhr-8vh7, GHSA-pf86-5x62-jrwf,
  GHSA-6chq-wfr3-2hj9, GHSA-q8qp-cvcw-x6jj): NO_PROXY bypass and
  prototype pollution via aws-crt. Forced to ^1.15.2 via resolution.
- fast-uri <=3.1.1 (GHSA-v39h-62p7-jpjc, GHSA-q3j6-qgpj-74h6) and
  fast-xml-builder <=1.1.6 (GHSA-5wm8-gmm8-39j9) via ajv/fast-xml-parser.
  Forced to patched versions via resolutions.

All resolution ranges are scoped to the exact descriptors present in
yarn.lock, matching upstream's approach for the same advisories.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@aznszn aznszn mentioned this pull request May 20, 2026
@aznszn aznszn closed this May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.