From 7875756d5b2f0de8b332b02d8a0b1c21c511572b Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 20 Apr 2026 10:59:34 +0800 Subject: [PATCH 01/28] feat(stage-pocket): add OIDC sign-in and callback pages aligned with stage-web Made-with: Cursor --- apps/stage-pocket/src/pages/auth/callback.vue | 109 ++++++++++++++++++ apps/stage-pocket/src/pages/auth/sign-in.vue | 107 +++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 apps/stage-pocket/src/pages/auth/callback.vue create mode 100644 apps/stage-pocket/src/pages/auth/sign-in.vue diff --git a/apps/stage-pocket/src/pages/auth/callback.vue b/apps/stage-pocket/src/pages/auth/callback.vue new file mode 100644 index 0000000000..4add31dcdd --- /dev/null +++ b/apps/stage-pocket/src/pages/auth/callback.vue @@ -0,0 +1,109 @@ + + + diff --git a/apps/stage-pocket/src/pages/auth/sign-in.vue b/apps/stage-pocket/src/pages/auth/sign-in.vue new file mode 100644 index 0000000000..1e4f42bd11 --- /dev/null +++ b/apps/stage-pocket/src/pages/auth/sign-in.vue @@ -0,0 +1,107 @@ + + + From 2a3ebeb9e891e3aff0bf07ed81f412ad84351a62 Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 20 Apr 2026 11:00:09 +0800 Subject: [PATCH 02/28] fix(stage-layouts): remove HeaderAvatar dropdown Transition for Capacitor WebView Made-with: Cursor --- .../src/components/Layouts/HeaderAvatar.vue | 107 ++++++++---------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue b/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue index f064cec818..1cb4ad8573 100644 --- a/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue +++ b/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue @@ -91,71 +91,62 @@ onClickOutside(dropdownRef, () => { /> - -
-
-

- Signed in as -

-

- {{ userName }} -

-
-
- {{ formattedCredits }} Flux -
+
+

+ Signed in as +

+

+ {{ userName }} +

+
+
+ {{ formattedCredits }} Flux
+
-
- -
- Profile - +
+ +
+ Profile + - -
- Flux - + +
+ Flux + - -
- Settings - -
+ +
+ Settings + +
-
- -
+
+
- +
From 73ef2e5be5c4d62a60104e8026ea64114157f9d0 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 26 Apr 2026 00:20:22 +0800 Subject: [PATCH 03/28] feat(stage-ui): [WIP] ngrok header + native OIDC flow - isNgrokServerUrl / applyNgrokSkipRequestHeader: free ngrok interstitial; pass header on Hono client, token/refresh, better-auth client fetch - buildAuthorizationURL with full params (keep provider= on authorize URL) - Capacitor: use localStorage for PKCE state; full-page location.assign(authorize) so ensureDynamicFirstPartyRedirectUri runs before social (skip signIn.social in-app on native) Tighten or replace ngrok host sniffing when a stable config exists. Made-with: Cursor --- packages/stage-ui/src/composables/api.ts | 10 ++++-- packages/stage-ui/src/libs/auth-oidc.ts | 39 ++++++++++++++++++------ packages/stage-ui/src/libs/auth.ts | 33 +++++++++++++++++--- packages/stage-ui/src/libs/server.ts | 21 +++++++++++++ 4 files changed, 86 insertions(+), 17 deletions(-) diff --git a/packages/stage-ui/src/composables/api.ts b/packages/stage-ui/src/composables/api.ts index 8004079c76..daf51f1901 100644 --- a/packages/stage-ui/src/composables/api.ts +++ b/packages/stage-ui/src/composables/api.ts @@ -3,10 +3,16 @@ import type { AppType } from '../../../../apps/server/src/app' import { hc } from 'hono/client' import { authedFetch } from '../libs/auth-fetch' -import { SERVER_URL } from '../libs/server' +import { applyNgrokSkipRequestHeader, SERVER_URL } from '../libs/server' + +async function fetchWithNgrok(input: RequestInfo | URL, init?: RequestInit): Promise { + const headers = new Headers(init?.headers) + applyNgrokSkipRequestHeader(headers) + return authedFetch(input, { ...init, headers }) +} export const client = hc(SERVER_URL, { - fetch: authedFetch, + fetch: fetchWithNgrok, }) export type StageApiClient = typeof client diff --git a/packages/stage-ui/src/libs/auth-oidc.ts b/packages/stage-ui/src/libs/auth-oidc.ts index 666ff33c5a..4be1996372 100644 --- a/packages/stage-ui/src/libs/auth-oidc.ts +++ b/packages/stage-ui/src/libs/auth-oidc.ts @@ -1,6 +1,7 @@ +import { Capacitor } from '@capacitor/core' import { generateCodeChallenge, generateCodeVerifier, generateState } from '@proj-airi/stage-shared/auth' -import { SERVER_URL } from './server' +import { applyNgrokSkipRequestHeader, SERVER_URL } from './server' // OIDC Authorization Code + PKCE client for all platforms. @@ -94,10 +95,12 @@ export async function exchangeCodeForTokens( bodyParams.client_secret = params.clientSecret const body = new URLSearchParams(bodyParams) + const tokenHeaders = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }) + applyNgrokSkipRequestHeader(tokenHeaders) const response = await fetch(new URL(OIDC_TOKEN_PATH, SERVER_URL), { method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: tokenHeaders, body, }) @@ -129,10 +132,12 @@ export async function refreshAccessToken( params.client_secret = clientSecret const body = new URLSearchParams(params) + const refreshHeaders = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }) + applyNgrokSkipRequestHeader(refreshHeaders) const response = await fetch(new URL(OIDC_TOKEN_PATH, SERVER_URL), { method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: refreshHeaders, body, }) @@ -142,30 +147,44 @@ export async function refreshAccessToken( return await response.json() } -// Session storage keys for PKCE flow state (survives page navigation during OAuth) +// Keys for PKCE + OAuth params. Web: sessionStorage (tab-scoped). Native: localStorage +// (Capacitor + system browser / ASWeb often lose sessionStorage for the return navigation). const FLOW_STATE_KEY = 'auth/v1/oidc-flow-state' const FLOW_PARAMS_KEY = 'auth/v1/oidc-flow-params' +function getOidcFlowStorage(): Storage { + try { + if (Capacitor.isNativePlatform()) + return localStorage + } + catch { + // Capacitor unavailable (SSR / non-native build) + } + return sessionStorage +} + /** * Persist OIDC flow state before navigating to the authorization server. */ export function persistFlowState(flowState: OIDCFlowState, params: OIDCFlowParams): void { - sessionStorage.setItem(FLOW_STATE_KEY, JSON.stringify(flowState)) - sessionStorage.setItem(FLOW_PARAMS_KEY, JSON.stringify(params)) + const storage = getOidcFlowStorage() + storage.setItem(FLOW_STATE_KEY, JSON.stringify(flowState)) + storage.setItem(FLOW_PARAMS_KEY, JSON.stringify(params)) } /** * Retrieve and clear persisted OIDC flow state after callback. */ export function consumeFlowState(): { flowState: OIDCFlowState, params: OIDCFlowParams } | null { - const flowStateRaw = sessionStorage.getItem(FLOW_STATE_KEY) - const paramsRaw = sessionStorage.getItem(FLOW_PARAMS_KEY) + const storage = getOidcFlowStorage() + const flowStateRaw = storage.getItem(FLOW_STATE_KEY) + const paramsRaw = storage.getItem(FLOW_PARAMS_KEY) if (!flowStateRaw || !paramsRaw) return null - sessionStorage.removeItem(FLOW_STATE_KEY) - sessionStorage.removeItem(FLOW_PARAMS_KEY) + storage.removeItem(FLOW_STATE_KEY) + storage.removeItem(FLOW_PARAMS_KEY) return { flowState: JSON.parse(flowStateRaw), diff --git a/packages/stage-ui/src/libs/auth.ts b/packages/stage-ui/src/libs/auth.ts index bc21837a6b..1fa1734d5d 100644 --- a/packages/stage-ui/src/libs/auth.ts +++ b/packages/stage-ui/src/libs/auth.ts @@ -1,11 +1,12 @@ import type { OIDCFlowParams, TokenResponse } from './auth-oidc' +import { Capacitor } from '@capacitor/core' import { createAuthClient } from 'better-auth/vue' import { useAuthStore } from '../stores/auth' import { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } from './auth-config' import { buildAuthorizationURL, persistFlowState } from './auth-oidc' -import { SERVER_URL } from './server' +import { isNgrokServerUrl, SERVER_URL } from './server' export type OAuthProvider = 'google' | 'github' @@ -30,6 +31,9 @@ export const authClient = createAuthClient({ type: 'Bearer', token: () => getAuthToken() ?? '', }, + ...(isNgrokServerUrl() + ? { headers: { 'ngrok-skip-browser-warning': 'true' } } + : {}), }, }) @@ -183,17 +187,36 @@ export async function signOut() { * Builds the authorization URL, persists PKCE state, and navigates. */ export async function signInOIDC(params: OIDCFlowParams) { - const { provider, ...oidcParams } = params - const { url, flowState } = await buildAuthorizationURL(oidcParams) + // Must pass `params` (incl. `provider`) so authorize URL can include `?provider=google` / github. + const { url, flowState } = await buildAuthorizationURL(params) persistFlowState(flowState, params) - if (!provider) { + if (!params.provider) { window.location.href = url return } + // better-auth's signIn.social() uses the fetch client; in Capacitor WKWebView + // that path often does not perform a real top-level navigation to the IdP. + // Match server /sign-in: full-page GET to social, then 302 to Google/GitHub. + let useFullPageSocial = false + try { + useFullPageSocial = Capacitor.isNativePlatform() + } + catch { + // no Capacitor + } + if (useFullPageSocial) { + // NOTICE: First request must be /api/auth/oauth2/authorize (not /sign-in/social): + // `ensureDynamicFirstPartyRedirectUri` only runs on authorize, so the LAN + // `redirect_uri` is written to `oauth_client` before login/social. Skipping + // authorize left social with an unregistered redirect and no Google 302. + window.location.assign(url.toString()) + return + } + await authClient.signIn.social({ - provider, + provider: params.provider, callbackURL: url.toString(), }) } diff --git a/packages/stage-ui/src/libs/server.ts b/packages/stage-ui/src/libs/server.ts index dd27eb710a..e20514aeef 100644 --- a/packages/stage-ui/src/libs/server.ts +++ b/packages/stage-ui/src/libs/server.ts @@ -1 +1,22 @@ export const SERVER_URL = import.meta.env.VITE_SERVER_URL || 'https://api.airi.build' + +/** Free ngrok serves an interstitial page unless this header is present; embedded WKWebView cannot dismiss it. */ +const NGROK_SKIP_HEADER = 'ngrok-skip-browser-warning' + +/** + * `window.location` navigations cannot set headers; the iOS DevBridge rewrites + * such requests. Fetches to `SERVER_URL` on ngrok need this. + */ +export function isNgrokServerUrl(): boolean { + try { + return new URL(SERVER_URL).hostname.includes('ngrok') + } + catch { + return false + } +} + +export function applyNgrokSkipRequestHeader(headers: Headers): void { + if (isNgrokServerUrl()) + headers.set(NGROK_SKIP_HEADER, 'true') +} From 23cc197b632c67a9b42adabcf84929c19e0eb861 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 26 Apr 2026 00:20:36 +0800 Subject: [PATCH 04/28] fix(ios): [WIP] ngrok skip interstitial in DEBUG WebView When navigating to an ngrok host, reload with ngrok-skip-browser-warning if missing (WKWebView cannot set headers on top-level location). Tidy provisional navigation log; add -1200 hint for TLS on LAN (dev). DEBUG-only path. Made-with: Cursor --- .../ios/App/App/DevBridgeViewController.swift | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift index ed63c23fed..26f4d297f2 100644 --- a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift +++ b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift @@ -90,6 +90,8 @@ extension DevBridgeViewController: WKScriptMessageHandler { } #if DEBUG +private let ngrokSkipHeaderField = "ngrok-skip-browser-warning" + extension DevBridgeViewController: WKNavigationDelegate { func webView( _ webView: WKWebView, @@ -99,6 +101,32 @@ extension DevBridgeViewController: WKNavigationDelegate { if let url = navigationAction.request.url { print("[DevBridge] Navigation request to: \(url.absoluteString)") } + + // Free tier ngrok interstitial cannot be dismissed in WKWebView. Reload with skip header. + if let requestURL = navigationAction.request.url, let host = requestURL.host, + host.contains("ngrok") + { + let existing = navigationAction.request.value( + forHTTPHeaderField: ngrokSkipHeaderField + ) ?? "" + if existing != "true" && existing != "1" { + var retry = URLRequest( + url: requestURL, + cachePolicy: .reloadIgnoringLocalCacheData, + timeoutInterval: 60.0 + ) + retry.setValue("true", forHTTPHeaderField: ngrokSkipHeaderField) + if let ref = navigationAction.request.mainDocumentURL { + print( + "[DevBridge] ngrok: injecting skip interstitial header for: \(ref.absoluteString)" + ) + } + decisionHandler(.cancel) + webView.load(retry) + return + } + } + decisionHandler(.allow) } @@ -106,7 +134,9 @@ extension DevBridgeViewController: WKNavigationDelegate { _ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation! ) { - print("[DevBridge] Started provisional navigation") + print( + "[DevBridge] didStartProvisional webView.url=\(webView.url?.absoluteString ?? "nil")" + ) } func webView( @@ -155,6 +185,11 @@ extension DevBridgeViewController: WKNavigationDelegate { "[DevBridge] Timeout error - check if Vite server is running and accessible." ) } + if nsError.code == -1200 { + print( + "[DevBridge] TLS failed (-1200). If the URL uses your Mac LAN IP, add `, https://:8443` to the Caddy site line in dev/caddy/Caddyfile, restart Caddy, and see dev/caddy/README (iOS section)." + ) + } } } From 2ee5898bbbd6ea2beaac5b807e309c30873833d5 Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 27 Apr 2026 21:19:34 +0800 Subject: [PATCH 05/28] fix(pocket): open Google auth in system session Use ASWebAuthenticationSession with an app-owned callback on iOS so Google OAuth no longer runs inside the Capacitor WKWebView. Made-with: Cursor --- apps/server/src/libs/auth.ts | 5 +- apps/server/src/libs/tests/auth.test.ts | 1 + .../ios/App/App.xcodeproj/project.pbxproj | 4 + .../ios/App/App/AiriNativeAuthPlugin.swift | 78 +++++++++++++++++++ .../ios/App/App/DevBridgeViewController.swift | 1 + apps/stage-pocket/ios/App/App/Info.plist | 11 +++ .../stage-ui/src/libs/auth-callback.test.ts | 59 ++++++++++++++ packages/stage-ui/src/libs/auth-callback.ts | 42 ++++++++++ .../stage-ui/src/libs/auth-config.test.ts | 54 +++++++++++++ packages/stage-ui/src/libs/auth-config.ts | 24 +++++- packages/stage-ui/src/libs/auth.ts | 37 +++++---- .../stage-ui/src/libs/native-auth.test.ts | 31 ++++++++ packages/stage-ui/src/libs/native-auth.ts | 22 ++++++ 13 files changed, 351 insertions(+), 18 deletions(-) create mode 100644 apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift create mode 100644 packages/stage-ui/src/libs/auth-callback.test.ts create mode 100644 packages/stage-ui/src/libs/auth-callback.ts create mode 100644 packages/stage-ui/src/libs/auth-config.test.ts create mode 100644 packages/stage-ui/src/libs/native-auth.test.ts create mode 100644 packages/stage-ui/src/libs/native-auth.ts diff --git a/apps/server/src/libs/auth.ts b/apps/server/src/libs/auth.ts index de10e72efa..34d2580770 100644 --- a/apps/server/src/libs/auth.ts +++ b/apps/server/src/libs/auth.ts @@ -169,13 +169,16 @@ function buildTrustedClientSeeds(env: Env): TrustedClientSeed[] { }) // Capacitor mobile app — public client (no secret, PKCE only). - // Same reasoning as Web: native WebView cannot safely store secrets. + // Uses an app-owned callback scheme so ASWebAuthenticationSession can hand + // the authorization response back to iOS Pocket outside the WKWebView. + // Keep the Capacitor WebView callback until Android has its native launcher. clients.push({ clientId: OIDC_CLIENT_ID_POCKET, name: 'AIRI Stage Mobile', type: 'native', public: true, redirectUris: [ + 'airi-pocket://auth/callback', 'capacitor://localhost/auth/callback', 'ai.moeru.airi-pocket://links/auth/callback', ], diff --git a/apps/server/src/libs/tests/auth.test.ts b/apps/server/src/libs/tests/auth.test.ts index 41d557990e..9607e3b787 100644 --- a/apps/server/src/libs/tests/auth.test.ts +++ b/apps/server/src/libs/tests/auth.test.ts @@ -103,6 +103,7 @@ describe('seedTrustedClients', () => { expect(pocketClient.public).toBe(true) expect(pocketClient.tokenEndpointAuthMethod).toBe('none') expect(pocketClient.redirectUris).toEqual([ + 'airi-pocket://auth/callback', 'capacitor://localhost/auth/callback', 'ai.moeru.airi-pocket://links/auth/callback', ]) diff --git a/apps/stage-pocket/ios/App/App.xcodeproj/project.pbxproj b/apps/stage-pocket/ios/App/App.xcodeproj/project.pbxproj index 0d79301cc9..fd647a2c7e 100644 --- a/apps/stage-pocket/ios/App/App.xcodeproj/project.pbxproj +++ b/apps/stage-pocket/ios/App/App.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; + A1B2C3D42F10000100AA0007 /* AiriNativeAuthPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D42F10000100AA0008 /* AiriNativeAuthPlugin.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -38,6 +39,7 @@ 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; 958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; }; + A1B2C3D42F10000100AA0008 /* AiriNativeAuthPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiriNativeAuthPlugin.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,6 +74,7 @@ 504EC3061FED79650016851F /* App */ = { isa = PBXGroup; children = ( + A1B2C3D42F10000100AA0008 /* AiriNativeAuthPlugin.swift */, 29ABB4B92F03F2B400285F7F /* DevBridgeViewController.swift */, A1B2C3D42F10000100AA0002 /* HostWebSocketBridge.swift */, A1B2C3D42F10000100AA0004 /* URLSessionHostWebSocketSession.swift */, @@ -171,6 +174,7 @@ buildActionMask = 2147483647; files = ( 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, + A1B2C3D42F10000100AA0007 /* AiriNativeAuthPlugin.swift in Sources */, 29ABB4BA2F03F2B400285F7F /* DevBridgeViewController.swift in Sources */, A1B2C3D42F10000100AA0001 /* HostWebSocketBridge.swift in Sources */, A1B2C3D42F10000100AA0003 /* URLSessionHostWebSocketSession.swift in Sources */, diff --git a/apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift b/apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift new file mode 100644 index 0000000000..3b01741b57 --- /dev/null +++ b/apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift @@ -0,0 +1,78 @@ +import AuthenticationServices +import Capacitor +import Foundation +import UIKit + +@objc(AiriNativeAuthPlugin) +class AiriNativeAuthPlugin: CAPPlugin, CAPBridgedPlugin, ASWebAuthenticationPresentationContextProviding { + let identifier = "AiriNativeAuthPlugin" + let jsName = "AiriNativeAuth" + + let pluginMethods: [CAPPluginMethod] = [ + CAPPluginMethod(name: "authenticate", returnType: CAPPluginReturnPromise) + ] + + private var session: ASWebAuthenticationSession? + + @objc func authenticate(_ call: CAPPluginCall) { + guard let urlString = call.getString("url"), + let callbackScheme = call.getString("callbackScheme") + else { + call.reject("MISSING_ARGUMENTS", "url and callbackScheme are required") + return + } + + guard let url = URL(string: urlString) else { + call.reject("INVALID_URL", "url must be a valid URL") + return + } + + DispatchQueue.main.async { + guard self.session == nil else { + call.reject("AUTH_IN_PROGRESS", "An authentication session is already in progress") + return + } + + let session = ASWebAuthenticationSession( + url: url, + callbackURLScheme: callbackScheme + ) { [weak self] callbackURL, error in + self?.session = nil + + if let callbackURL { + call.resolve(["callbackUrl": callbackURL.absoluteString]) + return + } + + if let authError = error as? ASWebAuthenticationSessionError, + authError.code == .canceledLogin + { + call.reject("USER_CANCELLED", "The authentication session was cancelled") + return + } + + call.reject("AUTH_FAILED", error?.localizedDescription ?? "Authentication failed") + } + + session.presentationContextProvider = self + session.prefersEphemeralWebBrowserSession = false + self.session = session + + if !session.start() { + self.session = nil + call.reject("AUTH_START_FAILED", "Failed to start authentication session") + } + } + } + + func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + if let window = bridge?.viewController?.view.window { + return window + } + + return UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .flatMap(\.windows) + .first { $0.isKeyWindow } ?? ASPresentationAnchor() + } +} diff --git a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift index 26f4d297f2..059fe3db09 100644 --- a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift +++ b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift @@ -14,6 +14,7 @@ class DevBridgeViewController: CAPBridgeViewController { override func capacitorDidLoad() { super.capacitorDidLoad() + bridge?.registerPluginInstance(AiriNativeAuthPlugin()) configureTransparentBackground() webView?.allowsBackForwardNavigationGestures = true installWebSocketBridge() diff --git a/apps/stage-pocket/ios/App/App/Info.plist b/apps/stage-pocket/ios/App/App/Info.plist index ed4385713d..33d7b39d43 100644 --- a/apps/stage-pocket/ios/App/App/Info.plist +++ b/apps/stage-pocket/ios/App/App/Info.plist @@ -18,6 +18,17 @@ $(PRODUCT_NAME) CFBundlePackageType APPL + CFBundleURLTypes + + + CFBundleURLName + ai.moeru.airi-pocket.auth + CFBundleURLSchemes + + airi-pocket + + + CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion diff --git a/packages/stage-ui/src/libs/auth-callback.test.ts b/packages/stage-ui/src/libs/auth-callback.test.ts new file mode 100644 index 0000000000..e7e56873d7 --- /dev/null +++ b/packages/stage-ui/src/libs/auth-callback.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it, vi } from 'vitest' + +import { completeOIDCCallbackUrl } from './auth-callback' + +describe('completeOIDCCallbackUrl', () => { + it('exchanges the native callback authorization code with the persisted PKCE flow', async () => { + const tokens = { + access_token: 'access-token', + token_type: 'Bearer', + expires_in: 3600, + refresh_token: 'refresh-token', + } + const flowState = { + codeVerifier: 'verifier', + state: 'expected-state', + } + const params = { + clientId: 'airi-stage-pocket', + redirectUri: 'airi-pocket://auth/callback', + provider: 'google' as const, + } + const exchangeCodeForTokens = vi.fn().mockResolvedValue(tokens) + const applyOIDCTokens = vi.fn().mockResolvedValue(undefined) + const fetchSession = vi.fn().mockResolvedValue(true) + + await completeOIDCCallbackUrl('airi-pocket://auth/callback?code=auth-code&state=expected-state', { + consumeFlowState: () => ({ flowState, params }), + exchangeCodeForTokens, + applyOIDCTokens, + fetchSession, + }) + + expect(exchangeCodeForTokens).toHaveBeenCalledWith('auth-code', flowState, params, 'expected-state') + expect(applyOIDCTokens).toHaveBeenCalledWith(tokens, 'airi-stage-pocket') + expect(fetchSession).toHaveBeenCalledOnce() + }) + + it('rejects an OAuth error callback before exchanging tokens', async () => { + const exchangeCodeForTokens = vi.fn() + + await expect(completeOIDCCallbackUrl('airi-pocket://auth/callback?error=access_denied&error_description=User%20cancelled', { + consumeFlowState: () => null, + exchangeCodeForTokens, + applyOIDCTokens: vi.fn(), + fetchSession: vi.fn(), + })).rejects.toThrow('User cancelled') + + expect(exchangeCodeForTokens).not.toHaveBeenCalled() + }) + + it('rejects a callback when the persisted PKCE flow is missing', async () => { + await expect(completeOIDCCallbackUrl('airi-pocket://auth/callback?code=auth-code&state=expected-state', { + consumeFlowState: () => null, + exchangeCodeForTokens: vi.fn(), + applyOIDCTokens: vi.fn(), + fetchSession: vi.fn(), + })).rejects.toThrow('Missing OIDC flow state') + }) +}) diff --git a/packages/stage-ui/src/libs/auth-callback.ts b/packages/stage-ui/src/libs/auth-callback.ts new file mode 100644 index 0000000000..7fcc57d36d --- /dev/null +++ b/packages/stage-ui/src/libs/auth-callback.ts @@ -0,0 +1,42 @@ +import type { OIDCFlowParams, OIDCFlowState, TokenResponse } from './auth-oidc' + +export interface OIDCCallbackCompletionHandlers { + consumeFlowState: () => { flowState: OIDCFlowState, params: OIDCFlowParams } | null + exchangeCodeForTokens: ( + code: string, + flowState: OIDCFlowState, + params: OIDCFlowParams, + returnedState: string, + ) => Promise + applyOIDCTokens: (tokens: TokenResponse, clientId: string) => Promise + fetchSession: () => Promise +} + +export async function completeOIDCCallbackUrl( + callbackUrl: string, + handlers: OIDCCallbackCompletionHandlers, +): Promise { + const url = new URL(callbackUrl) + const code = url.searchParams.get('code') + const state = url.searchParams.get('state') + const errorParam = url.searchParams.get('error') + + if (errorParam) + throw new Error(url.searchParams.get('error_description') ?? errorParam) + + if (!code || !state) + throw new Error('Missing OIDC code or state') + + const persisted = handlers.consumeFlowState() + if (!persisted) + throw new Error('Missing OIDC flow state') + + const tokens = await handlers.exchangeCodeForTokens( + code, + persisted.flowState, + persisted.params, + state, + ) + await handlers.applyOIDCTokens(tokens, persisted.params.clientId) + await handlers.fetchSession() +} diff --git a/packages/stage-ui/src/libs/auth-config.test.ts b/packages/stage-ui/src/libs/auth-config.test.ts new file mode 100644 index 0000000000..7d7536b6a8 --- /dev/null +++ b/packages/stage-ui/src/libs/auth-config.test.ts @@ -0,0 +1,54 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +let nativePlatform = false +let platform = 'web' + +vi.mock('@capacitor/core', () => ({ + Capacitor: { + getPlatform: () => platform, + isNativePlatform: () => nativePlatform, + }, + registerPlugin: () => ({ + authenticate: vi.fn(), + }), +})) + +describe('oIDC client config', () => { + beforeEach(() => { + nativePlatform = false + platform = 'web' + vi.resetModules() + vi.stubGlobal('window', { + location: { + origin: 'https://airi.moeru.ai', + }, + }) + }) + + it('uses the web redirect URI in browsers', async () => { + const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } = await import('./auth-config') + + expect(OIDC_CLIENT_ID).toBe('airi-stage-web') + expect(OIDC_REDIRECT_URI).toBe('https://airi.moeru.ai/auth/callback') + }) + + it('uses the app-owned Pocket redirect URI on native platforms', async () => { + nativePlatform = true + platform = 'ios' + + const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } = await import('./auth-config') + + expect(OIDC_CLIENT_ID).toBe('airi-stage-pocket') + expect(OIDC_REDIRECT_URI).toBe('airi-pocket://auth/callback') + }) + + it('keeps the existing Capacitor callback for Android until an Android launcher exists', async () => { + nativePlatform = true + platform = 'android' + + const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } = await import('./auth-config') + + expect(OIDC_CLIENT_ID).toBe('airi-stage-pocket') + expect(OIDC_REDIRECT_URI).toBe('capacitor://localhost/auth/callback') + }) +}) diff --git a/packages/stage-ui/src/libs/auth-config.ts b/packages/stage-ui/src/libs/auth-config.ts index b00401fdae..90df676971 100644 --- a/packages/stage-ui/src/libs/auth-config.ts +++ b/packages/stage-ui/src/libs/auth-config.ts @@ -1,5 +1,20 @@ +import { Capacitor } from '@capacitor/core' + +import { NATIVE_AUTH_REDIRECT_URI } from './native-auth' + const FALLBACK = 'http://localhost' +const CAPACITOR_WEBVIEW_REDIRECT_URI = 'capacitor://localhost/auth/callback' + +function getCapacitorPlatform(): string { + try { + return Capacitor.getPlatform() + } + catch { + return 'web' + } +} + /** * Safely retrieves environment status without crashing in non-browser runtimes. */ @@ -46,8 +61,15 @@ function getRedirectOrigin() { const { isNative } = getEnvStatus() const origin = getRedirectOrigin() +const capacitorPlatform = getCapacitorPlatform() export const OIDC_CLIENT_ID = import.meta.env.VITE_OIDC_CLIENT_ID || (isNative ? 'airi-stage-pocket' : 'airi-stage-web') -export const OIDC_REDIRECT_URI = `${origin}/auth/callback` +export const OIDC_REDIRECT_URI = import.meta.env.VITE_OIDC_REDIRECT_URI + ? `${origin}/auth/callback` + : capacitorPlatform === 'ios' + ? NATIVE_AUTH_REDIRECT_URI + : capacitorPlatform !== 'web' + ? CAPACITOR_WEBVIEW_REDIRECT_URI + : `${origin}/auth/callback` diff --git a/packages/stage-ui/src/libs/auth.ts b/packages/stage-ui/src/libs/auth.ts index 1fa1734d5d..68df9febe2 100644 --- a/packages/stage-ui/src/libs/auth.ts +++ b/packages/stage-ui/src/libs/auth.ts @@ -4,8 +4,10 @@ import { Capacitor } from '@capacitor/core' import { createAuthClient } from 'better-auth/vue' import { useAuthStore } from '../stores/auth' +import { completeOIDCCallbackUrl } from './auth-callback' import { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } from './auth-config' -import { buildAuthorizationURL, persistFlowState } from './auth-oidc' +import { buildAuthorizationURL, consumeFlowState, exchangeCodeForTokens, persistFlowState } from './auth-oidc' +import { openNativeAuthSession } from './native-auth' import { isNgrokServerUrl, SERVER_URL } from './server' export type OAuthProvider = 'google' | 'github' @@ -191,30 +193,33 @@ export async function signInOIDC(params: OIDCFlowParams) { const { url, flowState } = await buildAuthorizationURL(params) persistFlowState(flowState, params) - if (!params.provider) { - window.location.href = url - return - } - - // better-auth's signIn.social() uses the fetch client; in Capacitor WKWebView - // that path often does not perform a real top-level navigation to the IdP. - // Match server /sign-in: full-page GET to social, then 302 to Google/GitHub. - let useFullPageSocial = false + let capacitorPlatform = 'web' try { - useFullPageSocial = Capacitor.isNativePlatform() + capacitorPlatform = Capacitor.getPlatform() } catch { // no Capacitor } - if (useFullPageSocial) { - // NOTICE: First request must be /api/auth/oauth2/authorize (not /sign-in/social): - // `ensureDynamicFirstPartyRedirectUri` only runs on authorize, so the LAN - // `redirect_uri` is written to `oauth_client` before login/social. Skipping - // authorize left social with an unregistered redirect and no Google 302. + if (capacitorPlatform === 'ios') { + const callbackUrl = await openNativeAuthSession(url.toString()) + await completeOIDCCallbackUrl(callbackUrl, { + consumeFlowState, + exchangeCodeForTokens, + applyOIDCTokens, + fetchSession, + }) + return + } + if (capacitorPlatform !== 'web') { window.location.assign(url.toString()) return } + if (!params.provider) { + window.location.href = url + return + } + await authClient.signIn.social({ provider: params.provider, callbackURL: url.toString(), diff --git a/packages/stage-ui/src/libs/native-auth.test.ts b/packages/stage-ui/src/libs/native-auth.test.ts new file mode 100644 index 0000000000..900922bee0 --- /dev/null +++ b/packages/stage-ui/src/libs/native-auth.test.ts @@ -0,0 +1,31 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +let nativeAuthenticate: ReturnType + +vi.mock('@capacitor/core', () => ({ + registerPlugin: () => ({ + authenticate: nativeAuthenticate, + }), +})) + +describe('openNativeAuthSession', () => { + beforeEach(() => { + nativeAuthenticate = vi.fn().mockResolvedValue({ + callbackUrl: 'airi-pocket://auth/callback?code=auth-code&state=state', + }) + vi.resetModules() + }) + + it('opens an ASWebAuthenticationSession with the Pocket callback scheme', async () => { + const { NATIVE_AUTH_CALLBACK_SCHEME, openNativeAuthSession } = await import('./native-auth') + + const callbackUrl = await openNativeAuthSession('https://api.airi.build/api/auth/oauth2/authorize') + + expect(NATIVE_AUTH_CALLBACK_SCHEME).toBe('airi-pocket') + expect(callbackUrl).toBe('airi-pocket://auth/callback?code=auth-code&state=state') + expect(nativeAuthenticate).toHaveBeenCalledWith({ + url: 'https://api.airi.build/api/auth/oauth2/authorize', + callbackScheme: 'airi-pocket', + }) + }) +}) diff --git a/packages/stage-ui/src/libs/native-auth.ts b/packages/stage-ui/src/libs/native-auth.ts new file mode 100644 index 0000000000..b28654e6ae --- /dev/null +++ b/packages/stage-ui/src/libs/native-auth.ts @@ -0,0 +1,22 @@ +import { registerPlugin } from '@capacitor/core' + +export const NATIVE_AUTH_CALLBACK_SCHEME = 'airi-pocket' +export const NATIVE_AUTH_REDIRECT_URI = `${NATIVE_AUTH_CALLBACK_SCHEME}://auth/callback` + +export interface NativeAuthPlugin { + authenticate: (options: { + url: string + callbackScheme: string + }) => Promise<{ callbackUrl: string }> +} + +const nativeAuth: NativeAuthPlugin = registerPlugin('AiriNativeAuth') + +export async function openNativeAuthSession(url: string): Promise { + const result = await nativeAuth.authenticate({ + url, + callbackScheme: NATIVE_AUTH_CALLBACK_SCHEME, + }) + + return result.callbackUrl +} From 776cdf5db04650b81173f01ac72ffac36611b4f0 Mon Sep 17 00:00:00 2001 From: Lulu Date: Mon, 27 Apr 2026 22:22:26 +0800 Subject: [PATCH 06/28] chore(pocket-ios): remove DEBUG ngrok interstitial workaround in DevBridge Drop WKNavigationDelegate branch that reloaded ngrok hosts with ngrok-skip-browser-warning; API requests still add the header in stage-ui when SERVER_URL is ngrok. Made-with: Cursor --- .../ios/App/App/DevBridgeViewController.swift | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift index 059fe3db09..7d3ae525aa 100644 --- a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift +++ b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift @@ -91,8 +91,6 @@ extension DevBridgeViewController: WKScriptMessageHandler { } #if DEBUG -private let ngrokSkipHeaderField = "ngrok-skip-browser-warning" - extension DevBridgeViewController: WKNavigationDelegate { func webView( _ webView: WKWebView, @@ -103,31 +101,6 @@ extension DevBridgeViewController: WKNavigationDelegate { print("[DevBridge] Navigation request to: \(url.absoluteString)") } - // Free tier ngrok interstitial cannot be dismissed in WKWebView. Reload with skip header. - if let requestURL = navigationAction.request.url, let host = requestURL.host, - host.contains("ngrok") - { - let existing = navigationAction.request.value( - forHTTPHeaderField: ngrokSkipHeaderField - ) ?? "" - if existing != "true" && existing != "1" { - var retry = URLRequest( - url: requestURL, - cachePolicy: .reloadIgnoringLocalCacheData, - timeoutInterval: 60.0 - ) - retry.setValue("true", forHTTPHeaderField: ngrokSkipHeaderField) - if let ref = navigationAction.request.mainDocumentURL { - print( - "[DevBridge] ngrok: injecting skip interstitial header for: \(ref.absoluteString)" - ) - } - decisionHandler(.cancel) - webView.load(retry) - return - } - } - decisionHandler(.allow) } From 03d083a7aa8504c86b8646098eec36c38741b6e7 Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 28 Apr 2026 18:05:25 +0800 Subject: [PATCH 07/28] fix(server): resolve JWKS locally for token auth Made-with: Cursor --- apps/server/src/libs/request-auth.ts | 4 +++- apps/server/src/libs/tests/request-auth.test.ts | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/server/src/libs/request-auth.ts b/apps/server/src/libs/request-auth.ts index a06a68a9be..f96fe9c551 100644 --- a/apps/server/src/libs/request-auth.ts +++ b/apps/server/src/libs/request-auth.ts @@ -83,8 +83,10 @@ let cachedJWKS: ReturnType | null = null function getJWKS(env: Env): ReturnType { if (!cachedJWKS) { + // Use the local listener for this server's own JWKS so development tunnels + // such as ngrok are not involved in request-time token verification. cachedJWKS = createRemoteJWKSet( - new URL('/api/auth/jwks', env.API_SERVER_URL), + new URL('/api/auth/jwks', `http://127.0.0.1:${env.PORT}`), ) } return cachedJWKS diff --git a/apps/server/src/libs/tests/request-auth.test.ts b/apps/server/src/libs/tests/request-auth.test.ts index dc15379392..ec6dd09d52 100644 --- a/apps/server/src/libs/tests/request-auth.test.ts +++ b/apps/server/src/libs/tests/request-auth.test.ts @@ -9,10 +9,13 @@ vi.mock('jose', () => ({ })) const { jwtVerify } = await import('jose') +const { createRemoteJWKSet } = await import('jose') const mockedJwtVerify = vi.mocked(jwtVerify) +const mockedCreateRemoteJWKSet = vi.mocked(createRemoteJWKSet) const mockEnv = { API_SERVER_URL: 'http://localhost:3000', + PORT: 3000, TEST_AUTH_TOKEN: '', TEST_AUTH_USER_ID: 'test-user', TEST_AUTH_USER_EMAIL: 'test@example.com', @@ -116,6 +119,7 @@ describe('resolveRequestAuth', () => { new Headers({ Authorization: 'Bearer eyJhbGciOiJSUzI1NiJ9.test.sig' }), ) + expect(mockedCreateRemoteJWKSet).toHaveBeenCalledWith(new URL('http://127.0.0.1:3000/api/auth/jwks')) expect(result).toEqual({ user, session: { From e37941200aed8d7d03c5884636dec05cc0eea360 Mon Sep 17 00:00:00 2001 From: Lulu Date: Thu, 30 Apr 2026 11:52:26 +0800 Subject: [PATCH 08/28] docs(dev): document ngrok for local HTTPS API Replace the reverted Caddy workflow with ngrok-oriented guidance in apps/server README. Update DevBridge TLS hint to match (no dev/caddy path). Made-with: Cursor --- apps/server/README.md | 4 ++++ apps/stage-pocket/ios/App/App/DevBridgeViewController.swift | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/server/README.md b/apps/server/README.md index 1e86ba0d0f..0e25a869dc 100644 --- a/apps/server/README.md +++ b/apps/server/README.md @@ -53,3 +53,7 @@ When the mobile dev server uses a non-localhost origin (for example `https://10. `ADDITIONAL_TRUSTED_ORIGINS=https://10.0.0.129:5273,https://198.18.0.1:5273` Restart the API server after changing this variable. + +## Local HTTPS API (ngrok; no TLS in Node) + +When the client runs on **https** (e.g. Vite `dev:https` or Capacitor) while Hono stays on **`http://127.0.0.1:3000`**, WebKit can treat **`https` → `http` API** calls as mixed content. Use an **https** tunnel that forwards to this API (typical: [ngrok](https://ngrok.com/) http → `127.0.0.1:3000`) and point **`API_SERVER_URL`** / the frontend **`VITE_SERVER_URL`** (or equivalent `SERVER_URL`) at that **https** base URL. Stage UI sends `ngrok-skip-browser-warning` on requests when the host looks like ngrok so the free-tier interstitial does not block API traffic. diff --git a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift index 7d3ae525aa..389c390405 100644 --- a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift +++ b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift @@ -161,7 +161,7 @@ extension DevBridgeViewController: WKNavigationDelegate { } if nsError.code == -1200 { print( - "[DevBridge] TLS failed (-1200). If the URL uses your Mac LAN IP, add `, https://:8443` to the Caddy site line in dev/caddy/Caddyfile, restart Caddy, and see dev/caddy/README (iOS section)." + "[DevBridge] TLS failed (-1200). Prefer an https URL the device trusts (e.g. ngrok to the dev API), or use http for local Vite when policy allows; see apps/server/README.md (Local HTTPS API)." ) } } From b1f340543e35820987bf637cb7677c412f78abd7 Mon Sep 17 00:00:00 2001 From: Lulu Date: Thu, 30 Apr 2026 16:18:29 +0800 Subject: [PATCH 09/28] chore(stage-ui): sort imports in auth.ts after rebase Made-with: Cursor Co-authored-by: Cursor From 62fdde7915b4449c04a86afb3a3688e21a219b22 Mon Sep 17 00:00:00 2001 From: Lulu Date: Fri, 1 May 2026 21:01:58 +0800 Subject: [PATCH 10/28] Revert "fix(stage-layouts): remove HeaderAvatar dropdown Transition for Capacitor WebView" This reverts commit 2a5247f4bdaec795be686ee2dd0935daff714d00. --- .../src/components/Layouts/HeaderAvatar.vue | 107 ++++++++++-------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue b/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue index 1cb4ad8573..f064cec818 100644 --- a/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue +++ b/packages/stage-layouts/src/components/Layouts/HeaderAvatar.vue @@ -91,62 +91,71 @@ onClickOutside(dropdownRef, () => { /> -
-
-

- Signed in as -

-

- {{ userName }} -

-
-
- {{ formattedCredits }} Flux +
+
+

+ Signed in as +

+

+ {{ userName }} +

+
+
+ {{ formattedCredits }} Flux +
-
-
- -
- Profile - +
+ +
+ Profile + - -
- Flux - + +
+ Flux + - -
- Settings - -
+ +
+ Settings + +
-
- +
+ +
-
+
From b549322e505331e5e51e415ca25cc8d5456e8f4b Mon Sep 17 00:00:00 2001 From: Lulu Date: Fri, 8 May 2026 19:00:34 +0800 Subject: [PATCH 11/28] fix(stage-ui): derive native OIDC client id from Capacitor platform Restores parity with auth-config tests when window.Capacitor is unset but @capacitor/core is mocked (Vitest). Co-authored-by: Cursor --- packages/stage-ui/src/libs/auth-config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/stage-ui/src/libs/auth-config.ts b/packages/stage-ui/src/libs/auth-config.ts index 90df676971..d60d9f015e 100644 --- a/packages/stage-ui/src/libs/auth-config.ts +++ b/packages/stage-ui/src/libs/auth-config.ts @@ -63,8 +63,10 @@ const { isNative } = getEnvStatus() const origin = getRedirectOrigin() const capacitorPlatform = getCapacitorPlatform() +const usesNativeOidcClient = capacitorPlatform !== 'web' || isNative + export const OIDC_CLIENT_ID = import.meta.env.VITE_OIDC_CLIENT_ID - || (isNative ? 'airi-stage-pocket' : 'airi-stage-web') + || (usesNativeOidcClient ? 'airi-stage-pocket' : 'airi-stage-web') export const OIDC_REDIRECT_URI = import.meta.env.VITE_OIDC_REDIRECT_URI ? `${origin}/auth/callback` From 7420c5dbede1442e1ea10d50f3af6d05737a5f9a Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 9 May 2026 00:18:16 +0800 Subject: [PATCH 12/28] fix(ui-server-auth): alias @capacitor/core for Vite build with stage-ui Rolldown could not resolve @capacitor/core from stage-ui imports; stub Capacitor/registerPlugin for the browser-only auth bundle. Co-authored-by: Cursor --- .../src/shims/capacitor-core.ts | 23 +++++++++++++++++++ apps/ui-server-auth/vite.config.ts | 1 + 2 files changed, 24 insertions(+) create mode 100644 apps/ui-server-auth/src/shims/capacitor-core.ts diff --git a/apps/ui-server-auth/src/shims/capacitor-core.ts b/apps/ui-server-auth/src/shims/capacitor-core.ts new file mode 100644 index 0000000000..ffd919a0d6 --- /dev/null +++ b/apps/ui-server-auth/src/shims/capacitor-core.ts @@ -0,0 +1,23 @@ +/** + * Vite-only stub: ui-server-auth runs in a normal browser bundle. `@proj-airi/stage-ui` + * imports `@capacitor/core` for optional native runtime checks; those paths are dead + * here but must resolve for Rolldown. + */ +export const Capacitor = { + getPlatform(): string { + return 'web' + }, +} + +/** + * `native-auth.ts` calls `registerPlugin` at module load; ui-server-auth never invokes + * the Pocket native bridge — return a typed no-op implementation. + */ +export function registerPlugin(_name: string): T { + return { + authenticate: async () => { + throw new Error('AiriNativeAuth is not available in the ui-server-auth bundle') + }, + debugLog: async () => {}, + } as T +} diff --git a/apps/ui-server-auth/vite.config.ts b/apps/ui-server-auth/vite.config.ts index 52623ab560..57a84b0040 100644 --- a/apps/ui-server-auth/vite.config.ts +++ b/apps/ui-server-auth/vite.config.ts @@ -23,6 +23,7 @@ export default defineConfig({ resolve: { alias: { + '@capacitor/core': resolve(join(import.meta.dirname, 'src', 'shims', 'capacitor-core.ts')), '@proj-airi/i18n': resolve(join(import.meta.dirname, '..', '..', 'packages', 'i18n', 'src')), '@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src')), '@proj-airi/stage-shared': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-shared', 'src')), From 4048dff42f3aa9e3bd493cbee0c5276a0c512457 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 9 May 2026 00:18:27 +0800 Subject: [PATCH 13/28] feat(stage-ui): start OIDC from needsLogin via triggerSignIn - Auth store: replace router push to /auth/sign-in with dynamic triggerSignIn import (avoids cycle with libs/auth). - stage-pocket auth callback: retry uses triggerSignIn; use errorMessageFrom for exchange errors. Co-authored-by: Cursor --- apps/stage-pocket/src/pages/auth/callback.vue | 7 ++++--- packages/stage-ui/src/stores/auth.ts | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/apps/stage-pocket/src/pages/auth/callback.vue b/apps/stage-pocket/src/pages/auth/callback.vue index 4add31dcdd..e2ee1ac9c4 100644 --- a/apps/stage-pocket/src/pages/auth/callback.vue +++ b/apps/stage-pocket/src/pages/auth/callback.vue @@ -1,5 +1,6 @@ diff --git a/packages/stage-ui/src/stores/auth.ts b/packages/stage-ui/src/stores/auth.ts index e0a05c3439..52826000f3 100644 --- a/packages/stage-ui/src/stores/auth.ts +++ b/packages/stage-ui/src/stores/auth.ts @@ -7,7 +7,6 @@ import { computed, ref, watch } from 'vue' import { client } from '../composables/api' import { useBreakpoints } from '../composables/use-breakpoints' -import { triggerSignIn } from '../libs/auth' import { refreshAccessToken } from '../libs/auth-oidc' /** @@ -40,8 +39,9 @@ export const useAuthStore = defineStore('auth', () => { const credits = useLocalStorage('user/v1/flux', 0) - // Cross-app "user must log in" flag. Setting this to true triggers an - // immediate OIDC redirect on web (mobile + desktop). Electron skips this + // Cross-app "user must log in" flag. Setting this to true starts the OIDC + // flow (`triggerSignIn`) so the browser hits `/api/auth/oauth2/authorize` + // and the server-hosted login UI (`/auth/sign-in`). Electron skips this // path because controls-island-auth-button listens for IPC and handles // sign-in in the main process. const needsLogin = ref(false) @@ -50,7 +50,17 @@ export const useAuthStore = defineStore('auth', () => { whenever(needsLogin, async () => { if (isStageTamagotchi()) return - await triggerSignIn() + try { + // Dynamic import avoids a circular dependency: `libs/auth` imports this store. + const { triggerSignIn } = await import('../libs/auth') + await triggerSignIn() + } + catch { + // User cancelled native sheet or transient network failure; flag cleared in `finally`. + } + finally { + needsLogin.value = false + } }) // Reset the flag if the viewport class flips, so a stale needsLogin from a From cfd4013a56b3f5f623156d4097270869e2e9a1bd Mon Sep 17 00:00:00 2001 From: Lulu Date: Sat, 9 May 2026 00:26:29 +0800 Subject: [PATCH 14/28] chore(stage-pocket): remove local auth sign-in page Login uses triggerSignIn / server-hosted ui-server-auth; pocket no longer needs /auth/sign-in route. Co-authored-by: Cursor --- apps/stage-pocket/src/pages/auth/sign-in.vue | 107 ------------------- 1 file changed, 107 deletions(-) delete mode 100644 apps/stage-pocket/src/pages/auth/sign-in.vue diff --git a/apps/stage-pocket/src/pages/auth/sign-in.vue b/apps/stage-pocket/src/pages/auth/sign-in.vue deleted file mode 100644 index 1e4f42bd11..0000000000 --- a/apps/stage-pocket/src/pages/auth/sign-in.vue +++ /dev/null @@ -1,107 +0,0 @@ - - - From 4bad1ddf19b42421952e3c27ff5a411c7f92c120 Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 19 May 2026 23:23:41 +0800 Subject: [PATCH 15/28] fix(stage-ui): declare @capacitor/core for shared auth imports pnpm strict resolution requires Capacitor where stage-ui imports it for OIDC and native auth helpers. Co-authored-by: Cursor --- packages/stage-ui/package.json | 1 + pnpm-lock.yaml | 279 +++++---------------------------- 2 files changed, 37 insertions(+), 243 deletions(-) diff --git a/packages/stage-ui/package.json b/packages/stage-ui/package.json index 4ca6866536..6132e70295 100644 --- a/packages/stage-ui/package.json +++ b/packages/stage-ui/package.json @@ -62,6 +62,7 @@ "test:run": "vitest run" }, "dependencies": { + "@capacitor/core": "catalog:", "@date-fns/utc": "catalog:", "@formkit/auto-animate": "catalog:", "@huggingface/transformers": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5d520c634..657d65da8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1851,7 +1851,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/cap-vite': specifier: workspace:* version: link:../../packages/cap-vite @@ -1914,16 +1914,16 @@ importers: version: 4.6.4 unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' - version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@2.80.0) + version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -1938,7 +1938,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -2260,7 +2260,7 @@ importers: version: 3.0.2(electron@41.2.1) '@electron-toolkit/tsconfig': specifier: 'catalog:' - version: 2.0.0(@types/node@24.12.2) + version: 2.0.0(@types/node@25.6.0) '@electron-toolkit/utils': specifier: 'catalog:' version: 4.0.0(electron@41.2.1) @@ -2299,7 +2299,7 @@ importers: version: 3.1.0 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@modelcontextprotocol/sdk': specifier: 'catalog:' version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) @@ -2335,10 +2335,10 @@ importers: version: link:../../packages/ui-transitions '@proj-airi/unplugin-fetch': specifier: 'catalog:' - version: 0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@proj-airi/unplugin-live2d-sdk': specifier: 'catalog:' - version: 0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + version: 0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) '@types/audioworklet': specifier: 'catalog:' version: 0.0.97 @@ -2365,7 +2365,7 @@ importers: version: 2.10.3 '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) '@vue-macros/volar': specifier: 'catalog:' version: 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) @@ -2398,7 +2398,7 @@ importers: version: 6.8.3 electron-vite: specifier: 'catalog:' - version: 5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) get-port-please: specifier: 'catalog:' version: 3.2.0 @@ -2419,31 +2419,31 @@ importers: version: 2.2.6 unocss-preset-scrollbar: specifier: 'catalog:' - version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))) unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' - version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) vite-plugin-mkcert: specifier: 'catalog:' - version: 2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite-plugin-vue-devtools: specifier: 'catalog:' - version: 8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) + version: 8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) vite-plugin-vue-layouts: specifier: 'catalog:' - version: 0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -2747,7 +2747,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/iconify-meteocons': specifier: 'catalog:' version: 0.1.5 @@ -2810,16 +2810,16 @@ importers: version: 0.0.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' - version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) + version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@2.80.0) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -2834,7 +2834,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -4032,6 +4032,9 @@ importers: packages/stage-ui: dependencies: + '@capacitor/core': + specifier: 'catalog:' + version: 8.3.1 '@date-fns/utc': specifier: 'catalog:' version: 2.1.1 @@ -20735,9 +20738,9 @@ snapshots: dependencies: electron: 41.2.1 - '@electron-toolkit/tsconfig@2.0.0(@types/node@24.12.2)': + '@electron-toolkit/tsconfig@2.0.0(@types/node@25.6.0)': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@electron-toolkit/utils@4.0.0(electron@41.2.1)': dependencies: @@ -21649,31 +21652,6 @@ snapshots: - supports-color - typescript - '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) - '@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3))) - '@intlify/shared': 11.3.2 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.32)(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - '@rollup/pluginutils': 5.3.0(rollup@4.60.1) - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - debug: 4.4.3(supports-color@10.2.2) - fast-glob: 3.3.3 - pathe: 2.0.3 - picocolors: 1.1.1 - unplugin: 2.3.11 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vue: 3.5.32(typescript@5.9.3) - optionalDependencies: - vue-i18n: 11.3.2(vue@3.5.32(typescript@5.9.3)) - transitivePeerDependencies: - - '@vue/compiler-dom' - - eslint - - rollup - - supports-color - - typescript - '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) @@ -23676,35 +23654,11 @@ snapshots: transitivePeerDependencies: - magicast - '@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - ofetch: 1.5.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - '@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: ofetch: 1.5.1 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - '@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)': - dependencies: - ofetch: 1.5.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - yauzl: 3.3.0 - transitivePeerDependencies: - - '@types/node' - - '@vitejs/devtools' - - esbuild - - jiti - - less - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - '@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)': dependencies: ofetch: 1.5.1 @@ -25251,12 +25205,6 @@ snapshots: transitivePeerDependencies: - typescript - '@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))': - dependencies: - '@rolldown/pluginutils': 1.0.0-rc.13 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vue: 3.5.32(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.13 @@ -25534,15 +25482,6 @@ snapshots: transitivePeerDependencies: - vue - '@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - sirv: 3.0.2 - vue: 3.5.32(typescript@5.9.3) - optionalDependencies: - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - typescript - '@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: sirv: 3.0.2 @@ -27620,7 +27559,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron-vite@5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + electron-vite@5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) @@ -27628,7 +27567,7 @@ snapshots: esbuild: 0.25.12 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color @@ -33633,6 +33572,11 @@ snapshots: '@unocss/preset-mini': 66.6.8 unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + unocss-preset-scrollbar@4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))): + dependencies: + '@unocss/preset-mini': 66.6.8 + unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@unocss/cli': 66.6.8 @@ -33699,14 +33643,6 @@ snapshots: unplugin: 2.3.11 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - optionalDependencies: - esbuild: 0.27.2 - rolldown: 1.0.0-rc.16 - rollup: 4.60.1 - unplugin: 2.3.11 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): optionalDependencies: esbuild: 0.27.2 @@ -33728,19 +33664,6 @@ snapshots: transitivePeerDependencies: - supports-color - unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - ci-info: 4.4.0 - git-url-parse: 16.1.0 - simple-git: 3.36.0 - unplugin: 2.3.11 - optionalDependencies: - esbuild: 0.27.2 - rollup: 4.60.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - supports-color - unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: ci-info: 4.4.0 @@ -33834,17 +33757,6 @@ snapshots: rollup: 2.80.0 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.60.1) - unplugin: 3.0.0 - yaml: 2.8.3 - optionalDependencies: - esbuild: 0.27.2 - rolldown: 1.0.0-rc.16 - rollup: 4.60.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 @@ -34072,22 +33984,12 @@ snapshots: - rollup - supports-color - vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - birpc: 2.9.0 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-hot-client: 2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: birpc: 2.9.0 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - vite-hot-client@2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) @@ -34133,21 +34035,6 @@ snapshots: - tsx - yaml - vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - ansis: 4.2.0 - debug: 4.4.3(supports-color@10.2.2) - error-stack-parser-es: 1.0.5 - ohash: 2.0.11 - open: 10.2.0 - perfect-debounce: 2.1.0 - sirv: 3.0.2 - unplugin-utils: 0.3.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - transitivePeerDependencies: - - supports-color - vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: ansis: 4.2.0 @@ -34179,13 +34066,6 @@ snapshots: - typescript - ws - vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - debug: 4.4.3(supports-color@10.2.2) - supports-color: 10.2.2 - undici: 8.1.0 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -34204,20 +34084,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)): - dependencies: - '@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3)) - '@vue/devtools-kit': 8.1.1 - '@vue/devtools-shared': 8.1.1 - sirv: 3.0.2 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-plugin-inspect: 11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - vite-plugin-vue-inspector: 5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - transitivePeerDependencies: - - '@nuxt/kit' - - supports-color - - vue - vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)): dependencies: '@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3)) @@ -34232,21 +34098,6 @@ snapshots: - supports-color - vue - vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0) - '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0) - '@vue/compiler-dom': 3.5.32 - kolorist: 1.8.0 - magic-string: 0.30.21 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - supports-color - vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@babel/core': 7.29.0 @@ -34262,16 +34113,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)): - dependencies: - debug: 4.4.3(supports-color@10.2.2) - fast-glob: 3.3.3 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vue: 3.5.32(typescript@5.9.3) - vue-router: 5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - transitivePeerDependencies: - - supports-color - vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -34539,54 +34380,6 @@ snapshots: - vue-tsc - webpack - vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)): - dependencies: - '@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/boolean-prop': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/chain-call': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/common': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/config': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-emit': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-models': 3.1.2(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-prop': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-props': 3.1.2(@vue-macros/reactivity-transform@3.1.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-props-refs': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-render': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-slots': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-stylex': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/devtools': 3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vue-macros/export-expose': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/export-props': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/export-render': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/hoist-static': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/jsx-directive': 3.1.2(typescript@5.9.3) - '@vue-macros/named-template': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/reactivity-transform': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/script-lang': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/setup-block': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/setup-component': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/setup-sfc': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/short-bind': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/short-emits': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/short-vmodel': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/volar': 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) - unplugin: 2.3.11 - unplugin-combine: 2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - unplugin-vue-define-options: 3.1.2(vue@3.5.32(typescript@5.9.3)) - vue: 3.5.32(typescript@5.9.3) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - - '@rspack/core' - - '@vueuse/core' - - esbuild - - rolldown - - rollup - - typescript - - vite - - vue-tsc - - webpack - vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)): dependencies: '@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) From d4de0f436ebd4e4ddf6c3dfde5a75d63b16845d6 Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 19 May 2026 23:24:23 +0800 Subject: [PATCH 16/28] fix(server): resolve JWKS from the bound HOST, not always loopback LAN dev with HOST set to a concrete interface failed token verification because JWKS was fetched from 127.0.0.1 while the API listened elsewhere. Co-authored-by: Cursor --- apps/server/src/libs/request-auth.ts | 17 ++++--- .../src/libs/tests/request-auth.test.ts | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/apps/server/src/libs/request-auth.ts b/apps/server/src/libs/request-auth.ts index f96fe9c551..00d7945e20 100644 --- a/apps/server/src/libs/request-auth.ts +++ b/apps/server/src/libs/request-auth.ts @@ -81,14 +81,17 @@ function resolveTestAuthToken(env: Env, accessToken: string): RequestAuthSession let cachedJWKS: ReturnType | null = null +function localJwksUrl(env: Env): URL { + const host = env.HOST ?? '0.0.0.0' + const jwksHost = host === '0.0.0.0' || host === '127.0.0.1' || host === 'localhost' + ? '127.0.0.1' + : host + return new URL('/api/auth/jwks', `http://${jwksHost}:${env.PORT}`) +} + function getJWKS(env: Env): ReturnType { - if (!cachedJWKS) { - // Use the local listener for this server's own JWKS so development tunnels - // such as ngrok are not involved in request-time token verification. - cachedJWKS = createRemoteJWKSet( - new URL('/api/auth/jwks', `http://127.0.0.1:${env.PORT}`), - ) - } + if (!cachedJWKS) + cachedJWKS = createRemoteJWKSet(localJwksUrl(env)) return cachedJWKS } diff --git a/apps/server/src/libs/tests/request-auth.test.ts b/apps/server/src/libs/tests/request-auth.test.ts index ec6dd09d52..6bdbb6abe0 100644 --- a/apps/server/src/libs/tests/request-auth.test.ts +++ b/apps/server/src/libs/tests/request-auth.test.ts @@ -15,6 +15,7 @@ const mockedCreateRemoteJWKSet = vi.mocked(createRemoteJWKSet) const mockEnv = { API_SERVER_URL: 'http://localhost:3000', + HOST: '0.0.0.0', PORT: 3000, TEST_AUTH_TOKEN: '', TEST_AUTH_USER_ID: 'test-user', @@ -246,4 +247,49 @@ describe('resolveRequestAuth', () => { expect(result).toBeNull() }) + + it('fetches JWKS from the configured HOST when not bound to all interfaces', async () => { + vi.resetModules() + mockedCreateRemoteJWKSet.mockClear() + mockedJwtVerify.mockResolvedValue({ + payload: { + sub: 'user-1', + iss: 'http://10.0.0.5:3000/api/auth', + aud: 'http://10.0.0.5:3000', + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 3600, + jti: 'jwt-token-id', + }, + protectedHeader: { alg: 'RS256' }, + key: {} as any, + } as any) + + const { resolveRequestAuth: resolveWithHost } = await import('./request-auth') + const auth = { + api: { + getSession: vi.fn().mockResolvedValue(null), + }, + $context: Promise.resolve({ + internalAdapter: { + findUserById: vi.fn().mockResolvedValue({ + id: 'user-1', + email: 'user@example.com', + name: 'User', + emailVerified: true, + image: null, + createdAt: new Date(), + updatedAt: new Date(), + }), + }, + }), + } + + await resolveWithHost( + auth as any, + { API_SERVER_URL: 'http://10.0.0.5:3000', HOST: '10.0.0.5', PORT: 3000 } as any, + new Headers({ Authorization: 'Bearer eyJhbGciOiJSUzI1NiJ9.test.sig' }), + ) + + expect(mockedCreateRemoteJWKSet).toHaveBeenCalledWith(new URL('http://10.0.0.5:3000/api/auth/jwks')) + }) }) From 8f6694e832974881c39a477e1dcfc7925357feec Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 19 May 2026 23:24:56 +0800 Subject: [PATCH 17/28] fix(stage-ui): treat VITE_OIDC_REDIRECT_URI as the full redirect URI Avoid appending /auth/callback when the env already contains the complete callback URL. Co-authored-by: Cursor --- packages/stage-ui/src/libs/auth-config.test.ts | 9 +++++++++ packages/stage-ui/src/libs/auth-config.ts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/stage-ui/src/libs/auth-config.test.ts b/packages/stage-ui/src/libs/auth-config.test.ts index 7d7536b6a8..577d1237a4 100644 --- a/packages/stage-ui/src/libs/auth-config.test.ts +++ b/packages/stage-ui/src/libs/auth-config.test.ts @@ -17,6 +17,7 @@ describe('oIDC client config', () => { beforeEach(() => { nativePlatform = false platform = 'web' + vi.unstubAllEnvs() vi.resetModules() vi.stubGlobal('window', { location: { @@ -32,6 +33,14 @@ describe('oIDC client config', () => { expect(OIDC_REDIRECT_URI).toBe('https://airi.moeru.ai/auth/callback') }) + it('uses VITE_OIDC_REDIRECT_URI as the full redirect URI when set', async () => { + vi.stubEnv('VITE_OIDC_REDIRECT_URI', 'https://example.com/auth/callback') + + const { OIDC_REDIRECT_URI } = await import('./auth-config') + + expect(OIDC_REDIRECT_URI).toBe('https://example.com/auth/callback') + }) + it('uses the app-owned Pocket redirect URI on native platforms', async () => { nativePlatform = true platform = 'ios' diff --git a/packages/stage-ui/src/libs/auth-config.ts b/packages/stage-ui/src/libs/auth-config.ts index d60d9f015e..4a6724a95c 100644 --- a/packages/stage-ui/src/libs/auth-config.ts +++ b/packages/stage-ui/src/libs/auth-config.ts @@ -69,7 +69,7 @@ export const OIDC_CLIENT_ID = import.meta.env.VITE_OIDC_CLIENT_ID || (usesNativeOidcClient ? 'airi-stage-pocket' : 'airi-stage-web') export const OIDC_REDIRECT_URI = import.meta.env.VITE_OIDC_REDIRECT_URI - ? `${origin}/auth/callback` + ? import.meta.env.VITE_OIDC_REDIRECT_URI : capacitorPlatform === 'ios' ? NATIVE_AUTH_REDIRECT_URI : capacitorPlatform !== 'web' From 404502bd8db5225a27dfe32c27e9e575873b863e Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 19 May 2026 23:25:18 +0800 Subject: [PATCH 18/28] fix(server): reject opaque origins in ADDITIONAL_TRUSTED_ORIGINS Block javascript:, file:, and other inputs that normalize to the opaque origin "null" so misconfiguration cannot trust Origin: null. Co-authored-by: Cursor --- apps/server/src/libs/env.ts | 11 +++++++++-- apps/server/src/libs/tests/env.test.ts | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/server/src/libs/env.ts b/apps/server/src/libs/env.ts index bf11d76274..8c263d9390 100644 --- a/apps/server/src/libs/env.ts +++ b/apps/server/src/libs/env.ts @@ -31,14 +31,21 @@ export function parseAdditionalTrustedOriginsEnv(raw: string): string[] { if (!entry) continue - let normalized: string + let parsed: URL try { - normalized = new URL(entry).origin + parsed = new URL(entry) } catch { throw new TypeError(`ADDITIONAL_TRUSTED_ORIGINS: invalid URL origin segment "${entry}"`) } + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') + throw new TypeError(`ADDITIONAL_TRUSTED_ORIGINS: invalid URL origin segment "${entry}"`) + + const normalized = parsed.origin + if (normalized === 'null') + throw new TypeError(`ADDITIONAL_TRUSTED_ORIGINS: invalid URL origin segment "${entry}"`) + if (!seen.has(normalized)) { seen.add(normalized) out.push(normalized) diff --git a/apps/server/src/libs/tests/env.test.ts b/apps/server/src/libs/tests/env.test.ts index 4bf4a02520..4d0e6c6676 100644 --- a/apps/server/src/libs/tests/env.test.ts +++ b/apps/server/src/libs/tests/env.test.ts @@ -33,6 +33,11 @@ describe('parseAdditionalTrustedOriginsEnv', () => { it('throws on invalid segments', () => { expect(() => parseAdditionalTrustedOriginsEnv('not-a-url')).toThrow(/invalid URL origin segment/) }) + + it('rejects opaque and non-http(s) origins', () => { + expect(() => parseAdditionalTrustedOriginsEnv('javascript:alert(1)')).toThrow(/invalid URL origin segment/) + expect(() => parseAdditionalTrustedOriginsEnv('file:///tmp/a')).toThrow(/invalid URL origin segment/) + }) }) describe('parseEnv', () => { From 4e9b4dbd92b29e3f6f836705c24861a613e3326f Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 19 May 2026 23:31:07 +0800 Subject: [PATCH 19/28] fix(server): correct request-auth test import after libs/tests move Co-authored-by: Cursor --- apps/server/src/libs/tests/request-auth.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/libs/tests/request-auth.test.ts b/apps/server/src/libs/tests/request-auth.test.ts index 6bdbb6abe0..677354e8f7 100644 --- a/apps/server/src/libs/tests/request-auth.test.ts +++ b/apps/server/src/libs/tests/request-auth.test.ts @@ -264,7 +264,7 @@ describe('resolveRequestAuth', () => { key: {} as any, } as any) - const { resolveRequestAuth: resolveWithHost } = await import('./request-auth') + const { resolveRequestAuth: resolveWithHost } = await import('../request-auth') const auth = { api: { getSession: vi.fn().mockResolvedValue(null), From 5f7609a72800c384ea8f5bb70802a6f03cfeadf8 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 15:35:27 +0000 Subject: [PATCH 20/28] [autofix.ci] apply automated fixes --- apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift b/apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift index 3b01741b57..f92893f9e5 100644 --- a/apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift +++ b/apps/stage-pocket/ios/App/App/AiriNativeAuthPlugin.swift @@ -45,8 +45,7 @@ class AiriNativeAuthPlugin: CAPPlugin, CAPBridgedPlugin, ASWebAuthenticationPres } if let authError = error as? ASWebAuthenticationSessionError, - authError.code == .canceledLogin - { + authError.code == .canceledLogin { call.reject("USER_CANCELLED", "The authentication session was cancelled") return } From a6a186b2a097dfac36525a9b20c58794724f13d4 Mon Sep 17 00:00:00 2001 From: Lulu Date: Wed, 20 May 2026 23:51:39 +0800 Subject: [PATCH 21/28] fix(server): bracket IPv6 hosts in local JWKS URL Bearer JWT verification failed when HOST was :: or ::1 because the JWKS fetch URL was not a valid HTTP authority. Co-authored-by: Cursor --- apps/server/src/libs/request-auth.ts | 11 +++-- .../src/libs/tests/request-auth.test.ts | 46 ++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/apps/server/src/libs/request-auth.ts b/apps/server/src/libs/request-auth.ts index 00d7945e20..8ce9350361 100644 --- a/apps/server/src/libs/request-auth.ts +++ b/apps/server/src/libs/request-auth.ts @@ -83,10 +83,15 @@ let cachedJWKS: ReturnType | null = null function localJwksUrl(env: Env): URL { const host = env.HOST ?? '0.0.0.0' - const jwksHost = host === '0.0.0.0' || host === '127.0.0.1' || host === 'localhost' + const lookupHost = host === '0.0.0.0' || host === '127.0.0.1' || host === 'localhost' ? '127.0.0.1' - : host - return new URL('/api/auth/jwks', `http://${jwksHost}:${env.PORT}`) + : host === '::' + ? '::1' + : host + const origin = lookupHost.includes(':') + ? `http://[${lookupHost}]:${env.PORT}` + : `http://${lookupHost}:${env.PORT}` + return new URL('/api/auth/jwks', origin) } function getJWKS(env: Env): ReturnType { diff --git a/apps/server/src/libs/tests/request-auth.test.ts b/apps/server/src/libs/tests/request-auth.test.ts index 677354e8f7..fce5fc3f3f 100644 --- a/apps/server/src/libs/tests/request-auth.test.ts +++ b/apps/server/src/libs/tests/request-auth.test.ts @@ -2,7 +2,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { resolveRequestAuth } from '../request-auth' -// Mock jose module vi.mock('jose', () => ({ createRemoteJWKSet: vi.fn(() => 'mock-jwks'), jwtVerify: vi.fn(), @@ -292,4 +291,49 @@ describe('resolveRequestAuth', () => { expect(mockedCreateRemoteJWKSet).toHaveBeenCalledWith(new URL('http://10.0.0.5:3000/api/auth/jwks')) }) + + it.each(['::1', '::'])('fetches JWKS from bracketed IPv6 loopback when HOST is %s', async (host) => { + vi.resetModules() + mockedCreateRemoteJWKSet.mockClear() + mockedJwtVerify.mockResolvedValue({ + payload: { + sub: 'user-1', + iss: 'http://[::1]:3000/api/auth', + aud: 'http://[::1]:3000', + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 3600, + jti: 'jwt-token-id', + }, + protectedHeader: { alg: 'RS256' }, + key: {} as any, + } as any) + + const { resolveRequestAuth: resolveWithHost } = await import('../request-auth') + const auth = { + api: { + getSession: vi.fn().mockResolvedValue(null), + }, + $context: Promise.resolve({ + internalAdapter: { + findUserById: vi.fn().mockResolvedValue({ + id: 'user-1', + email: 'user@example.com', + name: 'User', + emailVerified: true, + image: null, + createdAt: new Date(), + updatedAt: new Date(), + }), + }, + }), + } + + await resolveWithHost( + auth as any, + { API_SERVER_URL: 'http://[::1]:3000', HOST: host, PORT: 3000 } as any, + new Headers({ Authorization: 'Bearer eyJhbGciOiJSUzI1NiJ9.test.sig' }), + ) + + expect(mockedCreateRemoteJWKSet).toHaveBeenCalledWith(new URL('http://[::1]:3000/api/auth/jwks')) + }) }) From 755b58b8f0b98fcd091dbb9df3b952a6e788cac5 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 24 May 2026 16:22:34 +0800 Subject: [PATCH 22/28] fix(stage-pocket): align CapApp-SPM with Capacitor 8.3.1 Co-authored-by: Cursor --- .../xcshareddata/swiftpm/Package.resolved | 6 +++--- apps/stage-pocket/ios/App/CapApp-SPM/Package.swift | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/stage-pocket/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/apps/stage-pocket/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5b4bd2d48c..79e244e7df 100644 --- a/apps/stage-pocket/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/apps/stage-pocket/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "8765687701ea10de7b4fa5981471aaf26157c825c91c99a7f0e25bbdf71f12e6", + "originHash" : "ba7adc1d840bb82293a01f8c96aa62c3684c551cb85db27a67ce3aaa8af8101a", "pins" : [ { "identity" : "capacitor-swift-pm", "kind" : "remoteSourceControl", "location" : "https://github.com/ionic-team/capacitor-swift-pm.git", "state" : { - "revision" : "0e862e6ff13852a710c8a484180ca4d6a2cc9761", - "version" : "8.2.0" + "revision" : "f1a8fadf1437c23b825c818fb6509c9dbbae2f61", + "version" : "8.3.1" } }, { diff --git a/apps/stage-pocket/ios/App/CapApp-SPM/Package.swift b/apps/stage-pocket/ios/App/CapApp-SPM/Package.swift index 9b6d2fc48a..4b691b39cb 100644 --- a/apps/stage-pocket/ios/App/CapApp-SPM/Package.swift +++ b/apps/stage-pocket/ios/App/CapApp-SPM/Package.swift @@ -11,10 +11,11 @@ let package = Package( targets: ["CapApp-SPM"]) ], dependencies: [ - .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.2.0"), - .package(name: "CapacitorBarcodeScanner", path: "../../../../../node_modules/.pnpm/@capacitor+barcode-scanner@3.0.2_@capacitor+core@8.2.0/node_modules/@capacitor/barcode-scanner"), - .package(name: "CapacitorLocalNotifications", path: "../../../../../node_modules/.pnpm/@capacitor+local-notifications@8.0.2_@capacitor+core@8.2.0/node_modules/@capacitor/local-notifications"), - .package(name: "CapacitorNativeSettings", path: "../../../../../node_modules/.pnpm/capacitor-native-settings@8.1.0_@capacitor+core@8.2.0/node_modules/capacitor-native-settings") + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "8.3.1"), + .package(name: "CapacitorApp", path: "../../../../../node_modules/.pnpm/@capacitor+app@8.1.0_@capacitor+core@8.3.1/node_modules/@capacitor/app"), + .package(name: "CapacitorBarcodeScanner", path: "../../../../../node_modules/.pnpm/@capacitor+barcode-scanner@3.0.2_@capacitor+core@8.3.1/node_modules/@capacitor/barcode-scanner"), + .package(name: "CapacitorLocalNotifications", path: "../../../../../node_modules/.pnpm/@capacitor+local-notifications@8.0.2_@capacitor+core@8.3.1/node_modules/@capacitor/local-notifications"), + .package(name: "CapacitorNativeSettings", path: "../../../../../node_modules/.pnpm/capacitor-native-settings@8.1.0_@capacitor+core@8.3.1/node_modules/capacitor-native-settings") ], targets: [ .target( @@ -22,6 +23,7 @@ let package = Package( dependencies: [ .product(name: "Capacitor", package: "capacitor-swift-pm"), .product(name: "Cordova", package: "capacitor-swift-pm"), + .product(name: "CapacitorApp", package: "CapacitorApp"), .product(name: "CapacitorBarcodeScanner", package: "CapacitorBarcodeScanner"), .product(name: "CapacitorLocalNotifications", package: "CapacitorLocalNotifications"), .product(name: "CapacitorNativeSettings", package: "CapacitorNativeSettings") From 7bb8ecaf1d5c2277412f2cca811b20a1979a1454 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 24 May 2026 16:25:16 +0800 Subject: [PATCH 23/28] refactor(stage-ui): remove ngrok skip-browser-warning handling Co-authored-by: Cursor --- apps/server/README.md | 4 ++-- .../ios/App/App/DevBridgeViewController.swift | 2 +- packages/stage-ui/src/composables/api.ts | 10 ++------- packages/stage-ui/src/libs/auth-oidc.ts | 10 +++------ packages/stage-ui/src/libs/auth.ts | 5 +---- packages/stage-ui/src/libs/server.ts | 21 ------------------- 6 files changed, 9 insertions(+), 43 deletions(-) diff --git a/apps/server/README.md b/apps/server/README.md index 0e25a869dc..1d56422648 100644 --- a/apps/server/README.md +++ b/apps/server/README.md @@ -54,6 +54,6 @@ When the mobile dev server uses a non-localhost origin (for example `https://10. Restart the API server after changing this variable. -## Local HTTPS API (ngrok; no TLS in Node) +## Local HTTPS API (reverse proxy; no TLS in Node) -When the client runs on **https** (e.g. Vite `dev:https` or Capacitor) while Hono stays on **`http://127.0.0.1:3000`**, WebKit can treat **`https` → `http` API** calls as mixed content. Use an **https** tunnel that forwards to this API (typical: [ngrok](https://ngrok.com/) http → `127.0.0.1:3000`) and point **`API_SERVER_URL`** / the frontend **`VITE_SERVER_URL`** (or equivalent `SERVER_URL`) at that **https** base URL. Stage UI sends `ngrok-skip-browser-warning` on requests when the host looks like ngrok so the free-tier interstitial does not block API traffic. +When the client runs on **https** (e.g. Vite `dev:https` or Capacitor) while Hono stays on **`http://127.0.0.1:3000`**, WebKit can treat **`https` → `http` API** calls as mixed content. Terminate TLS on a reverse proxy or tunnel (for example **frp**, Caddy, or nginx) that forwards to this API, then point **`API_SERVER_URL`** and the frontend **`VITE_SERVER_URL`** (or equivalent `SERVER_URL`) at that **https** public base URL. diff --git a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift index 389c390405..c86f2c30c9 100644 --- a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift +++ b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift @@ -161,7 +161,7 @@ extension DevBridgeViewController: WKNavigationDelegate { } if nsError.code == -1200 { print( - "[DevBridge] TLS failed (-1200). Prefer an https URL the device trusts (e.g. ngrok to the dev API), or use http for local Vite when policy allows; see apps/server/README.md (Local HTTPS API)." + "[DevBridge] TLS failed (-1200). Prefer an https URL the device trusts (e.g. frp or another reverse proxy to the dev API), or use http for local Vite when policy allows; see apps/server/README.md (Local HTTPS API)." ) } } diff --git a/packages/stage-ui/src/composables/api.ts b/packages/stage-ui/src/composables/api.ts index daf51f1901..8004079c76 100644 --- a/packages/stage-ui/src/composables/api.ts +++ b/packages/stage-ui/src/composables/api.ts @@ -3,16 +3,10 @@ import type { AppType } from '../../../../apps/server/src/app' import { hc } from 'hono/client' import { authedFetch } from '../libs/auth-fetch' -import { applyNgrokSkipRequestHeader, SERVER_URL } from '../libs/server' - -async function fetchWithNgrok(input: RequestInfo | URL, init?: RequestInit): Promise { - const headers = new Headers(init?.headers) - applyNgrokSkipRequestHeader(headers) - return authedFetch(input, { ...init, headers }) -} +import { SERVER_URL } from '../libs/server' export const client = hc(SERVER_URL, { - fetch: fetchWithNgrok, + fetch: authedFetch, }) export type StageApiClient = typeof client diff --git a/packages/stage-ui/src/libs/auth-oidc.ts b/packages/stage-ui/src/libs/auth-oidc.ts index 4be1996372..0b83100c21 100644 --- a/packages/stage-ui/src/libs/auth-oidc.ts +++ b/packages/stage-ui/src/libs/auth-oidc.ts @@ -1,7 +1,7 @@ import { Capacitor } from '@capacitor/core' import { generateCodeChallenge, generateCodeVerifier, generateState } from '@proj-airi/stage-shared/auth' -import { applyNgrokSkipRequestHeader, SERVER_URL } from './server' +import { SERVER_URL } from './server' // OIDC Authorization Code + PKCE client for all platforms. @@ -95,12 +95,10 @@ export async function exchangeCodeForTokens( bodyParams.client_secret = params.clientSecret const body = new URLSearchParams(bodyParams) - const tokenHeaders = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }) - applyNgrokSkipRequestHeader(tokenHeaders) const response = await fetch(new URL(OIDC_TOKEN_PATH, SERVER_URL), { method: 'POST', - headers: tokenHeaders, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body, }) @@ -132,12 +130,10 @@ export async function refreshAccessToken( params.client_secret = clientSecret const body = new URLSearchParams(params) - const refreshHeaders = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }) - applyNgrokSkipRequestHeader(refreshHeaders) const response = await fetch(new URL(OIDC_TOKEN_PATH, SERVER_URL), { method: 'POST', - headers: refreshHeaders, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body, }) diff --git a/packages/stage-ui/src/libs/auth.ts b/packages/stage-ui/src/libs/auth.ts index 68df9febe2..ceccd408bc 100644 --- a/packages/stage-ui/src/libs/auth.ts +++ b/packages/stage-ui/src/libs/auth.ts @@ -8,7 +8,7 @@ import { completeOIDCCallbackUrl } from './auth-callback' import { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } from './auth-config' import { buildAuthorizationURL, consumeFlowState, exchangeCodeForTokens, persistFlowState } from './auth-oidc' import { openNativeAuthSession } from './native-auth' -import { isNgrokServerUrl, SERVER_URL } from './server' +import { SERVER_URL } from './server' export type OAuthProvider = 'google' | 'github' @@ -33,9 +33,6 @@ export const authClient = createAuthClient({ type: 'Bearer', token: () => getAuthToken() ?? '', }, - ...(isNgrokServerUrl() - ? { headers: { 'ngrok-skip-browser-warning': 'true' } } - : {}), }, }) diff --git a/packages/stage-ui/src/libs/server.ts b/packages/stage-ui/src/libs/server.ts index e20514aeef..dd27eb710a 100644 --- a/packages/stage-ui/src/libs/server.ts +++ b/packages/stage-ui/src/libs/server.ts @@ -1,22 +1 @@ export const SERVER_URL = import.meta.env.VITE_SERVER_URL || 'https://api.airi.build' - -/** Free ngrok serves an interstitial page unless this header is present; embedded WKWebView cannot dismiss it. */ -const NGROK_SKIP_HEADER = 'ngrok-skip-browser-warning' - -/** - * `window.location` navigations cannot set headers; the iOS DevBridge rewrites - * such requests. Fetches to `SERVER_URL` on ngrok need this. - */ -export function isNgrokServerUrl(): boolean { - try { - return new URL(SERVER_URL).hostname.includes('ngrok') - } - catch { - return false - } -} - -export function applyNgrokSkipRequestHeader(headers: Headers): void { - if (isNgrokServerUrl()) - headers.set(NGROK_SKIP_HEADER, 'true') -} From cbbdb0da4192bb8e18e3925d4a8602b417f60952 Mon Sep 17 00:00:00 2001 From: Lulu Date: Sun, 24 May 2026 18:01:46 +0800 Subject: [PATCH 24/28] fix(server): preserve localhost when resolving local JWKS URL Co-authored-by: Cursor --- apps/server/src/libs/request-auth.ts | 2 +- apps/server/src/libs/tests/request-auth.test.ts | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/server/src/libs/request-auth.ts b/apps/server/src/libs/request-auth.ts index 8ce9350361..1e9e51b72d 100644 --- a/apps/server/src/libs/request-auth.ts +++ b/apps/server/src/libs/request-auth.ts @@ -83,7 +83,7 @@ let cachedJWKS: ReturnType | null = null function localJwksUrl(env: Env): URL { const host = env.HOST ?? '0.0.0.0' - const lookupHost = host === '0.0.0.0' || host === '127.0.0.1' || host === 'localhost' + const lookupHost = host === '0.0.0.0' || host === '127.0.0.1' ? '127.0.0.1' : host === '::' ? '::1' diff --git a/apps/server/src/libs/tests/request-auth.test.ts b/apps/server/src/libs/tests/request-auth.test.ts index fce5fc3f3f..ac91783a67 100644 --- a/apps/server/src/libs/tests/request-auth.test.ts +++ b/apps/server/src/libs/tests/request-auth.test.ts @@ -247,14 +247,17 @@ describe('resolveRequestAuth', () => { expect(result).toBeNull() }) - it('fetches JWKS from the configured HOST when not bound to all interfaces', async () => { + it.each([ + ['10.0.0.5', 'http://10.0.0.5:3000'], + ['localhost', 'http://localhost:3000'], + ])('fetches JWKS from HOST %s', async (host, origin) => { vi.resetModules() mockedCreateRemoteJWKSet.mockClear() mockedJwtVerify.mockResolvedValue({ payload: { sub: 'user-1', - iss: 'http://10.0.0.5:3000/api/auth', - aud: 'http://10.0.0.5:3000', + iss: `${origin}/api/auth`, + aud: origin, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 3600, jti: 'jwt-token-id', @@ -285,11 +288,11 @@ describe('resolveRequestAuth', () => { await resolveWithHost( auth as any, - { API_SERVER_URL: 'http://10.0.0.5:3000', HOST: '10.0.0.5', PORT: 3000 } as any, + { API_SERVER_URL: origin, HOST: host, PORT: 3000 } as any, new Headers({ Authorization: 'Bearer eyJhbGciOiJSUzI1NiJ9.test.sig' }), ) - expect(mockedCreateRemoteJWKSet).toHaveBeenCalledWith(new URL('http://10.0.0.5:3000/api/auth/jwks')) + expect(mockedCreateRemoteJWKSet).toHaveBeenCalledWith(new URL(`${origin}/api/auth/jwks`)) }) it.each(['::1', '::'])('fetches JWKS from bracketed IPv6 loopback when HOST is %s', async (host) => { From 943d249d5548bd0666ca0593c9f88d7b21398ace Mon Sep 17 00:00:00 2001 From: Lulu Date: Tue, 9 Jun 2026 23:47:57 +0800 Subject: [PATCH 25/28] docs(auth): document OIDC social bridge vs SPA watch split Co-authored-by: Cursor --- apps/ui-server-auth/src/pages/sign-in.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/ui-server-auth/src/pages/sign-in.vue b/apps/ui-server-auth/src/pages/sign-in.vue index 3536c5168f..30c43e6d84 100644 --- a/apps/ui-server-auth/src/pages/sign-in.vue +++ b/apps/ui-server-auth/src/pages/sign-in.vue @@ -100,6 +100,7 @@ watch(() => route.query.error, (value) => { errorMessage.value = typeof value === 'string' ? value : null }, { immediate: true }) +// NOTICE: Auto-start runs on the standalone auth UI — server GET /auth/sign-in?provider= redirects here (ui-routes.ts), then this watch POSTs to sign-in/social. watch(requestedProvider, async (provider) => { if (!provider || autoStartedProvider.value === provider) return From f2c61e6503d53c413b1dcfb377a2b35168504e53 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:26:23 +0000 Subject: [PATCH 26/28] [autofix.ci] apply automated fixes --- pnpm-lock.yaml | 276 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 243 insertions(+), 33 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 657d65da8e..888b142205 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1851,7 +1851,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/cap-vite': specifier: workspace:* version: link:../../packages/cap-vite @@ -1914,16 +1914,16 @@ importers: version: 4.6.4 unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' - version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) + version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@2.80.0) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -1938,7 +1938,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -2260,7 +2260,7 @@ importers: version: 3.0.2(electron@41.2.1) '@electron-toolkit/tsconfig': specifier: 'catalog:' - version: 2.0.0(@types/node@25.6.0) + version: 2.0.0(@types/node@24.12.2) '@electron-toolkit/utils': specifier: 'catalog:' version: 4.0.0(electron@41.2.1) @@ -2299,7 +2299,7 @@ importers: version: 3.1.0 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@modelcontextprotocol/sdk': specifier: 'catalog:' version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) @@ -2335,10 +2335,10 @@ importers: version: link:../../packages/ui-transitions '@proj-airi/unplugin-fetch': specifier: 'catalog:' - version: 0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@proj-airi/unplugin-live2d-sdk': specifier: 'catalog:' - version: 0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + version: 0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) '@types/audioworklet': specifier: 'catalog:' version: 0.0.97 @@ -2365,7 +2365,7 @@ importers: version: 2.10.3 '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) '@vue-macros/volar': specifier: 'catalog:' version: 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) @@ -2398,7 +2398,7 @@ importers: version: 6.8.3 electron-vite: specifier: 'catalog:' - version: 5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) get-port-please: specifier: 'catalog:' version: 3.2.0 @@ -2419,31 +2419,31 @@ importers: version: 2.2.6 unocss-preset-scrollbar: specifier: 'catalog:' - version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))) unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) vite-plugin-mkcert: specifier: 'catalog:' - version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite-plugin-vue-devtools: specifier: 'catalog:' - version: 8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) + version: 8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) vite-plugin-vue-layouts: specifier: 'catalog:' - version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -2747,7 +2747,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/iconify-meteocons': specifier: 'catalog:' version: 0.1.5 @@ -2810,16 +2810,16 @@ importers: version: 0.0.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' - version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@2.80.0) + version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -2834,7 +2834,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -20738,9 +20738,9 @@ snapshots: dependencies: electron: 41.2.1 - '@electron-toolkit/tsconfig@2.0.0(@types/node@25.6.0)': + '@electron-toolkit/tsconfig@2.0.0(@types/node@24.12.2)': dependencies: - '@types/node': 25.6.0 + '@types/node': 24.12.2 '@electron-toolkit/utils@4.0.0(electron@41.2.1)': dependencies: @@ -21652,6 +21652,31 @@ snapshots: - supports-color - typescript + '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) + '@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3))) + '@intlify/shared': 11.3.2 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.32)(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + '@rollup/pluginutils': 5.3.0(rollup@4.60.1) + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) + debug: 4.4.3(supports-color@10.2.2) + fast-glob: 3.3.3 + pathe: 2.0.3 + picocolors: 1.1.1 + unplugin: 2.3.11 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vue: 3.5.32(typescript@5.9.3) + optionalDependencies: + vue-i18n: 11.3.2(vue@3.5.32(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/compiler-dom' + - eslint + - rollup + - supports-color + - typescript + '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) @@ -23654,11 +23679,35 @@ snapshots: transitivePeerDependencies: - magicast + '@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + dependencies: + ofetch: 1.5.1 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + '@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: ofetch: 1.5.1 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + '@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)': + dependencies: + ofetch: 1.5.1 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + yauzl: 3.3.0 + transitivePeerDependencies: + - '@types/node' + - '@vitejs/devtools' + - esbuild + - jiti + - less + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + '@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)': dependencies: ofetch: 1.5.1 @@ -25205,6 +25254,12 @@ snapshots: transitivePeerDependencies: - typescript + '@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-rc.13 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vue: 3.5.32(typescript@5.9.3) + '@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.13 @@ -25482,6 +25537,15 @@ snapshots: transitivePeerDependencies: - vue + '@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + dependencies: + sirv: 3.0.2 + vue: 3.5.32(typescript@5.9.3) + optionalDependencies: + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + transitivePeerDependencies: + - typescript + '@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: sirv: 3.0.2 @@ -27559,7 +27623,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron-vite@5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + electron-vite@5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) @@ -27567,7 +27631,7 @@ snapshots: esbuild: 0.25.12 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color @@ -33572,11 +33636,6 @@ snapshots: '@unocss/preset-mini': 66.6.8 unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - unocss-preset-scrollbar@4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))): - dependencies: - '@unocss/preset-mini': 66.6.8 - unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@unocss/cli': 66.6.8 @@ -33643,6 +33702,14 @@ snapshots: unplugin: 2.3.11 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + optionalDependencies: + esbuild: 0.27.2 + rolldown: 1.0.0-rc.16 + rollup: 4.60.1 + unplugin: 2.3.11 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): optionalDependencies: esbuild: 0.27.2 @@ -33664,6 +33731,19 @@ snapshots: transitivePeerDependencies: - supports-color + unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + ci-info: 4.4.0 + git-url-parse: 16.1.0 + simple-git: 3.36.0 + unplugin: 2.3.11 + optionalDependencies: + esbuild: 0.27.2 + rollup: 4.60.1 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + transitivePeerDependencies: + - supports-color + unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: ci-info: 4.4.0 @@ -33757,6 +33837,17 @@ snapshots: rollup: 2.80.0 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.60.1) + unplugin: 3.0.0 + yaml: 2.8.3 + optionalDependencies: + esbuild: 0.27.2 + rolldown: 1.0.0-rc.16 + rollup: 4.60.1 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 @@ -33984,12 +34075,22 @@ snapshots: - rollup - supports-color + vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + birpc: 2.9.0 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite-hot-client: 2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: birpc: 2.9.0 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vite-hot-client@2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) @@ -34035,6 +34136,21 @@ snapshots: - tsx - yaml + vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + ansis: 4.2.0 + debug: 4.4.3(supports-color@10.2.2) + error-stack-parser-es: 1.0.5 + ohash: 2.0.11 + open: 10.2.0 + perfect-debounce: 2.1.0 + sirv: 3.0.2 + unplugin-utils: 0.3.1 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + transitivePeerDependencies: + - supports-color + vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: ansis: 4.2.0 @@ -34066,6 +34182,13 @@ snapshots: - typescript - ws + vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + supports-color: 10.2.2 + undici: 8.1.0 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -34084,6 +34207,20 @@ snapshots: transitivePeerDependencies: - supports-color + vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)): + dependencies: + '@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3)) + '@vue/devtools-kit': 8.1.1 + '@vue/devtools-shared': 8.1.1 + sirv: 3.0.2 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite-plugin-inspect: 11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vite-plugin-vue-inspector: 5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + transitivePeerDependencies: + - '@nuxt/kit' + - supports-color + - vue + vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)): dependencies: '@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3)) @@ -34098,6 +34235,21 @@ snapshots: - supports-color - vue + vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0) + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0) + '@vue/compiler-dom': 3.5.32 + kolorist: 1.8.0 + magic-string: 0.30.21 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + transitivePeerDependencies: + - supports-color + vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@babel/core': 7.29.0 @@ -34113,6 +34265,16 @@ snapshots: transitivePeerDependencies: - supports-color + vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + fast-glob: 3.3.3 + vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vue: 3.5.32(typescript@5.9.3) + vue-router: 5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + transitivePeerDependencies: + - supports-color + vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -34380,6 +34542,54 @@ snapshots: - vue-tsc - webpack + vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)): + dependencies: + '@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/boolean-prop': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/chain-call': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/common': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/config': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-emit': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-models': 3.1.2(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-prop': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-props': 3.1.2(@vue-macros/reactivity-transform@3.1.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-props-refs': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-render': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-slots': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/define-stylex': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/devtools': 3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vue-macros/export-expose': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/export-props': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/export-render': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/hoist-static': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/jsx-directive': 3.1.2(typescript@5.9.3) + '@vue-macros/named-template': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/reactivity-transform': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/script-lang': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/setup-block': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/setup-component': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/setup-sfc': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/short-bind': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/short-emits': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/short-vmodel': 3.1.2(vue@3.5.32(typescript@5.9.3)) + '@vue-macros/volar': 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + unplugin: 2.3.11 + unplugin-combine: 2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + unplugin-vue-define-options: 3.1.2(vue@3.5.32(typescript@5.9.3)) + vue: 3.5.32(typescript@5.9.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + - '@rspack/core' + - '@vueuse/core' + - esbuild + - rolldown + - rollup + - typescript + - vite + - vue-tsc + - webpack + vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)): dependencies: '@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) From e287a705d75f0002bbe5c45dd83d1b7e1b7f9aa8 Mon Sep 17 00:00:00 2001 From: Lulu Date: Wed, 10 Jun 2026 17:04:59 +0800 Subject: [PATCH 27/28] refactor(auth): move iOS native auth from stage-ui to stage-pocket Co-authored-by: Cursor --- .../src/libs/native-auth.test.ts | 0 .../stage-pocket}/src/libs/native-auth.ts | 0 apps/stage-pocket/src/libs/pocket-auth.ts | 18 +++++++++ apps/stage-pocket/src/main.ts | 2 + apps/ui-server-auth/src/pages/sign-in.vue | 1 - .../src/shims/capacitor-core.ts | 23 ----------- apps/ui-server-auth/vite.config.ts | 1 - packages/stage-ui/package.json | 1 - .../stage-ui/src/libs/auth-config.test.ts | 40 ++++++++----------- packages/stage-ui/src/libs/auth-config.ts | 16 ++------ packages/stage-ui/src/libs/auth-oidc.ts | 15 +++---- packages/stage-ui/src/libs/auth-platform.ts | 11 +++++ packages/stage-ui/src/libs/auth.ts | 28 +++++-------- .../stage-ui/src/libs/capacitor-runtime.ts | 23 +++++++++++ packages/stage-ui/src/stores/auth.ts | 8 ++-- pnpm-lock.yaml | 23 +++++------ 16 files changed, 101 insertions(+), 109 deletions(-) rename {packages/stage-ui => apps/stage-pocket}/src/libs/native-auth.test.ts (100%) rename {packages/stage-ui => apps/stage-pocket}/src/libs/native-auth.ts (100%) create mode 100644 apps/stage-pocket/src/libs/pocket-auth.ts delete mode 100644 apps/ui-server-auth/src/shims/capacitor-core.ts create mode 100644 packages/stage-ui/src/libs/auth-platform.ts create mode 100644 packages/stage-ui/src/libs/capacitor-runtime.ts diff --git a/packages/stage-ui/src/libs/native-auth.test.ts b/apps/stage-pocket/src/libs/native-auth.test.ts similarity index 100% rename from packages/stage-ui/src/libs/native-auth.test.ts rename to apps/stage-pocket/src/libs/native-auth.test.ts diff --git a/packages/stage-ui/src/libs/native-auth.ts b/apps/stage-pocket/src/libs/native-auth.ts similarity index 100% rename from packages/stage-ui/src/libs/native-auth.ts rename to apps/stage-pocket/src/libs/native-auth.ts diff --git a/apps/stage-pocket/src/libs/pocket-auth.ts b/apps/stage-pocket/src/libs/pocket-auth.ts new file mode 100644 index 0000000000..846c4e6ffa --- /dev/null +++ b/apps/stage-pocket/src/libs/pocket-auth.ts @@ -0,0 +1,18 @@ +import { applyOIDCTokens, fetchSession } from '@proj-airi/stage-ui/libs/auth' +import { completeOIDCCallbackUrl } from '@proj-airi/stage-ui/libs/auth-callback' +import { consumeFlowState, exchangeCodeForTokens } from '@proj-airi/stage-ui/libs/auth-oidc' +import { configureIOSSignIn } from '@proj-airi/stage-ui/libs/auth-platform' + +import { openNativeAuthSession } from './native-auth' + +export function installPocketAuth(): void { + configureIOSSignIn(async (authorizeUrl) => { + const callbackUrl = await openNativeAuthSession(authorizeUrl) + await completeOIDCCallbackUrl(callbackUrl, { + consumeFlowState, + exchangeCodeForTokens, + applyOIDCTokens, + fetchSession, + }) + }) +} diff --git a/apps/stage-pocket/src/main.ts b/apps/stage-pocket/src/main.ts index ea3e71595e..37e3acb636 100644 --- a/apps/stage-pocket/src/main.ts +++ b/apps/stage-pocket/src/main.ts @@ -15,6 +15,7 @@ import { routes } from 'vue-router/auto-routes' import App from './App.vue' +import { installPocketAuth } from './libs/pocket-auth' import { installDeepLinks } from './modules/deep-links' import { i18n } from './modules/i18n' @@ -51,6 +52,7 @@ window.addEventListener('unhandledrejection', (event) => { }) installDeepLinks(router) +installPocketAuth() createApp(App) .use(MotionPlugin) diff --git a/apps/ui-server-auth/src/pages/sign-in.vue b/apps/ui-server-auth/src/pages/sign-in.vue index 30c43e6d84..3536c5168f 100644 --- a/apps/ui-server-auth/src/pages/sign-in.vue +++ b/apps/ui-server-auth/src/pages/sign-in.vue @@ -100,7 +100,6 @@ watch(() => route.query.error, (value) => { errorMessage.value = typeof value === 'string' ? value : null }, { immediate: true }) -// NOTICE: Auto-start runs on the standalone auth UI — server GET /auth/sign-in?provider= redirects here (ui-routes.ts), then this watch POSTs to sign-in/social. watch(requestedProvider, async (provider) => { if (!provider || autoStartedProvider.value === provider) return diff --git a/apps/ui-server-auth/src/shims/capacitor-core.ts b/apps/ui-server-auth/src/shims/capacitor-core.ts deleted file mode 100644 index ffd919a0d6..0000000000 --- a/apps/ui-server-auth/src/shims/capacitor-core.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Vite-only stub: ui-server-auth runs in a normal browser bundle. `@proj-airi/stage-ui` - * imports `@capacitor/core` for optional native runtime checks; those paths are dead - * here but must resolve for Rolldown. - */ -export const Capacitor = { - getPlatform(): string { - return 'web' - }, -} - -/** - * `native-auth.ts` calls `registerPlugin` at module load; ui-server-auth never invokes - * the Pocket native bridge — return a typed no-op implementation. - */ -export function registerPlugin(_name: string): T { - return { - authenticate: async () => { - throw new Error('AiriNativeAuth is not available in the ui-server-auth bundle') - }, - debugLog: async () => {}, - } as T -} diff --git a/apps/ui-server-auth/vite.config.ts b/apps/ui-server-auth/vite.config.ts index 57a84b0040..52623ab560 100644 --- a/apps/ui-server-auth/vite.config.ts +++ b/apps/ui-server-auth/vite.config.ts @@ -23,7 +23,6 @@ export default defineConfig({ resolve: { alias: { - '@capacitor/core': resolve(join(import.meta.dirname, 'src', 'shims', 'capacitor-core.ts')), '@proj-airi/i18n': resolve(join(import.meta.dirname, '..', '..', 'packages', 'i18n', 'src')), '@proj-airi/stage-ui': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-ui', 'src')), '@proj-airi/stage-shared': resolve(join(import.meta.dirname, '..', '..', 'packages', 'stage-shared', 'src')), diff --git a/packages/stage-ui/package.json b/packages/stage-ui/package.json index 6132e70295..4ca6866536 100644 --- a/packages/stage-ui/package.json +++ b/packages/stage-ui/package.json @@ -62,7 +62,6 @@ "test:run": "vitest run" }, "dependencies": { - "@capacitor/core": "catalog:", "@date-fns/utc": "catalog:", "@formkit/auto-animate": "catalog:", "@huggingface/transformers": "catalog:", diff --git a/packages/stage-ui/src/libs/auth-config.test.ts b/packages/stage-ui/src/libs/auth-config.test.ts index 577d1237a4..a76c542460 100644 --- a/packages/stage-ui/src/libs/auth-config.test.ts +++ b/packages/stage-ui/src/libs/auth-config.test.ts @@ -1,29 +1,19 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -let nativePlatform = false -let platform = 'web' - -vi.mock('@capacitor/core', () => ({ - Capacitor: { - getPlatform: () => platform, - isNativePlatform: () => nativePlatform, - }, - registerPlugin: () => ({ - authenticate: vi.fn(), - }), -})) +function stubWindow(capacitor?: { getPlatform: () => string, isNativePlatform?: () => boolean }) { + vi.stubGlobal('window', { + location: { + origin: 'https://airi.moeru.ai', + }, + Capacitor: capacitor, + }) +} describe('oIDC client config', () => { beforeEach(() => { - nativePlatform = false - platform = 'web' vi.unstubAllEnvs() vi.resetModules() - vi.stubGlobal('window', { - location: { - origin: 'https://airi.moeru.ai', - }, - }) + stubWindow() }) it('uses the web redirect URI in browsers', async () => { @@ -42,8 +32,10 @@ describe('oIDC client config', () => { }) it('uses the app-owned Pocket redirect URI on native platforms', async () => { - nativePlatform = true - platform = 'ios' + stubWindow({ + getPlatform: () => 'ios', + isNativePlatform: () => true, + }) const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } = await import('./auth-config') @@ -52,8 +44,10 @@ describe('oIDC client config', () => { }) it('keeps the existing Capacitor callback for Android until an Android launcher exists', async () => { - nativePlatform = true - platform = 'android' + stubWindow({ + getPlatform: () => 'android', + isNativePlatform: () => true, + }) const { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } = await import('./auth-config') diff --git a/packages/stage-ui/src/libs/auth-config.ts b/packages/stage-ui/src/libs/auth-config.ts index 4a6724a95c..5fb07d829d 100644 --- a/packages/stage-ui/src/libs/auth-config.ts +++ b/packages/stage-ui/src/libs/auth-config.ts @@ -1,19 +1,9 @@ -import { Capacitor } from '@capacitor/core' - -import { NATIVE_AUTH_REDIRECT_URI } from './native-auth' +import { getCapacitorPlatform } from './capacitor-runtime' const FALLBACK = 'http://localhost' const CAPACITOR_WEBVIEW_REDIRECT_URI = 'capacitor://localhost/auth/callback' - -function getCapacitorPlatform(): string { - try { - return Capacitor.getPlatform() - } - catch { - return 'web' - } -} +const POCKET_IOS_AUTH_REDIRECT_URI = 'airi-pocket://auth/callback' /** * Safely retrieves environment status without crashing in non-browser runtimes. @@ -71,7 +61,7 @@ export const OIDC_CLIENT_ID = import.meta.env.VITE_OIDC_CLIENT_ID export const OIDC_REDIRECT_URI = import.meta.env.VITE_OIDC_REDIRECT_URI ? import.meta.env.VITE_OIDC_REDIRECT_URI : capacitorPlatform === 'ios' - ? NATIVE_AUTH_REDIRECT_URI + ? POCKET_IOS_AUTH_REDIRECT_URI : capacitorPlatform !== 'web' ? CAPACITOR_WEBVIEW_REDIRECT_URI : `${origin}/auth/callback` diff --git a/packages/stage-ui/src/libs/auth-oidc.ts b/packages/stage-ui/src/libs/auth-oidc.ts index 0b83100c21..dc7f0ea070 100644 --- a/packages/stage-ui/src/libs/auth-oidc.ts +++ b/packages/stage-ui/src/libs/auth-oidc.ts @@ -1,6 +1,6 @@ -import { Capacitor } from '@capacitor/core' import { generateCodeChallenge, generateCodeVerifier, generateState } from '@proj-airi/stage-shared/auth' +import { isCapacitorNativePlatform } from './capacitor-runtime' import { SERVER_URL } from './server' // OIDC Authorization Code + PKCE client for all platforms. @@ -143,19 +143,14 @@ export async function refreshAccessToken( return await response.json() } -// Keys for PKCE + OAuth params. Web: sessionStorage (tab-scoped). Native: localStorage -// (Capacitor + system browser / ASWeb often lose sessionStorage for the return navigation). +// NOTICE: native shells use localStorage — ASWeb return navigation often drops sessionStorage. const FLOW_STATE_KEY = 'auth/v1/oidc-flow-state' const FLOW_PARAMS_KEY = 'auth/v1/oidc-flow-params' function getOidcFlowStorage(): Storage { - try { - if (Capacitor.isNativePlatform()) - return localStorage - } - catch { - // Capacitor unavailable (SSR / non-native build) - } + if (isCapacitorNativePlatform()) + return localStorage + return sessionStorage } diff --git a/packages/stage-ui/src/libs/auth-platform.ts b/packages/stage-ui/src/libs/auth-platform.ts new file mode 100644 index 0000000000..1503e67346 --- /dev/null +++ b/packages/stage-ui/src/libs/auth-platform.ts @@ -0,0 +1,11 @@ +export type IOSSignInHandler = (authorizeUrl: string) => Promise + +let iosSignInHandler: IOSSignInHandler | null = null + +export function configureIOSSignIn(handler: IOSSignInHandler | null): void { + iosSignInHandler = handler +} + +export function getIOSSignInHandler(): IOSSignInHandler | null { + return iosSignInHandler +} diff --git a/packages/stage-ui/src/libs/auth.ts b/packages/stage-ui/src/libs/auth.ts index ceccd408bc..5ad6b5faf9 100644 --- a/packages/stage-ui/src/libs/auth.ts +++ b/packages/stage-ui/src/libs/auth.ts @@ -1,13 +1,12 @@ import type { OIDCFlowParams, TokenResponse } from './auth-oidc' -import { Capacitor } from '@capacitor/core' import { createAuthClient } from 'better-auth/vue' import { useAuthStore } from '../stores/auth' -import { completeOIDCCallbackUrl } from './auth-callback' import { OIDC_CLIENT_ID, OIDC_REDIRECT_URI } from './auth-config' -import { buildAuthorizationURL, consumeFlowState, exchangeCodeForTokens, persistFlowState } from './auth-oidc' -import { openNativeAuthSession } from './native-auth' +import { buildAuthorizationURL, persistFlowState } from './auth-oidc' +import { getIOSSignInHandler } from './auth-platform' +import { getCapacitorPlatform } from './capacitor-runtime' import { SERVER_URL } from './server' export type OAuthProvider = 'google' | 'github' @@ -186,25 +185,16 @@ export async function signOut() { * Builds the authorization URL, persists PKCE state, and navigates. */ export async function signInOIDC(params: OIDCFlowParams) { - // Must pass `params` (incl. `provider`) so authorize URL can include `?provider=google` / github. const { url, flowState } = await buildAuthorizationURL(params) persistFlowState(flowState, params) - let capacitorPlatform = 'web' - try { - capacitorPlatform = Capacitor.getPlatform() - } - catch { - // no Capacitor - } + const capacitorPlatform = getCapacitorPlatform() if (capacitorPlatform === 'ios') { - const callbackUrl = await openNativeAuthSession(url.toString()) - await completeOIDCCallbackUrl(callbackUrl, { - consumeFlowState, - exchangeCodeForTokens, - applyOIDCTokens, - fetchSession, - }) + const iosSignInHandler = getIOSSignInHandler() + if (!iosSignInHandler) + throw new Error('iOS sign-in is not configured') + + await iosSignInHandler(url.toString()) return } if (capacitorPlatform !== 'web') { diff --git a/packages/stage-ui/src/libs/capacitor-runtime.ts b/packages/stage-ui/src/libs/capacitor-runtime.ts new file mode 100644 index 0000000000..3601810410 --- /dev/null +++ b/packages/stage-ui/src/libs/capacitor-runtime.ts @@ -0,0 +1,23 @@ +export function getCapacitorPlatform(): string { + if (typeof window === 'undefined') + return 'web' + + // @ts-expect-error Capacitor is injected by the native runtime when available. + return window.Capacitor?.getPlatform?.() ?? 'web' +} + +export function isCapacitorNativePlatform(): boolean { + if (typeof window === 'undefined') + return false + + // @ts-expect-error Capacitor is injected by the native runtime when available. + const capacitor = window.Capacitor + if (!capacitor) + return false + + if (typeof capacitor.isNativePlatform === 'function') + return capacitor.isNativePlatform() + + const platform = capacitor.getPlatform?.() + return !!platform && platform !== 'web' +} diff --git a/packages/stage-ui/src/stores/auth.ts b/packages/stage-ui/src/stores/auth.ts index 52826000f3..2706f6385f 100644 --- a/packages/stage-ui/src/stores/auth.ts +++ b/packages/stage-ui/src/stores/auth.ts @@ -39,9 +39,8 @@ export const useAuthStore = defineStore('auth', () => { const credits = useLocalStorage('user/v1/flux', 0) - // Cross-app "user must log in" flag. Setting this to true starts the OIDC - // flow (`triggerSignIn`) so the browser hits `/api/auth/oauth2/authorize` - // and the server-hosted login UI (`/auth/sign-in`). Electron skips this + // Cross-app "user must log in" flag. Setting this to true triggers an + // immediate OIDC redirect on web (mobile + desktop). Electron skips this // path because controls-island-auth-button listens for IPC and handles // sign-in in the main process. const needsLogin = ref(false) @@ -51,12 +50,11 @@ export const useAuthStore = defineStore('auth', () => { if (isStageTamagotchi()) return try { - // Dynamic import avoids a circular dependency: `libs/auth` imports this store. const { triggerSignIn } = await import('../libs/auth') await triggerSignIn() } catch { - // User cancelled native sheet or transient network failure; flag cleared in `finally`. + // Native auth sheet cancelled or sign-in failed. } finally { needsLogin.value = false diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 657d65da8e..564ced4782 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1851,7 +1851,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/cap-vite': specifier: workspace:* version: link:../../packages/cap-vite @@ -1914,16 +1914,16 @@ importers: version: 4.6.4 unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' - version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) + version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@2.80.0) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -1938,7 +1938,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -2747,7 +2747,7 @@ importers: version: 1.2.45 '@intlify/unplugin-vue-i18n': specifier: 'catalog:' - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@proj-airi/iconify-meteocons': specifier: 'catalog:' version: 0.1.5 @@ -2810,16 +2810,16 @@ importers: version: 0.0.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-info: specifier: 'catalog:' - version: 1.3.2(esbuild@0.27.2)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: 'catalog:' - version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: 'catalog:' - version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@2.80.0) + version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) vite-plugin-mkcert: specifier: 'catalog:' version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) @@ -2834,7 +2834,7 @@ importers: version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: 'catalog:' - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@2.80.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -4032,9 +4032,6 @@ importers: packages/stage-ui: dependencies: - '@capacitor/core': - specifier: 'catalog:' - version: 8.3.1 '@date-fns/utc': specifier: 'catalog:' version: 2.1.1 From aa9e825d97ef0cc7d2aec832b5b6cccd911391d4 Mon Sep 17 00:00:00 2001 From: Lulu Date: Wed, 10 Jun 2026 17:22:45 +0800 Subject: [PATCH 28/28] fix(stage-pocket): wrap DevBridge TLS log for SwiftLint line length Co-authored-by: Cursor --- apps/stage-pocket/ios/App/App/DevBridgeViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift index c86f2c30c9..ed9004aaa2 100644 --- a/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift +++ b/apps/stage-pocket/ios/App/App/DevBridgeViewController.swift @@ -161,7 +161,9 @@ extension DevBridgeViewController: WKNavigationDelegate { } if nsError.code == -1200 { print( - "[DevBridge] TLS failed (-1200). Prefer an https URL the device trusts (e.g. frp or another reverse proxy to the dev API), or use http for local Vite when policy allows; see apps/server/README.md (Local HTTPS API)." + "[DevBridge] TLS failed (-1200). Prefer an https URL the device trusts " + + "(e.g. frp or another reverse proxy to the dev API), or use http for local Vite " + + "when policy allows; see apps/server/README.md (Local HTTPS API)." ) } }