@adminide-stack/yantra-mobile 12.0.64-alpha.1 → 12.0.64-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/contexts/ChatLiveStreamContext.js +5 -2
- package/lib/contexts/ChatLiveStreamContext.js.map +1 -1
- package/lib/features/canvas/surfaceIndex.js +4 -1
- package/lib/features/canvas/surfaceIndex.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +15 -2
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/invite/InviteDeepLink.js +202 -0
- package/lib/features/invite/InviteDeepLink.js.map +1 -0
- package/lib/features/invite/inviteLink.js +28 -0
- package/lib/features/invite/inviteLink.js.map +1 -0
- package/lib/features/invite/pendingInvite.js +42 -0
- package/lib/features/invite/pendingInvite.js.map +1 -0
- package/lib/features/memory/MemoryPressureResponder.js +21 -0
- package/lib/features/memory/MemoryPressureResponder.js.map +1 -0
- package/lib/features/memory/memoryPressure.js +24 -0
- package/lib/features/memory/memoryPressure.js.map +1 -0
- package/lib/features/memory/useMemoryPressureResponder.js +23 -0
- package/lib/features/memory/useMemoryPressureResponder.js.map +1 -0
- package/lib/hooks/mintSurfaceSystemToken.js +8 -0
- package/lib/hooks/mintSurfaceSystemToken.js.map +1 -1
- package/lib/hooks/useChatStream.js +96 -25
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/screens/Chat/index.js +73 -9
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/components/CanvasPrewarm.js +4 -2
- package/lib/screens/Home/components/CanvasPrewarm.js.map +1 -1
- package/lib/services/queuedGatewayMessages.js +145 -0
- package/lib/services/queuedGatewayMessages.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
const PENDING_INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3
|
+
const STORAGE_KEY = "yantra.invite.pending";
|
|
4
|
+
let AsyncStorage = null;
|
|
5
|
+
try {
|
|
6
|
+
AsyncStorage = (_a = require("@react-native-async-storage/async-storage").default) != null ? _a : null;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
AsyncStorage = null;
|
|
9
|
+
}
|
|
10
|
+
async function savePendingInvite(token, now = Date.now()) {
|
|
11
|
+
if (!AsyncStorage || !token) return;
|
|
12
|
+
try {
|
|
13
|
+
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
14
|
+
token,
|
|
15
|
+
savedAt: now
|
|
16
|
+
}));
|
|
17
|
+
} catch (e) {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function readPendingInvite(now = Date.now()) {
|
|
21
|
+
if (!AsyncStorage) return null;
|
|
22
|
+
try {
|
|
23
|
+
const raw = await AsyncStorage.getItem(STORAGE_KEY);
|
|
24
|
+
if (!raw) return null;
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
if (!(parsed == null ? void 0 : parsed.token) || typeof parsed.token !== "string") return null;
|
|
27
|
+
if (typeof parsed.savedAt !== "number" || now - parsed.savedAt > PENDING_INVITE_TTL_MS) {
|
|
28
|
+
await clearPendingInvite();
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return parsed.token;
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function clearPendingInvite() {
|
|
37
|
+
if (!AsyncStorage) return;
|
|
38
|
+
try {
|
|
39
|
+
await AsyncStorage.removeItem(STORAGE_KEY);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
}
|
|
42
|
+
}export{PENDING_INVITE_TTL_MS,clearPendingInvite,readPendingInvite,savePendingInvite};//# sourceMappingURL=pendingInvite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pendingInvite.js","sources":["../../../src/features/invite/pendingInvite.ts"],"sourcesContent":["/**\n * An invite token parked across the sign-in round trip.\n *\n * Tapping an invite link on a phone with no session sends the invitee through\n * login, and on iOS that can mean leaving the app entirely (Auth0's browser\n * tab). Whatever happens in between, the token has to survive - otherwise the\n * invitee comes back signed in but not a member, with nothing on screen\n * explaining why.\n *\n * Stored tokens expire with the invitation itself (7 days, set by\n * account-api-server when the JWT is signed), so a link that was never used\n * cannot resurface months later as a confusing prompt.\n */\n\n/** Matches the invitation JWT's own `expiresIn: '7d'`. */\nexport const PENDING_INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n\nconst STORAGE_KEY = 'yantra.invite.pending';\n\ntype StorageLike = {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n};\n\nlet AsyncStorage: StorageLike | null = null;\ntry {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n AsyncStorage = require('@react-native-async-storage/async-storage').default ?? null;\n} catch {\n AsyncStorage = null;\n}\n\nexport interface PendingInvite {\n token: string;\n savedAt: number;\n}\n\nexport async function savePendingInvite(token: string, now = Date.now()): Promise<void> {\n if (!AsyncStorage || !token) return;\n try {\n await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify({ token, savedAt: now }));\n } catch {\n /* best-effort: a lost token costs a re-tap, a thrown one costs the launch */\n }\n}\n\nexport async function readPendingInvite(now = Date.now()): Promise<string | null> {\n if (!AsyncStorage) return null;\n try {\n const raw = await AsyncStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Partial<PendingInvite>;\n if (!parsed?.token || typeof parsed.token !== 'string') return null;\n if (typeof parsed.savedAt !== 'number' || now - parsed.savedAt > PENDING_INVITE_TTL_MS) {\n await clearPendingInvite();\n return null;\n }\n return parsed.token;\n } catch {\n return null;\n }\n}\n\nexport async function clearPendingInvite(): Promise<void> {\n if (!AsyncStorage) return;\n try {\n await AsyncStorage.removeItem(STORAGE_KEY);\n } catch {\n /* best-effort */\n }\n}\n"],"names":[],"mappings":"AAAA,IAAA,EAAA;AAeO,MAAM,qBAAwB,GAAA,CAAA,GAAI,EAAK,GAAA,EAAA,GAAK,EAAK,GAAA;AACxD,MAAM,WAAc,GAAA,uBAAA;AAMpB,IAAI,YAAmC,GAAA,IAAA;AACvC,IAAI;AAEF,EAAA,YAAA,GAAA,CAAe,EAAQ,GAAA,OAAA,CAAA,2CAA2C,CAAE,CAAA,OAAA,KAArD,IAAgE,GAAA,EAAA,GAAA,IAAA;AACjF,CAAQ,CAAA,OAAA,CAAA,EAAA;AACN,EAAe,YAAA,GAAA,IAAA;AACjB;AAKA,eAAsB,iBAAkB,CAAA,KAAA,EAAe,GAAM,GAAA,IAAA,CAAK,KAAsB,EAAA;AACtF,EAAI,IAAA,CAAC,YAAgB,IAAA,CAAC,KAAO,EAAA;AAC7B,EAAI,IAAA;AACF,IAAA,MAAM,YAAa,CAAA,OAAA,CAAQ,WAAa,EAAA,IAAA,CAAK,SAAU,CAAA;AAAA,MACrD,KAAA;AAAA,MACA,OAAS,EAAA;AAAA,KACV,CAAC,CAAA;AAAA,GACI,CAAA,OAAA,CAAA,EAAA;AAAA;AAGV;AACA,eAAsB,iBAAkB,CAAA,GAAA,GAAM,IAAK,CAAA,GAAA,EAA+B,EAAA;AAChF,EAAI,IAAA,CAAC,cAAqB,OAAA,IAAA;AAC1B,EAAI,IAAA;AACF,IAAA,MAAM,GAAM,GAAA,MAAM,YAAa,CAAA,OAAA,CAAQ,WAAW,CAAA;AAClD,IAAI,IAAA,CAAC,KAAY,OAAA,IAAA;AACjB,IAAM,MAAA,MAAA,GAAS,IAAK,CAAA,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,EAAC,MAAQ,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,MAAA,CAAA,KAAA,CAAA,IAAS,OAAO,MAAO,CAAA,KAAA,KAAU,UAAiB,OAAA,IAAA;AAC/D,IAAA,IAAI,OAAO,MAAO,CAAA,OAAA,KAAY,YAAY,GAAM,GAAA,MAAA,CAAO,UAAU,qBAAuB,EAAA;AACtF,MAAA,MAAM,kBAAmB,EAAA;AACzB,MAAO,OAAA,IAAA;AAAA;AAET,IAAA,OAAO,MAAO,CAAA,KAAA;AAAA,GACR,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX;AACA,eAAsB,kBAAoC,GAAA;AACxD,EAAA,IAAI,CAAC,YAAc,EAAA;AACnB,EAAI,IAAA;AACF,IAAM,MAAA,YAAA,CAAa,WAAW,WAAW,CAAA;AAAA,GACnC,CAAA,OAAA,CAAA,EAAA;AAAA;AAGV"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {useEffect}from'react';import {useApolloClient}from'@apollo/client/index.js';import {clearCachedCanvasBoards}from'../canvas/surfaceIndex.js';import {registerMemoryReclaimer}from'./memoryPressure.js';import {useMemoryPressureResponder}from'./useMemoryPressureResponder.js';function MemoryPressureResponder() {
|
|
2
|
+
const apolloClient = useApolloClient();
|
|
3
|
+
useEffect(() => {
|
|
4
|
+
const offApollo = registerMemoryReclaimer({
|
|
5
|
+
id: "apollo-cache",
|
|
6
|
+
reclaim: () => {
|
|
7
|
+
apolloClient.cache.gc();
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
const offBoards = registerMemoryReclaimer({
|
|
11
|
+
id: "canvas-boards",
|
|
12
|
+
reclaim: () => clearCachedCanvasBoards()
|
|
13
|
+
});
|
|
14
|
+
return () => {
|
|
15
|
+
offApollo();
|
|
16
|
+
offBoards();
|
|
17
|
+
};
|
|
18
|
+
}, [apolloClient]);
|
|
19
|
+
useMemoryPressureResponder();
|
|
20
|
+
return null;
|
|
21
|
+
}export{MemoryPressureResponder,MemoryPressureResponder as default};//# sourceMappingURL=MemoryPressureResponder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryPressureResponder.js","sources":["../../../src/features/memory/MemoryPressureResponder.tsx"],"sourcesContent":["import React, { useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client';\nimport { clearCachedCanvasBoards } from '../canvas/surfaceIndex';\nimport { registerMemoryReclaimer } from './memoryPressure';\nimport { useMemoryPressureResponder } from './useMemoryPressureResponder';\n\n/**\n * Mount once at the app root. Listens for iOS memory warnings and for\n * backgrounding, and hands memory back instead of waiting to be killed.\n *\n * Renders nothing.\n *\n * Only caches that can be re-derived or re-fetched are dropped here - never\n * user input. The Apollo cache is the big one: `Query.messages` concatenates\n * every page it has ever seen with no cap and no eviction, and nothing in the\n * app had ever called `cache.gc()`.\n */\nexport function MemoryPressureResponder(): null {\n const apolloClient = useApolloClient();\n\n useEffect(() => {\n const offApollo = registerMemoryReclaimer({\n id: 'apollo-cache',\n reclaim: () => {\n // gc() only drops entries nothing references any more, so it\n // cannot invalidate data a mounted screen is still rendering.\n apolloClient.cache.gc();\n },\n });\n const offBoards = registerMemoryReclaimer({\n id: 'canvas-boards',\n reclaim: () => clearCachedCanvasBoards(),\n });\n return () => {\n offApollo();\n offBoards();\n };\n }, [apolloClient]);\n\n useMemoryPressureResponder();\n\n return null;\n}\n\nexport default MemoryPressureResponder;\n"],"names":[],"mappings":"uRAiBO,SAAS,uBAAgC,GAAA;AAC9C,EAAA,MAAM,eAAe,eAAgB,EAAA;AACrC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,YAAY,uBAAwB,CAAA;AAAA,MACxC,EAAI,EAAA,cAAA;AAAA,MACJ,SAAS,MAAM;AAGb,QAAA,YAAA,CAAa,MAAM,EAAG,EAAA;AAAA;AACxB,KACD,CAAA;AACD,IAAA,MAAM,YAAY,uBAAwB,CAAA;AAAA,MACxC,EAAI,EAAA,eAAA;AAAA,MACJ,OAAA,EAAS,MAAM,uBAAwB;AAAA,KACxC,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAU,SAAA,EAAA;AACV,MAAU,SAAA,EAAA;AAAA,KACZ;AAAA,GACF,EAAG,CAAC,YAAY,CAAC,CAAA;AACjB,EAA2B,0BAAA,EAAA;AAC3B,EAAO,OAAA,IAAA;AACT"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const reclaimers = /* @__PURE__ */ new Map();
|
|
2
|
+
function registerMemoryReclaimer(reclaimer) {
|
|
3
|
+
reclaimers.set(reclaimer.id, reclaimer);
|
|
4
|
+
return () => {
|
|
5
|
+
if (reclaimers.get(reclaimer.id) === reclaimer) reclaimers.delete(reclaimer.id);
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function runMemoryReclaim(reason) {
|
|
9
|
+
const ran = [];
|
|
10
|
+
const failed = [];
|
|
11
|
+
for (const reclaimer of Array.from(reclaimers.values())) {
|
|
12
|
+
try {
|
|
13
|
+
reclaimer.reclaim(reason);
|
|
14
|
+
ran.push(reclaimer.id);
|
|
15
|
+
} catch (e) {
|
|
16
|
+
failed.push(reclaimer.id);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
reason,
|
|
21
|
+
ran,
|
|
22
|
+
failed
|
|
23
|
+
};
|
|
24
|
+
}export{registerMemoryReclaimer,runMemoryReclaim};//# sourceMappingURL=memoryPressure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryPressure.js","sources":["../../../src/features/memory/memoryPressure.ts"],"sourcesContent":["/**\n * Respond to iOS memory pressure instead of waiting to be killed.\n *\n * iOS sends an app a memory warning BEFORE it evicts it, and it evicts the\n * largest suspended process first. This app never listened for that warning and\n * never released anything - measured 2026-09-18 on a 6GB iPhone 13 Pro, it is\n * terminated within ~5s of being backgrounded, so every return is a cold launch\n * that loses the user's screen and their unsent composer text.\n *\n * Two signals matter and both are handled here:\n * - `memoryWarning`: iOS is asking. Give memory back now.\n * - `background`: the moment our footprint decides whether we survive.\n *\n * Reclaimers are REGISTERED rather than hard-coded so a feature owns its own\n * cache: this module must not have to import the canvas board cache, the Apollo\n * client and the attachment store (which would drag all of them into the launch\n * graph - the very problem `lazyLocalExperience` exists to avoid).\n *\n * This is deliberately conservative: a reclaimer may only drop data that can be\n * re-derived or re-fetched. Nothing here may lose user input.\n */\n\nexport type MemoryReclaimReason = 'memory-warning' | 'background';\n\nexport interface IMemoryReclaimer {\n /** For logs, and so a reclaimer can be replaced rather than duplicated. */\n id: string;\n /**\n * Free what you can. MUST NOT throw - a failing reclaimer must not stop the\n * others, and must never take the app down while it is already under\n * pressure. Return a rough byte estimate if you have one, for logging only.\n */\n reclaim: (reason: MemoryReclaimReason) => void | number;\n}\n\nconst reclaimers = new Map<string, IMemoryReclaimer>();\n\n/** Register (or replace) a reclaimer. Returns an unregister function. */\nexport function registerMemoryReclaimer(reclaimer: IMemoryReclaimer): () => void {\n reclaimers.set(reclaimer.id, reclaimer);\n return () => {\n // Only remove it if it is still the same registration: a later\n // re-register of the same id owns the slot now.\n if (reclaimers.get(reclaimer.id) === reclaimer) reclaimers.delete(reclaimer.id);\n };\n}\n\nexport function registeredMemoryReclaimerIds(): string[] {\n return Array.from(reclaimers.keys());\n}\n\n/** Test seam. */\nexport function __clearMemoryReclaimersForTests(): void {\n reclaimers.clear();\n}\n\nexport interface IMemoryReclaimResult {\n reason: MemoryReclaimReason;\n ran: string[];\n failed: string[];\n}\n\n/**\n * Run every reclaimer. Never throws: one broken reclaimer must not prevent the\n * rest from freeing memory, and must not crash an app that is already being\n * squeezed.\n */\nexport function runMemoryReclaim(reason: MemoryReclaimReason): IMemoryReclaimResult {\n const ran: string[] = [];\n const failed: string[] = [];\n for (const reclaimer of Array.from(reclaimers.values())) {\n try {\n reclaimer.reclaim(reason);\n ran.push(reclaimer.id);\n } catch {\n failed.push(reclaimer.id);\n }\n }\n return { reason, ran, failed };\n}\n"],"names":[],"mappings":"AAiCA,MAAM,UAAA,uBAAiB,GAA8B,EAAA;AAG9C,SAAS,wBAAwB,SAAyC,EAAA;AAC/E,EAAW,UAAA,CAAA,GAAA,CAAI,SAAU,CAAA,EAAA,EAAI,SAAS,CAAA;AACtC,EAAA,OAAO,MAAM;AAGX,IAAI,IAAA,UAAA,CAAW,IAAI,SAAU,CAAA,EAAE,MAAM,SAAW,EAAA,UAAA,CAAW,MAAO,CAAA,SAAA,CAAU,EAAE,CAAA;AAAA,GAChF;AACF;AAoBO,SAAS,iBAAiB,MAAmD,EAAA;AAClF,EAAA,MAAM,MAAgB,EAAC;AACvB,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,KAAA,MAAW,aAAa,KAAM,CAAA,IAAA,CAAK,UAAW,CAAA,MAAA,EAAQ,CAAG,EAAA;AACvD,IAAI,IAAA;AACF,MAAA,SAAA,CAAU,QAAQ,MAAM,CAAA;AACxB,MAAI,GAAA,CAAA,IAAA,CAAK,UAAU,EAAE,CAAA;AAAA,KACf,CAAA,OAAA,CAAA,EAAA;AACN,MAAO,MAAA,CAAA,IAAA,CAAK,UAAU,EAAE,CAAA;AAAA;AAC1B;AAEF,EAAO,OAAA;AAAA,IACL,MAAA;AAAA,IACA,GAAA;AAAA,IACA;AAAA,GACF;AACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {useEffect}from'react';import {AppState}from'react-native';import {runMemoryReclaim}from'./memoryPressure.js';function useMemoryPressureResponder() {
|
|
2
|
+
useEffect(() => {
|
|
3
|
+
const onMemoryWarning = () => {
|
|
4
|
+
const result = runMemoryReclaim("memory-warning");
|
|
5
|
+
if (__DEV__) {
|
|
6
|
+
console.log("[memory] warning -> reclaimed:", result.ran.join(", ") || "(nothing registered)");
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
const onAppStateChange = (next) => {
|
|
10
|
+
if (next !== "background") return;
|
|
11
|
+
const result = runMemoryReclaim("background");
|
|
12
|
+
if (__DEV__) {
|
|
13
|
+
console.log("[memory] background -> reclaimed:", result.ran.join(", ") || "(nothing registered)");
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const warningSub = AppState.addEventListener("memoryWarning", onMemoryWarning);
|
|
17
|
+
const stateSub = AppState.addEventListener("change", onAppStateChange);
|
|
18
|
+
return () => {
|
|
19
|
+
warningSub.remove();
|
|
20
|
+
stateSub.remove();
|
|
21
|
+
};
|
|
22
|
+
}, []);
|
|
23
|
+
}export{useMemoryPressureResponder as default,useMemoryPressureResponder};//# sourceMappingURL=useMemoryPressureResponder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMemoryPressureResponder.js","sources":["../../../src/features/memory/useMemoryPressureResponder.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { AppState, AppStateStatus } from 'react-native';\nimport { runMemoryReclaim } from './memoryPressure';\n\n/**\n * Wire the reclaimers to the two signals that matter. Mount ONCE, at the app\n * root.\n *\n * `memoryWarning` is a real React Native AppState event (see\n * react-native/Libraries/AppState/AppState.js) that nothing in this app was\n * listening to, even though it is iOS telling us we are about to be killed.\n */\nexport function useMemoryPressureResponder(): void {\n useEffect(() => {\n const onMemoryWarning = () => {\n const result = runMemoryReclaim('memory-warning');\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[memory] warning -> reclaimed:', result.ran.join(', ') || '(nothing registered)');\n }\n };\n\n const onAppStateChange = (next: AppStateStatus) => {\n // Only 'background'. 'inactive' fires for the app switcher peek and\n // for call banners, and throwing caches away on those would make the\n // common case (a glance, then straight back) slower for no gain.\n if (next !== 'background') return;\n const result = runMemoryReclaim('background');\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[memory] background -> reclaimed:', result.ran.join(', ') || '(nothing registered)');\n }\n };\n\n const warningSub = AppState.addEventListener('memoryWarning', onMemoryWarning);\n const stateSub = AppState.addEventListener('change', onAppStateChange);\n return () => {\n warningSub.remove();\n stateSub.remove();\n };\n }, []);\n}\n\nexport default useMemoryPressureResponder;\n"],"names":[],"mappings":"qHAYO,SAAS,0BAAmC,GAAA;AACjD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,kBAAkB,MAAM;AAC5B,MAAM,MAAA,MAAA,GAAS,iBAAiB,gBAAgB,CAAA;AAChD,MAAA,IAAI,OAAS,EAAA;AAEX,QAAA,OAAA,CAAQ,IAAI,gCAAkC,EAAA,MAAA,CAAO,IAAI,IAAK,CAAA,IAAI,KAAK,sBAAsB,CAAA;AAAA;AAC/F,KACF;AACA,IAAM,MAAA,gBAAA,GAAmB,CAAC,IAAyB,KAAA;AAIjD,MAAA,IAAI,SAAS,YAAc,EAAA;AAC3B,MAAM,MAAA,MAAA,GAAS,iBAAiB,YAAY,CAAA;AAC5C,MAAA,IAAI,OAAS,EAAA;AAEX,QAAA,OAAA,CAAQ,IAAI,mCAAqC,EAAA,MAAA,CAAO,IAAI,IAAK,CAAA,IAAI,KAAK,sBAAsB,CAAA;AAAA;AAClG,KACF;AACA,IAAA,MAAM,UAAa,GAAA,QAAA,CAAS,gBAAiB,CAAA,eAAA,EAAiB,eAAe,CAAA;AAC7E,IAAA,MAAM,QAAW,GAAA,QAAA,CAAS,gBAAiB,CAAA,QAAA,EAAU,gBAAgB,CAAA;AACrE,IAAA,OAAO,MAAM;AACX,MAAA,UAAA,CAAW,MAAO,EAAA;AAClB,MAAA,QAAA,CAAS,MAAO,EAAA;AAAA,KAClB;AAAA,GACF,EAAG,EAAE,CAAA;AACP"}
|
|
@@ -191,6 +191,13 @@ async function mintSurfaceSystemToken(input) {
|
|
|
191
191
|
}
|
|
192
192
|
try {
|
|
193
193
|
const obtained = await gqlFetch(graphqlUrl, idToken, OBTAIN_USER_SYSTEM_TOKEN, {
|
|
194
|
+
// No `algorithm`: the backend signs with its platform default
|
|
195
|
+
// (DEFAULT_REGISTRY_API_TOKEN_ALGORITHM - RS256 on production).
|
|
196
|
+
// Pinning HS256 here flipped the user's ONE system-token row to
|
|
197
|
+
// HS256 on prod, and every other client (the web shell, the
|
|
198
|
+
// hosted cdecli-agent, the issues/calendar lanes) then revealed
|
|
199
|
+
// and presented that HS256 JWT: "invalid HS256 signature" at
|
|
200
|
+
// the agent, UNAUTHENTICATED at the subgraphs.
|
|
194
201
|
input: {
|
|
195
202
|
projectId,
|
|
196
203
|
environmentTagId,
|
|
@@ -233,6 +240,7 @@ async function mintSurfaceSystemToken(input) {
|
|
|
233
240
|
let tokenId = (_q = (_p = (_o = getResult.data) == null ? void 0 : _o.getUserSystemToken) == null ? void 0 : _p.id) != null ? _q : null;
|
|
234
241
|
if (!tokenId) {
|
|
235
242
|
const createResult = await gqlFetch(graphqlUrl, idToken, CREATE_SECRET_API_TOKEN, {
|
|
243
|
+
// Platform-default algorithm, same reason as the obtain call above.
|
|
236
244
|
input: {
|
|
237
245
|
name: "System Token",
|
|
238
246
|
expiryInSeconds: 31536e3,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintSurfaceSystemToken.js","sources":["../../src/hooks/mintSurfaceSystemToken.ts"],"sourcesContent":["import { AuthStrategy, HttpHeaders } from 'common';\nimport { readAuth0IdToken } from '../bridge/readAuth0Session';\nimport { config } from '../config/env-config';\n\nconst OBTAIN_USER_SYSTEM_TOKEN = `\n mutation ObtainUserSystemToken($input: ObtainUserSystemTokenInput!) {\n obtainUserSystemToken(input: $input) {\n token\n }\n }\n`;\n\nconst GET_USER_SYSTEM_TOKEN = `\n query GetUserSystemToken {\n getUserSystemToken {\n id\n expiresAt\n isSystemToken\n }\n }\n`;\n\nconst GET_USER_ORGANIZATIONS = `\n query GetUserOrganizations {\n getUserOrganizations {\n name\n }\n }\n`;\n\nconst GET_PROJECTS = `\n query GetProjects($orgName: String!) {\n getProjects(orgName: $orgName) {\n data {\n id\n }\n }\n }\n`;\n\nconst GET_ENVIRONMENT_TAGS = `\n query GetEnvironmentTags($projectId: ID) {\n getEnvironmentTags(projectId: $projectId) {\n id\n }\n }\n`;\n\nconst CREATE_SECRET_API_TOKEN = `\n mutation CreateSecretApiToken($input: CreateSecretApiTokenInput!) {\n createSecretApiToken(input: $input) {\n id\n expiresAt\n }\n }\n`;\n\nconst REVEAL_SECRET_API_TOKEN = `\n mutation RevealSecretApiToken($id: ID!) {\n revealSecretApiToken(id: $id)\n }\n`;\n\ntype GqlResult<T> = {\n data?: T;\n errors?: Array<{ message?: string }>;\n};\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\n/** App GRAPHQL_URL from env (optional override for device LAN rewrite). */\nexport function resolveSurfaceCatalogBackend(graphqlUrl?: string): string {\n const raw = graphqlUrl?.trim() || config.GRAPHQL_URL;\n if (!raw?.trim()) {\n throw new Error('GRAPHQL_URL is not configured');\n }\n return normalizeGraphqlUrl(raw);\n}\n\nexport { isCanvasSurfaceUrl, resolveSurfaceAuthGraphqlUrl } from './resolveSurfaceAuthGraphqlUrl';\nexport type { SurfaceAuthUrlConfig } from './resolveSurfaceAuthGraphqlUrl';\n\nasync function gqlFetch<T>(\n graphqlUrl: string,\n idToken: string,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<GqlResult<T>> {\n // Staging JWT middleware only treats Bearer as mobile/SPA when\n // `ismobile` (or `isspa`) is set — without it we get HTTP 401 Unauthorized.\n // Match Apollo mobile authHeadersLink + Auth0Client.commonFetchRequestHeaders.\n const res = await fetch(graphqlUrl, {\n method: 'POST',\n headers: {\n [HttpHeaders.CONTENT_TYPE]: 'application/json',\n [HttpHeaders.AUTHORIZATION]: `Bearer ${idToken}`,\n [HttpHeaders.IS_MOBILE]: 'true',\n [HttpHeaders.AUTH_STRATEGY]: AuthStrategy.Auth0,\n },\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`GraphQL HTTP ${res.status}: ${text.slice(0, 200)}`);\n }\n return (await res.json()) as GqlResult<T>;\n}\n\nconst REVOKE_SECRET_API_TOKEN = `\n mutation RevokeSecretApiToken($id: ID!) {\n revokeSecretApiToken(id: $id)\n }\n`;\n\nexport type MintSurfaceTokenInput = {\n /** App GRAPHQL_URL (e.g. device-rewritten). Defaults to env GRAPHQL_URL. */\n graphqlUrl?: string;\n orgName?: string | null;\n projectId?: string | null;\n tagId?: string | null;\n /**\n * Resolve org/project/env on `graphqlUrl` instead of using the mobile\n * app's local ids. Required when minting against a different cluster\n * than GRAPHQL_URL (canvas index lives on yantra-app-v1).\n */\n discoverScope?: boolean;\n /** Revoke the existing system-token row before obtain/create (dead signature). */\n forceRemint?: boolean;\n};\n\nexport type MintSurfaceTokenResult = {\n token: string;\n /** Exact backend URL string to stamp into cdecli_token_meta. */\n backend: string;\n};\n\n/** Local-dev testing project — must never mint against this on staging/prod. */\nconst DEV_TESTING_PROJECT_UUID = '9a8ac704-1040-4836-9b72-48b771ca91b1';\n\nconst OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;\nconst PROJECT_UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nasync function discoverMintScope(\n graphqlUrl: string,\n idToken: string,\n): Promise<{ orgName: string; projectId: string; environmentTagId: string }> {\n const orgs = await gqlFetch<{ getUserOrganizations?: Array<{ name?: string | null } | null> | null }>(\n graphqlUrl,\n idToken,\n GET_USER_ORGANIZATIONS,\n );\n if (orgs.errors?.length) {\n throw new Error(orgs.errors[0]?.message || 'getUserOrganizations failed');\n }\n const orgName = orgs.data?.getUserOrganizations?.find((o) => o?.name?.trim())?.name?.trim() || '';\n if (!orgName) {\n throw new Error(\n 'No organization on the canvas backend. Sign in once in the canvas harness (Chrome on localhost:5175), then retry from the app.',\n );\n }\n\n const projects = await gqlFetch<{ getProjects?: { data?: Array<{ id?: string | null } | null> | null } | null }>(\n graphqlUrl,\n idToken,\n GET_PROJECTS,\n { orgName },\n );\n if (projects.errors?.length) {\n throw new Error(projects.errors[0]?.message || 'getProjects failed');\n }\n const projectId = projects.data?.getProjects?.data?.find((p) => p?.id?.trim())?.id?.trim() || '';\n if (!projectId) {\n throw new Error(`No project on the canvas backend for org \"${orgName}\".`);\n }\n\n const tags = await gqlFetch<{ getEnvironmentTags?: Array<{ id?: string | null } | null> | null }>(\n graphqlUrl,\n idToken,\n GET_ENVIRONMENT_TAGS,\n { projectId },\n );\n if (tags.errors?.length) {\n throw new Error(tags.errors[0]?.message || 'getEnvironmentTags failed');\n }\n const environmentTagId = tags.data?.getEnvironmentTags?.find((t) => t?.id?.trim())?.id?.trim() || '';\n if (!environmentTagId) {\n throw new Error(`No environment tag on the canvas backend for project \"${projectId}\".`);\n }\n\n return { orgName, projectId, environmentTagId };\n}\n\nfunction assertMintableProjectId(projectId: string): void {\n if (projectId === 'default' || !projectId.trim()) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!__DEV__ && projectId === DEV_TESTING_PROJECT_UUID) {\n throw new Error(\n 'Invalid projectId (dev testing UUID). Open Home so a real project can be created, then retry.',\n );\n }\n if (!OBJECT_ID_HEX.test(projectId) && !PROJECT_UUID.test(projectId)) {\n throw new Error(`Invalid projectId format for system token mint: ${projectId.slice(0, 48)}`);\n }\n}\n\n/**\n * Reveal (or obtain) this user's system token on GRAPHQL_URL.\n *\n * ── Never pin a signing algorithm here ──────────────────────────────────────\n * The system token is ONE ROW PER USER, shared by every lane that user touches:\n * this app, the web shell, the embedded surfaces, and the workflow backend. The\n * mint honours whatever `algorithm` it is asked for and re-mints the row when\n * the request disagrees with what is stored - so a pin set here does not just\n * choose this app's credential, it REPLACES everyone's.\n *\n * These two calls used to pin `HS256`. `DEFAULT_REGISTRY_API_TOKEN_ALGORITHM`\n * is `RS256` on both clusters and cdecli-agent verifies signatures itself, so\n * connecting from the app rewrote the shared row to HS256 and the web lanes\n * that read it (meetings, design, canvas) then failed with\n * `cdecli-agent HTTP 401: invalid token: invalid HS256 signature`.\n *\n * Omitting `algorithm` lets the backend sign with the platform default, which\n * is the same thing every other mint in the product does (browser\n * `agentSystemToken.fetchAndCacheToken`, mobile `useObtainSystemToken`).\n * ────────────────────────────────────────────────────────────────────────────\n */\nexport async function mintSurfaceSystemToken(input: MintSurfaceTokenInput): Promise<MintSurfaceTokenResult> {\n const idToken = await readAuth0IdToken();\n if (!idToken) {\n throw new Error('No Auth0 id_token in storage — sign in to the mobile app first.');\n }\n\n const graphqlUrl = resolveSurfaceCatalogBackend(input.graphqlUrl);\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minting system token via', graphqlUrl, input.discoverScope ? '(discover scope)' : '');\n }\n\n let orgName = input.orgName?.trim() || '';\n let projectId = input.projectId?.trim() || '';\n let environmentTagId = input.tagId?.trim() || '';\n\n if (input.discoverScope) {\n const scope = await discoverMintScope(graphqlUrl, idToken);\n orgName = scope.orgName;\n projectId = scope.projectId;\n environmentTagId = scope.environmentTagId;\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] discovered canvas scope', { orgName, projectId, environmentTagId });\n }\n }\n\n if (!orgName) {\n throw new Error('Missing orgName — wait for org bootstrap, then retry.');\n }\n if (!projectId) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!environmentTagId) {\n throw new Error('Missing environmentTagId — cannot mint system token.');\n }\n assertMintableProjectId(projectId);\n\n if (input.forceRemint) {\n const existing = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n const existingId = existing.data?.getUserSystemToken?.id ?? null;\n if (existingId) {\n await gqlFetch(graphqlUrl, idToken, REVOKE_SECRET_API_TOKEN, { id: existingId }).catch(() => undefined);\n }\n }\n\n // Canonical get-or-create (resolves project UUID → Mongo id server-side).\n try {\n const obtained = await gqlFetch<{ obtainUserSystemToken?: { token?: string } | null }>(\n graphqlUrl,\n idToken,\n OBTAIN_USER_SYSTEM_TOKEN,\n {\n input: {\n projectId,\n environmentTagId,\n orgName,\n renewBufferSeconds: 7 * 24 * 60 * 60,\n // NO `algorithm` - see the mint-lane note above.\n },\n },\n );\n const token = obtained.data?.obtainUserSystemToken?.token?.trim();\n if (token && !obtained.errors?.length) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] obtained system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n }\n if (obtained.errors?.length) {\n throw new Error(obtained.errors[0]?.message || 'obtainUserSystemToken failed');\n }\n } catch (err) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.warn('[WebBuilder] obtainUserSystemToken failed, falling back to reveal:', err);\n }\n // Fall through to get/create/reveal only when obtain is unavailable.\n const msg = err instanceof Error ? err.message : String(err);\n if (/12 bytes|24 hex|Project not found/i.test(msg)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n // Continue to legacy path for older backends without obtainUserSystemToken.\n if (!/Unknown field|Cannot query field|obtainUserSystemToken/i.test(msg)) {\n // Non-schema errors from obtain should surface (auth, project, etc.).\n if (/Unauthorized|401|Project not found|Invalid project/i.test(msg)) {\n throw err instanceof Error ? err : new Error(msg);\n }\n }\n }\n\n const getResult = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n if (getResult.errors?.length) {\n throw new Error(getResult.errors[0]?.message || 'GetUserSystemToken failed');\n }\n\n let tokenId = getResult.data?.getUserSystemToken?.id ?? null;\n\n if (!tokenId) {\n const createResult = await gqlFetch<{ createSecretApiToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n CREATE_SECRET_API_TOKEN,\n {\n input: {\n name: 'System Token',\n expiryInSeconds: 31536000,\n isSystemToken: true,\n environmentTagId,\n orgName,\n projectId,\n // NO `algorithm` - see the mint-lane note above.\n },\n },\n );\n\n const createErr = createResult.errors?.[0]?.message || '';\n if (createErr && !/already have a system token/i.test(createErr)) {\n if (/12 bytes|24 hex/i.test(createErr)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n throw new Error(createErr);\n }\n\n if (createResult.data?.createSecretApiToken?.id) {\n tokenId = createResult.data.createSecretApiToken.id;\n } else {\n const refetch = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n tokenId = refetch.data?.getUserSystemToken?.id ?? null;\n }\n }\n\n if (!tokenId) {\n throw new Error('No system token id on GraphQL backend — cannot reveal.');\n }\n\n const reveal = await gqlFetch<{ revealSecretApiToken?: string | null }>(\n graphqlUrl,\n idToken,\n REVEAL_SECRET_API_TOKEN,\n { id: tokenId },\n );\n if (reveal.errors?.length) {\n throw new Error(reveal.errors[0]?.message || 'RevealSecretApiToken failed');\n }\n\n const token = reveal.data?.revealSecretApiToken?.trim();\n if (!token) {\n throw new Error('RevealSecretApiToken returned empty token');\n }\n\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minted system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n}\n\nexport function appendSurfaceAuthParams(baseUrl: string, token: string, state: string): string {\n try {\n const url = new URL(baseUrl);\n url.searchParams.set('token', token);\n url.searchParams.set('code', token);\n url.searchParams.set('state', state);\n return url.toString();\n } catch {\n const sep = baseUrl.includes('?') ? '&' : '?';\n return `${baseUrl}${sep}token=${encodeURIComponent(token)}&code=${encodeURIComponent(token)}&state=${encodeURIComponent(state)}`;\n }\n}\n\nfunction tokenFingerprint(token: string): string {\n const sig = token.split('.')[2] ?? token;\n return sig.slice(-24);\n}\n\n/** Shared WebView boot script: token + meta + fetch shims for surface-kit auth. */\nfunction buildSurfaceHandoffScript(token: string, backend: string, state: string, withGateRecovery: boolean): string {\n const safeToken = JSON.stringify(token);\n const safeBackend = JSON.stringify(backend);\n const safeState = JSON.stringify(state);\n const fp = JSON.stringify(tokenFingerprint(token));\n const recovery = withGateRecovery\n ? `\n function tryRecover() {\n stamp();\n var gate = document.querySelector('[data-surface-login]');\n if (!gate) return;\n if (sessionStorage.getItem('__yantra_wb_token_reload')) return;\n sessionStorage.setItem('__yantra_wb_token_reload', '1');\n var u = new URL(window.location.href);\n u.searchParams.set('token', t);\n u.searchParams.set('code', t);\n u.searchParams.set('state', state);\n window.location.replace(u.toString());\n }\n tryRecover();\n var i = 0;\n var timer = setInterval(function () {\n i += 1;\n tryRecover();\n if (i >= 40 || !document.querySelector('[data-surface-login]')) clearInterval(timer);\n }, 250);\n`\n : `\n var n = 0;\n var id = setInterval(function () {\n stamp();\n n += 1;\n if (n >= 80) clearInterval(id);\n }, 50);\n`;\n\n return `\n(function () {\n try {\n var t = ${safeToken};\n var backend = ${safeBackend};\n var state = ${safeState};\n var fp = ${fp};\n var PROBE_Q = 'query { getUserAccount { id } }';\n\n if (!window.__yantraWbFetchPatched) {\n window.__yantraWbFetchPatched = true;\n var origFetch = window.fetch.bind(window);\n window.fetch = function (input, init) {\n try {\n var url = typeof input === 'string' ? input : (input && input.url) ? String(input.url) : '';\n // Force surface-kit resolveCatalogBackend() to our GRAPHQL_URL (not remote agent → ideback).\n if (url.indexOf('/v1/auth/context') !== -1) {\n return Promise.resolve(new Response(\n JSON.stringify({ backendGraphqlUrl: backend }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n var body = init && typeof init.body === 'string' ? init.body : '';\n if (body && body.indexOf('getUserAccount') !== -1) {\n var parsed = JSON.parse(body);\n var q = parsed && typeof parsed.query === 'string' ? parsed.query.replace(/\\\\s+/g, ' ').trim() : '';\n if (q === PROBE_Q) {\n return Promise.resolve(new Response(\n JSON.stringify({ data: { getUserAccount: { id: 'mobile-webview-handoff' } } }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n }\n } catch (e0) {}\n return origFetch(input, init);\n };\n }\n\n function stamp() {\n try {\n sessionStorage.setItem('cdecli_oauth_state', state);\n localStorage.setItem('cdecli_token', t);\n sessionStorage.setItem('cdecli_token', t);\n localStorage.setItem('cdecli_system_token', t);\n localStorage.setItem('cdecli_token_meta', JSON.stringify({\n backend: backend,\n verifiedAt: Date.now(),\n fp: fp\n }));\n } catch (e1) {}\n }\n stamp();\n ${recovery}\n } catch (e) {}\n true;\n})();\ntrue;\n`;\n}\n\nexport function buildCdecliTokenInjection(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, false);\n}\n\nexport function buildSurfaceGateRecoveryScript(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, true);\n}\n"],"names":["_a","token"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mintSurfaceSystemToken.js","sources":["../../src/hooks/mintSurfaceSystemToken.ts"],"sourcesContent":["import { AuthStrategy, HttpHeaders } from 'common';\nimport { readAuth0IdToken } from '../bridge/readAuth0Session';\nimport { config } from '../config/env-config';\n\nconst OBTAIN_USER_SYSTEM_TOKEN = `\n mutation ObtainUserSystemToken($input: ObtainUserSystemTokenInput!) {\n obtainUserSystemToken(input: $input) {\n token\n }\n }\n`;\n\nconst GET_USER_SYSTEM_TOKEN = `\n query GetUserSystemToken {\n getUserSystemToken {\n id\n expiresAt\n isSystemToken\n }\n }\n`;\n\nconst GET_USER_ORGANIZATIONS = `\n query GetUserOrganizations {\n getUserOrganizations {\n name\n }\n }\n`;\n\nconst GET_PROJECTS = `\n query GetProjects($orgName: String!) {\n getProjects(orgName: $orgName) {\n data {\n id\n }\n }\n }\n`;\n\nconst GET_ENVIRONMENT_TAGS = `\n query GetEnvironmentTags($projectId: ID) {\n getEnvironmentTags(projectId: $projectId) {\n id\n }\n }\n`;\n\nconst CREATE_SECRET_API_TOKEN = `\n mutation CreateSecretApiToken($input: CreateSecretApiTokenInput!) {\n createSecretApiToken(input: $input) {\n id\n expiresAt\n }\n }\n`;\n\nconst REVEAL_SECRET_API_TOKEN = `\n mutation RevealSecretApiToken($id: ID!) {\n revealSecretApiToken(id: $id)\n }\n`;\n\ntype GqlResult<T> = {\n data?: T;\n errors?: Array<{ message?: string }>;\n};\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\n/** App GRAPHQL_URL from env (optional override for device LAN rewrite). */\nexport function resolveSurfaceCatalogBackend(graphqlUrl?: string): string {\n const raw = graphqlUrl?.trim() || config.GRAPHQL_URL;\n if (!raw?.trim()) {\n throw new Error('GRAPHQL_URL is not configured');\n }\n return normalizeGraphqlUrl(raw);\n}\n\nexport { isCanvasSurfaceUrl, resolveSurfaceAuthGraphqlUrl } from './resolveSurfaceAuthGraphqlUrl';\nexport type { SurfaceAuthUrlConfig } from './resolveSurfaceAuthGraphqlUrl';\n\nasync function gqlFetch<T>(\n graphqlUrl: string,\n idToken: string,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<GqlResult<T>> {\n // Staging JWT middleware only treats Bearer as mobile/SPA when\n // `ismobile` (or `isspa`) is set — without it we get HTTP 401 Unauthorized.\n // Match Apollo mobile authHeadersLink + Auth0Client.commonFetchRequestHeaders.\n const res = await fetch(graphqlUrl, {\n method: 'POST',\n headers: {\n [HttpHeaders.CONTENT_TYPE]: 'application/json',\n [HttpHeaders.AUTHORIZATION]: `Bearer ${idToken}`,\n [HttpHeaders.IS_MOBILE]: 'true',\n [HttpHeaders.AUTH_STRATEGY]: AuthStrategy.Auth0,\n },\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`GraphQL HTTP ${res.status}: ${text.slice(0, 200)}`);\n }\n return (await res.json()) as GqlResult<T>;\n}\n\nconst REVOKE_SECRET_API_TOKEN = `\n mutation RevokeSecretApiToken($id: ID!) {\n revokeSecretApiToken(id: $id)\n }\n`;\n\nexport type MintSurfaceTokenInput = {\n /** App GRAPHQL_URL (e.g. device-rewritten). Defaults to env GRAPHQL_URL. */\n graphqlUrl?: string;\n orgName?: string | null;\n projectId?: string | null;\n tagId?: string | null;\n /**\n * Resolve org/project/env on `graphqlUrl` instead of using the mobile\n * app's local ids. Required when minting against a different cluster\n * than GRAPHQL_URL (canvas index lives on yantra-app-v1).\n */\n discoverScope?: boolean;\n /** Revoke the existing system-token row before obtain/create (dead signature). */\n forceRemint?: boolean;\n};\n\nexport type MintSurfaceTokenResult = {\n token: string;\n /** Exact backend URL string to stamp into cdecli_token_meta. */\n backend: string;\n};\n\n/** Local-dev testing project — must never mint against this on staging/prod. */\nconst DEV_TESTING_PROJECT_UUID = '9a8ac704-1040-4836-9b72-48b771ca91b1';\n\nconst OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;\nconst PROJECT_UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nasync function discoverMintScope(\n graphqlUrl: string,\n idToken: string,\n): Promise<{ orgName: string; projectId: string; environmentTagId: string }> {\n const orgs = await gqlFetch<{ getUserOrganizations?: Array<{ name?: string | null } | null> | null }>(\n graphqlUrl,\n idToken,\n GET_USER_ORGANIZATIONS,\n );\n if (orgs.errors?.length) {\n throw new Error(orgs.errors[0]?.message || 'getUserOrganizations failed');\n }\n const orgName = orgs.data?.getUserOrganizations?.find((o) => o?.name?.trim())?.name?.trim() || '';\n if (!orgName) {\n throw new Error(\n 'No organization on the canvas backend. Sign in once in the canvas harness (Chrome on localhost:5175), then retry from the app.',\n );\n }\n\n const projects = await gqlFetch<{ getProjects?: { data?: Array<{ id?: string | null } | null> | null } | null }>(\n graphqlUrl,\n idToken,\n GET_PROJECTS,\n { orgName },\n );\n if (projects.errors?.length) {\n throw new Error(projects.errors[0]?.message || 'getProjects failed');\n }\n const projectId = projects.data?.getProjects?.data?.find((p) => p?.id?.trim())?.id?.trim() || '';\n if (!projectId) {\n throw new Error(`No project on the canvas backend for org \"${orgName}\".`);\n }\n\n const tags = await gqlFetch<{ getEnvironmentTags?: Array<{ id?: string | null } | null> | null }>(\n graphqlUrl,\n idToken,\n GET_ENVIRONMENT_TAGS,\n { projectId },\n );\n if (tags.errors?.length) {\n throw new Error(tags.errors[0]?.message || 'getEnvironmentTags failed');\n }\n const environmentTagId = tags.data?.getEnvironmentTags?.find((t) => t?.id?.trim())?.id?.trim() || '';\n if (!environmentTagId) {\n throw new Error(`No environment tag on the canvas backend for project \"${projectId}\".`);\n }\n\n return { orgName, projectId, environmentTagId };\n}\n\nfunction assertMintableProjectId(projectId: string): void {\n if (projectId === 'default' || !projectId.trim()) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!__DEV__ && projectId === DEV_TESTING_PROJECT_UUID) {\n throw new Error(\n 'Invalid projectId (dev testing UUID). Open Home so a real project can be created, then retry.',\n );\n }\n if (!OBJECT_ID_HEX.test(projectId) && !PROJECT_UUID.test(projectId)) {\n throw new Error(`Invalid projectId format for system token mint: ${projectId.slice(0, 48)}`);\n }\n}\n\n/**\n * Reveal (or obtain) this user's system token on GRAPHQL_URL.\n *\n * ── Never pin a signing algorithm here ──────────────────────────────────────\n * The system token is ONE ROW PER USER, shared by every lane that user touches:\n * this app, the web shell, the embedded surfaces, and the workflow backend. The\n * mint honours whatever `algorithm` it is asked for and re-mints the row when\n * the request disagrees with what is stored - so a pin set here does not just\n * choose this app's credential, it REPLACES everyone's.\n *\n * These two calls used to pin `HS256`. `DEFAULT_REGISTRY_API_TOKEN_ALGORITHM`\n * is `RS256` on both clusters and cdecli-agent verifies signatures itself, so\n * connecting from the app rewrote the shared row to HS256 and the web lanes\n * that read it (meetings, design, canvas) then failed with\n * `cdecli-agent HTTP 401: invalid token: invalid HS256 signature`.\n *\n * Omitting `algorithm` lets the backend sign with the platform default, which\n * is the same thing every other mint in the product does (browser\n * `agentSystemToken.fetchAndCacheToken`, mobile `useObtainSystemToken`).\n * ────────────────────────────────────────────────────────────────────────────\n */\nexport async function mintSurfaceSystemToken(input: MintSurfaceTokenInput): Promise<MintSurfaceTokenResult> {\n const idToken = await readAuth0IdToken();\n if (!idToken) {\n throw new Error('No Auth0 id_token in storage — sign in to the mobile app first.');\n }\n\n const graphqlUrl = resolveSurfaceCatalogBackend(input.graphqlUrl);\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minting system token via', graphqlUrl, input.discoverScope ? '(discover scope)' : '');\n }\n\n let orgName = input.orgName?.trim() || '';\n let projectId = input.projectId?.trim() || '';\n let environmentTagId = input.tagId?.trim() || '';\n\n if (input.discoverScope) {\n const scope = await discoverMintScope(graphqlUrl, idToken);\n orgName = scope.orgName;\n projectId = scope.projectId;\n environmentTagId = scope.environmentTagId;\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] discovered canvas scope', { orgName, projectId, environmentTagId });\n }\n }\n\n if (!orgName) {\n throw new Error('Missing orgName — wait for org bootstrap, then retry.');\n }\n if (!projectId) {\n throw new Error('Missing projectId — wait for org/project bootstrap, then retry.');\n }\n if (!environmentTagId) {\n throw new Error('Missing environmentTagId — cannot mint system token.');\n }\n assertMintableProjectId(projectId);\n\n if (input.forceRemint) {\n const existing = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n const existingId = existing.data?.getUserSystemToken?.id ?? null;\n if (existingId) {\n await gqlFetch(graphqlUrl, idToken, REVOKE_SECRET_API_TOKEN, { id: existingId }).catch(() => undefined);\n }\n }\n\n // Canonical get-or-create (resolves project UUID → Mongo id server-side).\n try {\n const obtained = await gqlFetch<{ obtainUserSystemToken?: { token?: string } | null }>(\n graphqlUrl,\n idToken,\n OBTAIN_USER_SYSTEM_TOKEN,\n {\n // No `algorithm`: the backend signs with its platform default\n // (DEFAULT_REGISTRY_API_TOKEN_ALGORITHM - RS256 on production).\n // Pinning HS256 here flipped the user's ONE system-token row to\n // HS256 on prod, and every other client (the web shell, the\n // hosted cdecli-agent, the issues/calendar lanes) then revealed\n // and presented that HS256 JWT: \"invalid HS256 signature\" at\n // the agent, UNAUTHENTICATED at the subgraphs.\n input: {\n projectId,\n environmentTagId,\n orgName,\n renewBufferSeconds: 7 * 24 * 60 * 60,\n // NO `algorithm` - see the mint-lane note above.\n },\n },\n );\n const token = obtained.data?.obtainUserSystemToken?.token?.trim();\n if (token && !obtained.errors?.length) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] obtained system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n }\n if (obtained.errors?.length) {\n throw new Error(obtained.errors[0]?.message || 'obtainUserSystemToken failed');\n }\n } catch (err) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.warn('[WebBuilder] obtainUserSystemToken failed, falling back to reveal:', err);\n }\n // Fall through to get/create/reveal only when obtain is unavailable.\n const msg = err instanceof Error ? err.message : String(err);\n if (/12 bytes|24 hex|Project not found/i.test(msg)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n // Continue to legacy path for older backends without obtainUserSystemToken.\n if (!/Unknown field|Cannot query field|obtainUserSystemToken/i.test(msg)) {\n // Non-schema errors from obtain should surface (auth, project, etc.).\n if (/Unauthorized|401|Project not found|Invalid project/i.test(msg)) {\n throw err instanceof Error ? err : new Error(msg);\n }\n }\n }\n\n const getResult = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n if (getResult.errors?.length) {\n throw new Error(getResult.errors[0]?.message || 'GetUserSystemToken failed');\n }\n\n let tokenId = getResult.data?.getUserSystemToken?.id ?? null;\n\n if (!tokenId) {\n const createResult = await gqlFetch<{ createSecretApiToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n CREATE_SECRET_API_TOKEN,\n {\n // Platform-default algorithm, same reason as the obtain call above.\n input: {\n name: 'System Token',\n expiryInSeconds: 31536000,\n isSystemToken: true,\n environmentTagId,\n orgName,\n projectId,\n // NO `algorithm` - see the mint-lane note above.\n },\n },\n );\n\n const createErr = createResult.errors?.[0]?.message || '';\n if (createErr && !/already have a system token/i.test(createErr)) {\n if (/12 bytes|24 hex/i.test(createErr)) {\n throw new Error(\n `Project \"${projectId}\" is not valid on this backend. Pick/create a real project in the app, then retry.`,\n );\n }\n throw new Error(createErr);\n }\n\n if (createResult.data?.createSecretApiToken?.id) {\n tokenId = createResult.data.createSecretApiToken.id;\n } else {\n const refetch = await gqlFetch<{ getUserSystemToken?: { id?: string } | null }>(\n graphqlUrl,\n idToken,\n GET_USER_SYSTEM_TOKEN,\n );\n tokenId = refetch.data?.getUserSystemToken?.id ?? null;\n }\n }\n\n if (!tokenId) {\n throw new Error('No system token id on GraphQL backend — cannot reveal.');\n }\n\n const reveal = await gqlFetch<{ revealSecretApiToken?: string | null }>(\n graphqlUrl,\n idToken,\n REVEAL_SECRET_API_TOKEN,\n { id: tokenId },\n );\n if (reveal.errors?.length) {\n throw new Error(reveal.errors[0]?.message || 'RevealSecretApiToken failed');\n }\n\n const token = reveal.data?.revealSecretApiToken?.trim();\n if (!token) {\n throw new Error('RevealSecretApiToken returned empty token');\n }\n\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log('[WebBuilder] minted system token length=', token.length);\n }\n return { token, backend: graphqlUrl };\n}\n\nexport function appendSurfaceAuthParams(baseUrl: string, token: string, state: string): string {\n try {\n const url = new URL(baseUrl);\n url.searchParams.set('token', token);\n url.searchParams.set('code', token);\n url.searchParams.set('state', state);\n return url.toString();\n } catch {\n const sep = baseUrl.includes('?') ? '&' : '?';\n return `${baseUrl}${sep}token=${encodeURIComponent(token)}&code=${encodeURIComponent(token)}&state=${encodeURIComponent(state)}`;\n }\n}\n\nfunction tokenFingerprint(token: string): string {\n const sig = token.split('.')[2] ?? token;\n return sig.slice(-24);\n}\n\n/** Shared WebView boot script: token + meta + fetch shims for surface-kit auth. */\nfunction buildSurfaceHandoffScript(token: string, backend: string, state: string, withGateRecovery: boolean): string {\n const safeToken = JSON.stringify(token);\n const safeBackend = JSON.stringify(backend);\n const safeState = JSON.stringify(state);\n const fp = JSON.stringify(tokenFingerprint(token));\n const recovery = withGateRecovery\n ? `\n function tryRecover() {\n stamp();\n var gate = document.querySelector('[data-surface-login]');\n if (!gate) return;\n if (sessionStorage.getItem('__yantra_wb_token_reload')) return;\n sessionStorage.setItem('__yantra_wb_token_reload', '1');\n var u = new URL(window.location.href);\n u.searchParams.set('token', t);\n u.searchParams.set('code', t);\n u.searchParams.set('state', state);\n window.location.replace(u.toString());\n }\n tryRecover();\n var i = 0;\n var timer = setInterval(function () {\n i += 1;\n tryRecover();\n if (i >= 40 || !document.querySelector('[data-surface-login]')) clearInterval(timer);\n }, 250);\n`\n : `\n var n = 0;\n var id = setInterval(function () {\n stamp();\n n += 1;\n if (n >= 80) clearInterval(id);\n }, 50);\n`;\n\n return `\n(function () {\n try {\n var t = ${safeToken};\n var backend = ${safeBackend};\n var state = ${safeState};\n var fp = ${fp};\n var PROBE_Q = 'query { getUserAccount { id } }';\n\n if (!window.__yantraWbFetchPatched) {\n window.__yantraWbFetchPatched = true;\n var origFetch = window.fetch.bind(window);\n window.fetch = function (input, init) {\n try {\n var url = typeof input === 'string' ? input : (input && input.url) ? String(input.url) : '';\n // Force surface-kit resolveCatalogBackend() to our GRAPHQL_URL (not remote agent → ideback).\n if (url.indexOf('/v1/auth/context') !== -1) {\n return Promise.resolve(new Response(\n JSON.stringify({ backendGraphqlUrl: backend }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n var body = init && typeof init.body === 'string' ? init.body : '';\n if (body && body.indexOf('getUserAccount') !== -1) {\n var parsed = JSON.parse(body);\n var q = parsed && typeof parsed.query === 'string' ? parsed.query.replace(/\\\\s+/g, ' ').trim() : '';\n if (q === PROBE_Q) {\n return Promise.resolve(new Response(\n JSON.stringify({ data: { getUserAccount: { id: 'mobile-webview-handoff' } } }),\n { status: 200, headers: { 'Content-Type': 'application/json' } }\n ));\n }\n }\n } catch (e0) {}\n return origFetch(input, init);\n };\n }\n\n function stamp() {\n try {\n sessionStorage.setItem('cdecli_oauth_state', state);\n localStorage.setItem('cdecli_token', t);\n sessionStorage.setItem('cdecli_token', t);\n localStorage.setItem('cdecli_system_token', t);\n localStorage.setItem('cdecli_token_meta', JSON.stringify({\n backend: backend,\n verifiedAt: Date.now(),\n fp: fp\n }));\n } catch (e1) {}\n }\n stamp();\n ${recovery}\n } catch (e) {}\n true;\n})();\ntrue;\n`;\n}\n\nexport function buildCdecliTokenInjection(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, false);\n}\n\nexport function buildSurfaceGateRecoveryScript(token: string, backend: string, state: string): string {\n return buildSurfaceHandoffScript(token, backend, state, true);\n}\n"],"names":["_a","token"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useState,useRef,useCallback,useEffect,useMemo}from'react';import {useCreateAccountChatFileUploadLinksMutation,useCreateAccountChatFileDownloadLinksMutation}from'common/graphql';import {markChannelHistoryRunning,useChatMutations,useChatMessages,clearChannelHistoryRunning,patchChannelHistoryPreview}from'./useChatApi.js';import {useCdecliChannel,isNoActiveSessionError}from'./useCdecliChannel.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {isAgentJwtRejected,shouldInlineGatewayMediaForHostedAgent}from'./systemTokenJwt.js';import {config}from'../config/env-config.js';import {isImageAttachment}from'../features/attachments/isImageAttachment.js';import {buildGatewayMedia,buildGatewayMediaPreferUrls}from'../features/attachments/buildGatewayMedia.js';import {preserveAttachmentPreviews}from'../features/attachments/preserveAttachmentPreviews.js';import {resolveAssistantReply}from'../features/chat/emptyChatResponse.js';import {appendOrReplaceAssistant,mergeOptimisticGatewayMessages,collapseConsecutiveAssistants}from'../state/chatThreadMerge.js';var __defProp = Object.defineProperty;
|
|
1
|
+
import {useState,useRef,useCallback,useEffect,useMemo,useSyncExternalStore}from'react';import {useCreateAccountChatFileUploadLinksMutation,useCreateAccountChatFileDownloadLinksMutation}from'common/graphql';import {markChannelHistoryRunning,useChatMutations,useChatMessages,clearChannelHistoryRunning,patchChannelHistoryPreview}from'./useChatApi.js';import {useCdecliChannel,isNoActiveSessionError}from'./useCdecliChannel.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {isAgentJwtRejected,shouldInlineGatewayMediaForHostedAgent}from'./systemTokenJwt.js';import {config}from'../config/env-config.js';import {queueGatewayMessage,MAX_QUEUED_MESSAGES,dropQueuedGatewayMessage,queuedGatewayMessages,subscribeQueuedGatewayMessages,restoreQueuedGatewayMessages,clearQueuedGatewayMessages}from'../services/queuedGatewayMessages.js';import {isImageAttachment}from'../features/attachments/isImageAttachment.js';import {buildGatewayMedia,buildGatewayMediaPreferUrls}from'../features/attachments/buildGatewayMedia.js';import {preserveAttachmentPreviews}from'../features/attachments/preserveAttachmentPreviews.js';import {resolveAssistantReply}from'../features/chat/emptyChatResponse.js';import {appendOrReplaceAssistant,mergeOptimisticGatewayMessages,collapseConsecutiveAssistants}from'../state/chatThreadMerge.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -22,6 +22,10 @@ function isCdecliAgentFailureOutput(text) {
|
|
|
22
22
|
if (!t) return false;
|
|
23
23
|
return /⚠\s*Error:/.test(t) || /LLM stream error/i.test(t) || /Incorrect API key/i.test(t) || /invalid_api_key/i.test(t) || /authentication_error/i.test(t) || isAgentJwtRejected(t);
|
|
24
24
|
}
|
|
25
|
+
function withoutTrailingUserMessage(prev, text) {
|
|
26
|
+
const last = prev[prev.length - 1];
|
|
27
|
+
return (last == null ? void 0 : last.role) === "user" && last.content === text ? prev.slice(0, -1) : prev;
|
|
28
|
+
}
|
|
25
29
|
function formatBackendThread(backendMessages) {
|
|
26
30
|
return collapseConsecutiveAssistants(backendMessages.map((msg) => {
|
|
27
31
|
var _a, _b;
|
|
@@ -63,8 +67,7 @@ function useChatStream(sessionId, routing = {
|
|
|
63
67
|
const sessionLiveRef = useRef(false);
|
|
64
68
|
const flushPendingSteersRef = useRef(async () => {
|
|
65
69
|
});
|
|
66
|
-
const sendMessageRef = useRef(async () =>
|
|
67
|
-
});
|
|
70
|
+
const sendMessageRef = useRef(async () => "failed");
|
|
68
71
|
const [cdecliStreamOverrideId, setCdecliStreamOverrideId] = useState(void 0);
|
|
69
72
|
const userMsgForSaveRef = useRef(null);
|
|
70
73
|
const saveSessionIdRef = useRef(null);
|
|
@@ -354,9 +357,9 @@ function useChatStream(sessionId, routing = {
|
|
|
354
357
|
}
|
|
355
358
|
}
|
|
356
359
|
}, [sessionId, backendMessages, backendMessagesLoaded, clearPendingStream]);
|
|
357
|
-
const sendMessage = useCallback(async (content, attachments, sessionIdOverride, alreadyVisible = false) => {
|
|
360
|
+
const sendMessage = useCallback(async (content, attachments, sessionIdOverride, alreadyVisible = false, queuedId = void 0) => {
|
|
358
361
|
var _a;
|
|
359
|
-
if (!content.trim() && !(attachments == null ? void 0 : attachments.length)) return;
|
|
362
|
+
if (!content.trim() && !(attachments == null ? void 0 : attachments.length)) return "failed";
|
|
360
363
|
const effectiveSessionId = sessionIdOverride !== void 0 ? sessionIdOverride : sessionId;
|
|
361
364
|
const rt = routingRef.current;
|
|
362
365
|
const trimmed = content.trim();
|
|
@@ -366,16 +369,37 @@ function useChatStream(sessionId, routing = {
|
|
|
366
369
|
content: sendText,
|
|
367
370
|
attachments
|
|
368
371
|
};
|
|
372
|
+
if (!rt.channelConnected) {
|
|
373
|
+
if (!effectiveSessionId) {
|
|
374
|
+
setError("Missing chat session id.");
|
|
375
|
+
return "failed";
|
|
376
|
+
}
|
|
377
|
+
if (queuedId) return "queued";
|
|
378
|
+
const result = queueGatewayMessage({
|
|
379
|
+
channelId: effectiveSessionId,
|
|
380
|
+
accountId: rt.accountId,
|
|
381
|
+
text: sendText,
|
|
382
|
+
attachments
|
|
383
|
+
});
|
|
384
|
+
if (!result.ok) {
|
|
385
|
+
setError(result.reason === "full" ? `${MAX_QUEUED_MESSAGES} messages are already waiting for the gateway. They send as soon as it connects.` : "Nothing to send.");
|
|
386
|
+
return "failed";
|
|
387
|
+
}
|
|
388
|
+
if (alreadyVisible) setMessages((prev) => withoutTrailingUserMessage(prev, sendText));
|
|
389
|
+
setError(null);
|
|
390
|
+
return "queued";
|
|
391
|
+
}
|
|
369
392
|
if (isSendingRef.current) {
|
|
370
|
-
if (!
|
|
371
|
-
setError(
|
|
372
|
-
return;
|
|
393
|
+
if (!effectiveSessionId) {
|
|
394
|
+
setError("Missing chat session id.");
|
|
395
|
+
return "failed";
|
|
373
396
|
}
|
|
374
|
-
setMessages((prev) => [...prev, userMessage]);
|
|
397
|
+
if (!alreadyVisible) setMessages((prev) => [...prev, userMessage]);
|
|
375
398
|
setError(null);
|
|
376
399
|
pendingSteersRef.current = [...pendingSteersRef.current, sendText];
|
|
400
|
+
if (queuedId) dropQueuedGatewayMessage(queuedId);
|
|
377
401
|
void flushPendingSteers();
|
|
378
|
-
return;
|
|
402
|
+
return "sent";
|
|
379
403
|
}
|
|
380
404
|
isSendingRef.current = true;
|
|
381
405
|
sessionLiveRef.current = false;
|
|
@@ -397,17 +421,6 @@ function useChatStream(sessionId, routing = {
|
|
|
397
421
|
userMsgForSaveRef.current = userMessage;
|
|
398
422
|
saveSessionIdRef.current = effectiveSessionId != null ? effectiveSessionId : null;
|
|
399
423
|
cdecliRoundHandledRef.current = false;
|
|
400
|
-
if (!rt.channelConnected) {
|
|
401
|
-
setError("CDeCLI is not connected yet. Check gateway status in the header.");
|
|
402
|
-
if (!alreadyVisible) setMessages((p) => p.slice(0, -1));
|
|
403
|
-
isSendingRef.current = false;
|
|
404
|
-
setIsLoading(false);
|
|
405
|
-
setIsStreaming(false);
|
|
406
|
-
userMsgForSaveRef.current = null;
|
|
407
|
-
saveSessionIdRef.current = null;
|
|
408
|
-
if (effectiveSessionId) clearChannelHistoryRunning(effectiveSessionId);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
424
|
if (!effectiveSessionId) {
|
|
412
425
|
setError("Missing chat session id.");
|
|
413
426
|
if (!alreadyVisible) setMessages((p) => p.slice(0, -1));
|
|
@@ -416,7 +429,7 @@ function useChatStream(sessionId, routing = {
|
|
|
416
429
|
setIsStreaming(false);
|
|
417
430
|
userMsgForSaveRef.current = null;
|
|
418
431
|
saveSessionIdRef.current = null;
|
|
419
|
-
return;
|
|
432
|
+
return "failed";
|
|
420
433
|
}
|
|
421
434
|
if (sessionIdOverride) {
|
|
422
435
|
setCdecliStreamOverrideId(sessionIdOverride);
|
|
@@ -453,17 +466,69 @@ function useChatStream(sessionId, routing = {
|
|
|
453
466
|
const ok = await sendCdecliMessage(sendText, effectiveSessionId, media);
|
|
454
467
|
if (!ok && !cdecliRoundHandledRef.current && !recoveringAuthRef.current) {
|
|
455
468
|
cdecliRoundHandledRef.current = true;
|
|
456
|
-
|
|
457
|
-
|
|
469
|
+
const parked = queuedId ? {
|
|
470
|
+
ok: true
|
|
471
|
+
} : queueGatewayMessage({
|
|
472
|
+
channelId: effectiveSessionId,
|
|
473
|
+
accountId: rt.accountId,
|
|
474
|
+
text: sendText,
|
|
475
|
+
attachments
|
|
476
|
+
});
|
|
477
|
+
setError(parked.ok ? "Gateway unreachable - message queued. It sends as soon as the connection is back." : "Failed to send message via CDeCLI. Check gateway connection.");
|
|
478
|
+
setMessages((p) => {
|
|
479
|
+
if (parked.ok) return withoutTrailingUserMessage(p, sendText);
|
|
480
|
+
return alreadyVisible ? p : p.slice(0, -1);
|
|
481
|
+
});
|
|
458
482
|
isSendingRef.current = false;
|
|
459
483
|
setIsLoading(false);
|
|
460
484
|
setIsStreaming(false);
|
|
461
485
|
userMsgForSaveRef.current = null;
|
|
462
486
|
saveSessionIdRef.current = null;
|
|
463
487
|
clearChannelHistoryRunning(effectiveSessionId);
|
|
488
|
+
return parked.ok ? "queued" : "failed";
|
|
464
489
|
}
|
|
490
|
+
if (ok && queuedId) dropQueuedGatewayMessage(queuedId);
|
|
491
|
+
return ok ? "sent" : "failed";
|
|
465
492
|
}, [sessionId, sendCdecliMessage, patchChannelTitle, flushPendingSteers, createAccountChatFileUploadLinksMutation, createAccountChatFileDownloadLinksMutation]);
|
|
466
493
|
sendMessageRef.current = sendMessage;
|
|
494
|
+
const queuedChannelId = sessionId || cdecliStreamOverrideId || null;
|
|
495
|
+
const queuedSnapshot = useCallback(() => queuedGatewayMessages(queuedChannelId, routing.accountId), [queuedChannelId, routing.accountId]);
|
|
496
|
+
const queuedMessages = useSyncExternalStore(subscribeQueuedGatewayMessages, queuedSnapshot, queuedSnapshot);
|
|
497
|
+
useEffect(() => {
|
|
498
|
+
void restoreQueuedGatewayMessages();
|
|
499
|
+
}, []);
|
|
500
|
+
const flushingRef = useRef(false);
|
|
501
|
+
const flushQueuedMessages = useCallback(async () => {
|
|
502
|
+
if (flushingRef.current) return;
|
|
503
|
+
flushingRef.current = true;
|
|
504
|
+
try {
|
|
505
|
+
for (let i = 0; i <= MAX_QUEUED_MESSAGES; i += 1) {
|
|
506
|
+
const rt = routingRef.current;
|
|
507
|
+
const sid = sessionIdRef.current;
|
|
508
|
+
if (!rt.channelConnected || !sid) return;
|
|
509
|
+
const next = queuedGatewayMessages(sid, rt.accountId)[0];
|
|
510
|
+
if (!next) return;
|
|
511
|
+
const outcome = await sendMessageRef.current(next.text, next.attachments, next.channelId, false, next.id);
|
|
512
|
+
if (outcome !== "sent") return;
|
|
513
|
+
}
|
|
514
|
+
} finally {
|
|
515
|
+
flushingRef.current = false;
|
|
516
|
+
}
|
|
517
|
+
}, []);
|
|
518
|
+
const wasConnectedRef = useRef(routing.channelConnected);
|
|
519
|
+
useEffect(() => {
|
|
520
|
+
const connected = routing.channelConnected;
|
|
521
|
+
const reconnected = connected && !wasConnectedRef.current;
|
|
522
|
+
wasConnectedRef.current = connected;
|
|
523
|
+
if (!connected || !queuedChannelId) return;
|
|
524
|
+
if (!reconnected && queuedGatewayMessages(queuedChannelId, routing.accountId).length === 0) return;
|
|
525
|
+
void flushQueuedMessages();
|
|
526
|
+
}, [routing.channelConnected, routing.accountId, queuedChannelId, flushQueuedMessages]);
|
|
527
|
+
const discardQueuedMessages = useCallback(() => {
|
|
528
|
+
if (!queuedChannelId) return;
|
|
529
|
+
clearQueuedGatewayMessages(queuedChannelId);
|
|
530
|
+
setError(null);
|
|
531
|
+
}, [queuedChannelId]);
|
|
467
532
|
tryRecoverAndResendRef.current = async (reason) => {
|
|
468
533
|
if (authRetryUsedRef.current || cdecliRoundHandledRef.current) return false;
|
|
469
534
|
if (!isAgentJwtRejected(reason)) return false;
|
|
@@ -522,6 +587,7 @@ function useChatStream(sessionId, routing = {
|
|
|
522
587
|
void cancelCdecliTurn(sid != null ? sid : "messenger");
|
|
523
588
|
}
|
|
524
589
|
ignoreHydrationRef.current = true;
|
|
590
|
+
if (sid) clearQueuedGatewayMessages(sid);
|
|
525
591
|
setMessages([]);
|
|
526
592
|
setResponse("");
|
|
527
593
|
setQuickReply("");
|
|
@@ -539,7 +605,12 @@ function useChatStream(sessionId, routing = {
|
|
|
539
605
|
isLoading,
|
|
540
606
|
isStreaming,
|
|
541
607
|
messagesLoading,
|
|
542
|
-
hasMessages: messages.length > 0 || !!response || !!quickReply,
|
|
608
|
+
hasMessages: messages.length > 0 || !!response || !!quickReply || queuedMessages.length > 0,
|
|
609
|
+
/** Turns waiting on the gateway, oldest first. Rendered as queued bubbles. */
|
|
610
|
+
queuedMessages,
|
|
611
|
+
/** Send the queue now (the notice's retry), independent of the reconnect edge. */
|
|
612
|
+
flushQueuedMessages,
|
|
613
|
+
discardQueuedMessages,
|
|
543
614
|
sendMessage,
|
|
544
615
|
cancel,
|
|
545
616
|
clearMessages
|