@adminide-stack/yantra-mobile 12.0.52-alpha.0 → 12.0.52-alpha.1
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/components/KeyboardComposerDock.js +4 -3
- package/lib/components/KeyboardComposerDock.js.map +1 -1
- package/lib/features/attachments/buildGatewayMedia.js +22 -15
- package/lib/features/attachments/buildGatewayMedia.js.map +1 -1
- package/lib/features/attachments/uploadChatAttachments.js +6 -4
- package/lib/features/attachments/uploadChatAttachments.js.map +1 -1
- package/lib/features/attachments/useImageAttachments.js +63 -17
- package/lib/features/attachments/useImageAttachments.js.map +1 -1
- package/lib/features/chat/AskUserCard.js +244 -121
- package/lib/features/chat/AskUserCard.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +206 -130
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/chat/askUser.js +111 -15
- package/lib/features/chat/askUser.js.map +1 -1
- package/lib/features/chat/streamingMarkdown.js +7 -0
- package/lib/features/chat/streamingMarkdown.js.map +1 -0
- package/lib/features/chat/transcriptGroups.js +29 -0
- package/lib/features/chat/transcriptGroups.js.map +1 -0
- package/lib/hooks/mintSurfaceSystemToken.js +28 -14
- package/lib/hooks/mintSurfaceSystemToken.js.map +1 -1
- package/lib/hooks/systemTokenJwt.js +75 -0
- package/lib/hooks/systemTokenJwt.js.map +1 -0
- package/lib/hooks/useCdecliAutoConnect.js +116 -138
- package/lib/hooks/useCdecliAutoConnect.js.map +1 -1
- package/lib/hooks/useChatApi.js +43 -17
- package/lib/hooks/useChatApi.js.map +1 -1
- package/lib/hooks/useChatStream.js +61 -13
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/hooks/useObtainSystemToken.js +121 -36
- package/lib/hooks/useObtainSystemToken.js.map +1 -1
- package/lib/screens/Chat/index.js +25 -6
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/HomeScreen.js +21 -6
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/Home/components/ChatHistoryLanding.js +46 -16
- package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,27 +1,123 @@
|
|
|
1
1
|
const MAX_ASK_USER_QUESTIONS = 4;
|
|
2
2
|
const ASK_USER_RE = /\[ASK_USER\]([\s\S]*?)\[\/ASK_USER\]/;
|
|
3
|
+
const ASK_USER_OPEN_RE = /\[ASK_USER\]/;
|
|
4
|
+
function unwrapJsonCandidate(inner) {
|
|
5
|
+
let s = inner.trim();
|
|
6
|
+
const fence = /^```(?:json)?\s*([\s\S]*?)\s*```$/i.exec(s);
|
|
7
|
+
if (fence) s = fence[1].trim();
|
|
8
|
+
return s;
|
|
9
|
+
}
|
|
10
|
+
function parseJsonObject(s) {
|
|
11
|
+
const start = s.indexOf("{");
|
|
12
|
+
if (start < 0) return null;
|
|
13
|
+
let depth = 0;
|
|
14
|
+
let inStr = false;
|
|
15
|
+
let esc = false;
|
|
16
|
+
for (let i = start; i < s.length; i++) {
|
|
17
|
+
const c = s[i];
|
|
18
|
+
if (inStr) {
|
|
19
|
+
if (esc) {
|
|
20
|
+
esc = false;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (c === "\\") {
|
|
24
|
+
esc = true;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (c === '"') inStr = false;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (c === '"') {
|
|
31
|
+
inStr = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (c === "{") depth += 1;
|
|
35
|
+
else if (c === "}") {
|
|
36
|
+
depth -= 1;
|
|
37
|
+
if (depth === 0) {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(s.slice(start, i + 1));
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function normalizeQuestion(raw) {
|
|
49
|
+
if (!raw || typeof raw !== "object") return null;
|
|
50
|
+
const q = raw;
|
|
51
|
+
const id = typeof q.id === "string" ? q.id : "";
|
|
52
|
+
const title = typeof q.title === "string" ? q.title : typeof q.question === "string" ? q.question : "";
|
|
53
|
+
if (!id || !title.trim()) return null;
|
|
54
|
+
const options = Array.isArray(q.options) ? q.options.filter((o) => !!o && typeof o === "object" && typeof o.id === "string" && typeof o.label === "string") : void 0;
|
|
55
|
+
return {
|
|
56
|
+
id,
|
|
57
|
+
title: title.trim(),
|
|
58
|
+
header: typeof q.header === "string" ? q.header : void 0,
|
|
59
|
+
options,
|
|
60
|
+
allowText: q.allowText !== false,
|
|
61
|
+
textPlaceholder: typeof q.textPlaceholder === "string" ? q.textPlaceholder : void 0,
|
|
62
|
+
multiSelect: q.multiSelect === true
|
|
63
|
+
};
|
|
64
|
+
}
|
|
3
65
|
function parseAskUser(raw) {
|
|
66
|
+
var _a;
|
|
4
67
|
if (!(raw == null ? void 0 : raw.trim())) return null;
|
|
5
|
-
const
|
|
6
|
-
if (!
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
68
|
+
const tagged = (_a = ASK_USER_RE.exec(raw)) != null ? _a : ASK_USER_OPEN_RE.test(raw) ? /\[ASK_USER\]([\s\S]*)$/.exec(raw) : null;
|
|
69
|
+
if (!tagged) return null;
|
|
70
|
+
const parsed = parseJsonObject(unwrapJsonCandidate(tagged[1]));
|
|
71
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
72
|
+
const body = parsed;
|
|
73
|
+
if (typeof body.requestId !== "string" || !Array.isArray(body.questions)) return null;
|
|
74
|
+
const questions = body.questions.map(normalizeQuestion).filter((q) => q !== null).slice(0, MAX_ASK_USER_QUESTIONS);
|
|
75
|
+
return questions.length ? {
|
|
76
|
+
requestId: body.requestId,
|
|
77
|
+
questions
|
|
78
|
+
} : null;
|
|
18
79
|
}
|
|
19
80
|
function stripAskUser(raw) {
|
|
20
81
|
if (!raw) return "";
|
|
21
|
-
|
|
82
|
+
let stripped = raw.replace(new RegExp(ASK_USER_RE.source, "g"), "");
|
|
83
|
+
stripped = stripped.replace(/\[ASK_USER\][\s\S]*$/, "");
|
|
22
84
|
if (stripped === raw) return raw;
|
|
23
85
|
return stripped.replace(/\n{3,}/g, "\n\n").trim();
|
|
24
86
|
}
|
|
87
|
+
function splitAskUserParts(raw) {
|
|
88
|
+
if (!raw) return {
|
|
89
|
+
before: "",
|
|
90
|
+
after: "",
|
|
91
|
+
hasMarker: false
|
|
92
|
+
};
|
|
93
|
+
const tagged = ASK_USER_RE.exec(raw);
|
|
94
|
+
if (tagged && tagged.index !== void 0) {
|
|
95
|
+
return {
|
|
96
|
+
before: raw.slice(0, tagged.index).trim(),
|
|
97
|
+
after: raw.slice(tagged.index + tagged[0].length).trim(),
|
|
98
|
+
hasMarker: true
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const open = raw.search(ASK_USER_OPEN_RE);
|
|
102
|
+
if (open >= 0) {
|
|
103
|
+
return {
|
|
104
|
+
before: raw.slice(0, open).trim(),
|
|
105
|
+
after: "",
|
|
106
|
+
hasMarker: true
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
before: "",
|
|
111
|
+
after: "",
|
|
112
|
+
hasMarker: false
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function displayAskUserAssistantText(raw, answered) {
|
|
116
|
+
const parts = splitAskUserParts(raw);
|
|
117
|
+
if (!parts.hasMarker) return stripAskUser(raw);
|
|
118
|
+
const keep = answered ? parts.after : [parts.before, parts.after].filter(Boolean).join("\n\n");
|
|
119
|
+
return stripAskUser(keep || (answered ? "" : parts.before));
|
|
120
|
+
}
|
|
25
121
|
function pendingAskUser(messages) {
|
|
26
122
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
27
123
|
const msg = messages[i];
|
|
@@ -37,4 +133,4 @@ function formatAskUserAnswers(questions, answers) {
|
|
|
37
133
|
const answer = (_a = answers[q.id]) == null ? void 0 : _a.trim();
|
|
38
134
|
return answer ? `**${q.title.replace(/:\s*$/, "")}:** ${answer}` : null;
|
|
39
135
|
}).filter(Boolean).join("\n\n");
|
|
40
|
-
}export{MAX_ASK_USER_QUESTIONS,formatAskUserAnswers,parseAskUser,pendingAskUser,stripAskUser};//# sourceMappingURL=askUser.js.map
|
|
136
|
+
}export{MAX_ASK_USER_QUESTIONS,displayAskUserAssistantText,formatAskUserAnswers,parseAskUser,pendingAskUser,splitAskUserParts,stripAskUser};//# sourceMappingURL=askUser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"askUser.js","sources":["../../../src/features/chat/askUser.ts"],"sourcesContent":["/**\n * The `[ASK_USER]` clarifying-question protocol, mobile side (Clockbook #1643).\n *\n * cdecli emits the block itself — from Claude's AskUserQuestion and its own\n * ask_user_question tool — and PARKS the run until the answer arrives:\n *\n * [ASK_USER]{\"requestId\":\"ask-…\",\"questions\":[{\"id\":\"q1\",\"title\":\"Which\n * database?\",\"header\":\"Database\",\"options\":[{\"id\":\"o1\",\"label\":\"Postgres\",\n * \"detail\":\"…\"}],\"allowText\":true}]}[/ASK_USER]\n *\n * The reply is deliberately NOT a marker: answers are formatted into one visible\n * user message (`**Title:** answer` lines) and sent through the normal chat path,\n * so the producer needs no reply-protocol support. Only the question side is\n * structured.\n *\n * Deliberately a copy of the web's `responsePromptProtocol` rather than an\n * import — mobile is a separate bundle and shares no code with the web shell.\n * The reply side is plain text, so the two stay compatible without a shared\n * version.\n */\n\nexport interface AskUserOption {\n id: string;\n label: string;\n /** Secondary line under the label. */\n detail?: string;\n /** \"Something else\": focuses the free-text row instead of answering. */\n other?: boolean;\n}\n\nexport interface AskUserQuestion {\n id: string;\n /** Also the bold prefix of the formatted answer. */\n title: string;\n /** Short category eyebrow above the title, e.g. \"Database\". */\n header?: string;\n options?: AskUserOption[];\n /** Show the free-text row (default true). */\n allowText?: boolean;\n textPlaceholder?: string;\n /** Pick several options; the answer is sent as comma-joined labels. */\n multiSelect?: boolean;\n}\n\nexport interface AskUserPayload {\n requestId: string;\n questions: AskUserQuestion[];\n}\n\n/** Anything longer is a form, not a prompt — the web caps it the same way. */\nexport const MAX_ASK_USER_QUESTIONS = 4;\n\n// Not anchored: the channel layer may prepend a stream envelope or cost banner.\nconst ASK_USER_RE = /\\[ASK_USER\\]([\\s\\S]*?)\\[\\/ASK_USER\\]/;\n\n/** Parse an `[ASK_USER]…[/ASK_USER]` message, or null if it isn't one. */\nexport function parseAskUser(raw: string | null | undefined): AskUserPayload | null {\n if (!raw?.trim()) return null;\n const m = ASK_USER_RE.exec(raw);\n if (!m) return null;\n try {\n const parsed = JSON.parse(m[1]) as AskUserPayload;\n if (!parsed?.requestId || !Array.isArray(parsed.questions)) return null;\n const questions = parsed.questions\n .filter((q) => q && typeof q.id === 'string' && typeof q.title === 'string' && q.title.trim())\n .slice(0, MAX_ASK_USER_QUESTIONS);\n return questions.length ? { requestId: parsed.requestId, questions } : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Remove every `[ASK_USER]` block, leaving the rest of the message intact.\n *\n * cdecli emits the block MID-turn and keeps streaming its reply into the SAME\n * message once the answer releases the parked run, so the block and the reply it\n * was asked for share one message. Hiding the whole message — which is what a\n * naive implementation does — would swallow that reply.\n */\nexport function stripAskUser(raw: string | null | undefined): string {\n if (!raw) return '';\n const stripped = raw.replace(new RegExp(ASK_USER_RE.source, 'g'), '');\n if (stripped === raw) return raw;\n return stripped.replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n\n/**\n * The question still awaiting an answer: the last assistant block, unless the\n * user has already replied after it. Scanning backwards means a stale card from\n * earlier in the thread never reappears.\n */\nexport function pendingAskUser(messages: ReadonlyArray<{ role: string; content: string }>): AskUserPayload | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'user') return null;\n const parsed = msg.role === 'assistant' ? parseAskUser(msg.content) : null;\n if (parsed) return parsed;\n }\n return null;\n}\n\n/**\n * Fold the answers into the single visible reply message:\n *\n * **Which database?:** Postgres\n *\n * Skipped questions are omitted; an empty result means \"nothing to send\", which\n * the card treats as a dismiss.\n */\nexport function formatAskUserAnswers(questions: readonly AskUserQuestion[], answers: Record<string, string>): string {\n return questions\n .map((q) => {\n const answer = answers[q.id]?.trim();\n return answer ? `**${q.title.replace(/:\\s*$/, '')}:** ${answer}` : null;\n })\n .filter(Boolean)\n .join('\\n\\n');\n}\n"],"names":[],"mappings":"AAgDO,MAAM,sBAAyB,GAAA;AAGtC,MAAM,WAAc,GAAA,sCAAA;AAGb,SAAS,aAAa,GAAuD,EAAA;AAClF,EAAI,IAAA,EAAC,GAAK,IAAA,IAAA,GAAA,MAAA,GAAA,GAAA,CAAA,IAAA,EAAA,CAAA,EAAe,OAAA,IAAA;AACzB,EAAM,MAAA,CAAA,GAAI,WAAY,CAAA,IAAA,CAAK,GAAG,CAAA;AAC9B,EAAI,IAAA,CAAC,GAAU,OAAA,IAAA;AACf,EAAI,IAAA;AACF,IAAA,MAAM,MAAS,GAAA,IAAA,CAAK,KAAM,CAAA,CAAA,CAAE,CAAC,CAAC,CAAA;AAC9B,IAAI,IAAA,EAAC,iCAAQ,SAAa,CAAA,IAAA,CAAC,MAAM,OAAQ,CAAA,MAAA,CAAO,SAAS,CAAA,EAAU,OAAA,IAAA;AACnE,IAAM,MAAA,SAAA,GAAY,OAAO,SAAU,CAAA,MAAA,CAAO,OAAK,CAAK,IAAA,OAAO,EAAE,EAAO,KAAA,QAAA,IAAY,OAAO,CAAE,CAAA,KAAA,KAAU,YAAY,CAAE,CAAA,KAAA,CAAM,MAAM,CAAA,CAAE,KAAM,CAAA,CAAA,EAAG,sBAAsB,CAAA;AAC9J,IAAA,OAAO,UAAU,MAAS,GAAA;AAAA,MACxB,WAAW,MAAO,CAAA,SAAA;AAAA,MAClB;AAAA,KACE,GAAA,IAAA;AAAA,GACE,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX;AAUO,SAAS,aAAa,GAAwC,EAAA;AACnE,EAAI,IAAA,CAAC,KAAY,OAAA,EAAA;AACjB,EAAM,MAAA,QAAA,GAAW,IAAI,OAAQ,CAAA,IAAI,OAAO,WAAY,CAAA,MAAA,EAAQ,GAAG,CAAA,EAAG,EAAE,CAAA;AACpE,EAAI,IAAA,QAAA,KAAa,KAAY,OAAA,GAAA;AAC7B,EAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,SAAW,EAAA,MAAM,EAAE,IAAK,EAAA;AAClD;AAOO,SAAS,eAAe,QAGJ,EAAA;AACzB,EAAA,KAAA,IAAS,IAAI,QAAS,CAAA,MAAA,GAAS,CAAG,EAAA,CAAA,IAAK,GAAG,CAAK,EAAA,EAAA;AAC7C,IAAM,MAAA,GAAA,GAAM,SAAS,CAAC,CAAA;AACtB,IAAI,IAAA,GAAA,CAAI,IAAS,KAAA,MAAA,EAAe,OAAA,IAAA;AAChC,IAAA,MAAM,SAAS,GAAI,CAAA,IAAA,KAAS,cAAc,YAAa,CAAA,GAAA,CAAI,OAAO,CAAI,GAAA,IAAA;AACtE,IAAA,IAAI,QAAe,OAAA,MAAA;AAAA;AAErB,EAAO,OAAA,IAAA;AACT;AAUgB,SAAA,oBAAA,CAAqB,WAAuC,OAAyC,EAAA;AACnH,EAAO,OAAA,SAAA,CAAU,IAAI,CAAK,CAAA,KAAA;AAjH5B,IAAA,IAAA,EAAA;AAkHI,IAAA,MAAM,MAAS,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,CAAE,CAAA,EAAE,MAAZ,IAAe,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC9B,IAAO,OAAA,MAAA,GAAS,CAAK,EAAA,EAAA,CAAA,CAAE,KAAM,CAAA,OAAA,CAAQ,SAAS,EAAE,CAAC,CAAO,IAAA,EAAA,MAAM,CAAK,CAAA,GAAA,IAAA;AAAA,GACpE,CAAE,CAAA,MAAA,CAAO,OAAO,CAAA,CAAE,KAAK,MAAM,CAAA;AAChC"}
|
|
1
|
+
{"version":3,"file":"askUser.js","sources":["../../../src/features/chat/askUser.ts"],"sourcesContent":["/**\n * The `[ASK_USER]` clarifying-question protocol, mobile side (Clockbook #1643).\n *\n * cdecli emits the block itself — from Claude's AskUserQuestion and its own\n * ask_user_question tool — and PARKS the run until the answer arrives:\n *\n * [ASK_USER]{\"requestId\":\"ask-…\",\"questions\":[{\"id\":\"q1\",\"title\":\"Which\n * database?\",\"header\":\"Database\",\"options\":[{\"id\":\"o1\",\"label\":\"Postgres\",\n * \"detail\":\"…\"}],\"allowText\":true}]}[/ASK_USER]\n *\n * The reply is deliberately NOT a marker: answers are formatted into one visible\n * user message (`**Title:** answer` lines) and sent through the normal chat path,\n * so the producer needs no reply-protocol support. Only the question side is\n * structured.\n *\n * Deliberately a copy of the web's `responsePromptProtocol` rather than an\n * import — mobile is a separate bundle and shares no code with the web shell.\n * The reply side is plain text, so the two stay compatible without a shared\n * version.\n */\n\nexport interface AskUserOption {\n id: string;\n label: string;\n /** Secondary line under the label. */\n detail?: string;\n /** \"Something else\": focuses the free-text row instead of answering. */\n other?: boolean;\n}\n\nexport interface AskUserQuestion {\n id: string;\n /** Also the bold prefix of the formatted answer. */\n title: string;\n /** Short category eyebrow above the title, e.g. \"Database\". */\n header?: string;\n options?: AskUserOption[];\n /** Show the free-text row (default true). */\n allowText?: boolean;\n textPlaceholder?: string;\n /** Pick several options; the answer is sent as comma-joined labels. */\n multiSelect?: boolean;\n}\n\nexport interface AskUserPayload {\n requestId: string;\n questions: AskUserQuestion[];\n}\n\n/** Anything longer is a form, not a prompt — the web caps it the same way. */\nexport const MAX_ASK_USER_QUESTIONS = 4;\n\n// Not anchored: the channel layer may prepend a stream envelope or cost banner.\nconst ASK_USER_RE = /\\[ASK_USER\\]([\\s\\S]*?)\\[\\/ASK_USER\\]/;\nconst ASK_USER_OPEN_RE = /\\[ASK_USER\\]/;\n\nfunction unwrapJsonCandidate(inner: string): string {\n let s = inner.trim();\n const fence = /^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i.exec(s);\n if (fence) s = fence[1].trim();\n return s;\n}\n\n/** First complete `{...}` object in `s`, or null while the JSON is still streaming. */\nfunction parseJsonObject(s: string): unknown | null {\n const start = s.indexOf('{');\n if (start < 0) return null;\n let depth = 0;\n let inStr = false;\n let esc = false;\n for (let i = start; i < s.length; i++) {\n const c = s[i];\n if (inStr) {\n if (esc) {\n esc = false;\n continue;\n }\n if (c === '\\\\') {\n esc = true;\n continue;\n }\n if (c === '\"') inStr = false;\n continue;\n }\n if (c === '\"') {\n inStr = true;\n continue;\n }\n if (c === '{') depth += 1;\n else if (c === '}') {\n depth -= 1;\n if (depth === 0) {\n try {\n return JSON.parse(s.slice(start, i + 1));\n } catch {\n return null;\n }\n }\n }\n }\n return null;\n}\n\nfunction normalizeQuestion(raw: unknown): AskUserQuestion | null {\n if (!raw || typeof raw !== 'object') return null;\n const q = raw as Record<string, unknown>;\n const id = typeof q.id === 'string' ? q.id : '';\n const title = typeof q.title === 'string' ? q.title : typeof q.question === 'string' ? q.question : '';\n if (!id || !title.trim()) return null;\n const options = Array.isArray(q.options)\n ? q.options.filter(\n (o): o is AskUserOption =>\n !!o &&\n typeof o === 'object' &&\n typeof (o as AskUserOption).id === 'string' &&\n typeof (o as AskUserOption).label === 'string',\n )\n : undefined;\n return {\n id,\n title: title.trim(),\n header: typeof q.header === 'string' ? q.header : undefined,\n options,\n allowText: q.allowText !== false,\n textPlaceholder: typeof q.textPlaceholder === 'string' ? q.textPlaceholder : undefined,\n multiSelect: q.multiSelect === true,\n };\n}\n\n/** Parse an `[ASK_USER]…[/ASK_USER]` message, or null if it isn't one. */\nexport function parseAskUser(raw: string | null | undefined): AskUserPayload | null {\n if (!raw?.trim()) return null;\n const tagged = ASK_USER_RE.exec(raw) ?? (ASK_USER_OPEN_RE.test(raw) ? /\\[ASK_USER\\]([\\s\\S]*)$/.exec(raw) : null);\n if (!tagged) return null;\n const parsed = parseJsonObject(unwrapJsonCandidate(tagged[1]));\n if (!parsed || typeof parsed !== 'object') return null;\n const body = parsed as { requestId?: unknown; questions?: unknown };\n if (typeof body.requestId !== 'string' || !Array.isArray(body.questions)) return null;\n const questions = body.questions\n .map(normalizeQuestion)\n .filter((q): q is AskUserQuestion => q !== null)\n .slice(0, MAX_ASK_USER_QUESTIONS);\n return questions.length ? { requestId: body.requestId, questions } : null;\n}\n\n/**\n * Remove every `[ASK_USER]` block, leaving the rest of the message intact.\n *\n * cdecli emits the block MID-turn and keeps streaming its reply into the SAME\n * message once the answer releases the parked run, so the block and the reply it\n * was asked for share one message. Hiding the whole message — which is what a\n * naive implementation does — would swallow that reply.\n *\n * An unclosed `[ASK_USER]` (still streaming) is also stripped so the raw JSON\n * never paints in the bubble the way it does on web after `stripAskUser`.\n */\nexport function stripAskUser(raw: string | null | undefined): string {\n if (!raw) return '';\n let stripped = raw.replace(new RegExp(ASK_USER_RE.source, 'g'), '');\n stripped = stripped.replace(/\\[ASK_USER\\][\\s\\S]*$/, '');\n if (stripped === raw) return raw;\n return stripped.replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n\n/**\n * Text around an `[ASK_USER]` marker. cdecli often speaks an intro *before* the\n * card (\"I'll need a couple more details…\") and streams the real reply *after*\n * the user answers. Once there is a user reply, the intro is stale.\n */\nexport function splitAskUserParts(raw: string | null | undefined): {\n before: string;\n after: string;\n hasMarker: boolean;\n} {\n if (!raw) return { before: '', after: '', hasMarker: false };\n const tagged = ASK_USER_RE.exec(raw);\n if (tagged && tagged.index !== undefined) {\n return {\n before: raw.slice(0, tagged.index).trim(),\n after: raw.slice(tagged.index + tagged[0].length).trim(),\n hasMarker: true,\n };\n }\n const open = raw.search(ASK_USER_OPEN_RE);\n if (open >= 0) {\n return { before: raw.slice(0, open).trim(), after: '', hasMarker: true };\n }\n return { before: '', after: '', hasMarker: false };\n}\n\n/**\n * Assistant prose to paint. After the user answers the card, drop the intro that\n * sat above `[ASK_USER]` so we don't re-ask for details already in the thread.\n */\nexport function displayAskUserAssistantText(raw: string | null | undefined, answered: boolean): string {\n const parts = splitAskUserParts(raw);\n if (!parts.hasMarker) return stripAskUser(raw);\n const keep = answered ? parts.after : [parts.before, parts.after].filter(Boolean).join('\\n\\n');\n return stripAskUser(keep || (answered ? '' : parts.before));\n}\n\n/**\n * The question still awaiting an answer: the last assistant block, unless the\n * user has already replied after it. Scanning backwards means a stale card from\n * earlier in the thread never reappears.\n */\nexport function pendingAskUser(messages: ReadonlyArray<{ role: string; content: string }>): AskUserPayload | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'user') return null;\n const parsed = msg.role === 'assistant' ? parseAskUser(msg.content) : null;\n if (parsed) return parsed;\n }\n return null;\n}\n\n/**\n * Fold the answers into the single visible reply message:\n *\n * **Which database?:** Postgres\n *\n * Skipped questions are omitted; an empty result means \"nothing to send\", which\n * the card treats as a dismiss.\n */\nexport function formatAskUserAnswers(questions: readonly AskUserQuestion[], answers: Record<string, string>): string {\n return questions\n .map((q) => {\n const answer = answers[q.id]?.trim();\n return answer ? `**${q.title.replace(/:\\s*$/, '')}:** ${answer}` : null;\n })\n .filter(Boolean)\n .join('\\n\\n');\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
function closeOpenMarkdownFences(text) {
|
|
2
|
+
if (!text) return text;
|
|
3
|
+
const opens = text.match(/^ {0,3}```/gm);
|
|
4
|
+
if (!opens || opens.length % 2 === 0) return text;
|
|
5
|
+
return `${text}
|
|
6
|
+
\`\`\``;
|
|
7
|
+
}export{closeOpenMarkdownFences};//# sourceMappingURL=streamingMarkdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamingMarkdown.js","sources":["../../../src/features/chat/streamingMarkdown.ts"],"sourcesContent":["/**\n * Streaming markdown is incomplete: the model often has an open ``` fence\n * when the next token has not arrived. Closing it for the preview makes\n * react-native-markdown-display paint a code block instead of raw fences.\n */\nexport function closeOpenMarkdownFences(text: string): string {\n if (!text) return text;\n const opens = text.match(/^ {0,3}```/gm);\n if (!opens || opens.length % 2 === 0) return text;\n return `${text}\\n\\`\\`\\``;\n}\n"],"names":[],"mappings":"AAKO,SAAS,wBAAwB,IAAsB,EAAA;AAC5D,EAAI,IAAA,CAAC,MAAa,OAAA,IAAA;AAClB,EAAM,MAAA,KAAA,GAAQ,IAAK,CAAA,KAAA,CAAM,cAAc,CAAA;AACvC,EAAA,IAAI,CAAC,KAAS,IAAA,KAAA,CAAM,MAAS,GAAA,CAAA,KAAM,GAAU,OAAA,IAAA;AAC7C,EAAA,OAAO,GAAG,IAAI;AAAA,MAAA,CAAA;AAChB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
function collapseDuplicateUserRows(rows) {
|
|
2
|
+
const out = [];
|
|
3
|
+
for (const row of rows) {
|
|
4
|
+
const last = out[out.length - 1];
|
|
5
|
+
const trimmed = row.content.trim();
|
|
6
|
+
if (row.role === "user" && (last == null ? void 0 : last.role) === "user" && last.content.trim() === trimmed && trimmed) {
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
out.push(row);
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
function groupConsecutiveRoleRows(rows) {
|
|
14
|
+
const groups = [];
|
|
15
|
+
for (const row of rows) {
|
|
16
|
+
const last = groups[groups.length - 1];
|
|
17
|
+
if (last && last.role === row.role) {
|
|
18
|
+
last.items.push(row);
|
|
19
|
+
last.id = `${last.items[0].id}..${row.id}`;
|
|
20
|
+
} else {
|
|
21
|
+
groups.push({
|
|
22
|
+
id: row.id,
|
|
23
|
+
role: row.role,
|
|
24
|
+
items: [row]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return groups;
|
|
29
|
+
}export{collapseDuplicateUserRows,groupConsecutiveRoleRows};//# sourceMappingURL=transcriptGroups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcriptGroups.js","sources":["../../../src/features/chat/transcriptGroups.ts"],"sourcesContent":["/**\n * Consecutive same-role turns share one visual cluster, matching web\n * (stacked bubbles, not one concatenated blob). ASK_USER-only assistant rows\n * are stripped first, so a card answer sits under the original prompt as its\n * own bubble in the same right-aligned stack.\n */\n\nexport type RoleRow = {\n id: string;\n role: string;\n content: string;\n};\n\nexport type RoleGroup<T extends RoleRow> = {\n id: string;\n role: string;\n items: T[];\n};\n\n/** Drop an immediately-repeated user bubble (web planMessages). */\nexport function collapseDuplicateUserRows<T extends RoleRow>(rows: readonly T[]): T[] {\n const out: T[] = [];\n for (const row of rows) {\n const last = out[out.length - 1];\n const trimmed = row.content.trim();\n if (row.role === 'user' && last?.role === 'user' && last.content.trim() === trimmed && trimmed) {\n continue;\n }\n out.push(row);\n }\n return out;\n}\n\nexport function groupConsecutiveRoleRows<T extends RoleRow>(rows: readonly T[]): Array<RoleGroup<T>> {\n const groups: Array<RoleGroup<T>> = [];\n for (const row of rows) {\n const last = groups[groups.length - 1];\n if (last && last.role === row.role) {\n last.items.push(row);\n last.id = `${last.items[0].id}..${row.id}`;\n } else {\n groups.push({ id: row.id, role: row.role, items: [row] });\n }\n }\n return groups;\n}\n\n/** Visible user-group copy, one block — follow-ups append to the previous send. */\nexport function joinGroupedTexts(texts: readonly string[]): string {\n return texts\n .map((t) => t.trim())\n .filter(Boolean)\n .join('\\n\\n');\n}\n"],"names":[],"mappings":"AAmBO,SAAS,0BAA6C,IAAyB,EAAA;AACpF,EAAA,MAAM,MAAW,EAAC;AAClB,EAAA,KAAA,MAAW,OAAO,IAAM,EAAA;AACtB,IAAA,MAAM,IAAO,GAAA,GAAA,CAAI,GAAI,CAAA,MAAA,GAAS,CAAC,CAAA;AAC/B,IAAM,MAAA,OAAA,GAAU,GAAI,CAAA,OAAA,CAAQ,IAAK,EAAA;AACjC,IAAI,IAAA,GAAA,CAAI,IAAS,KAAA,MAAA,IAAA,CAAU,IAAM,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAA,IAAA,MAAS,MAAU,IAAA,IAAA,CAAK,OAAQ,CAAA,IAAA,EAAW,KAAA,OAAA,IAAW,OAAS,EAAA;AAC9F,MAAA;AAAA;AAEF,IAAA,GAAA,CAAI,KAAK,GAAG,CAAA;AAAA;AAEd,EAAO,OAAA,GAAA;AACT;AACO,SAAS,yBAA4C,IAAyC,EAAA;AACnG,EAAA,MAAM,SAA8B,EAAC;AACrC,EAAA,KAAA,MAAW,OAAO,IAAM,EAAA;AACtB,IAAA,MAAM,IAAO,GAAA,MAAA,CAAO,MAAO,CAAA,MAAA,GAAS,CAAC,CAAA;AACrC,IAAA,IAAI,IAAQ,IAAA,IAAA,CAAK,IAAS,KAAA,GAAA,CAAI,IAAM,EAAA;AAClC,MAAK,IAAA,CAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACnB,MAAK,IAAA,CAAA,EAAA,GAAK,GAAG,IAAK,CAAA,KAAA,CAAM,CAAC,CAAE,CAAA,EAAE,CAAK,EAAA,EAAA,GAAA,CAAI,EAAE,CAAA,CAAA;AAAA,KACnC,MAAA;AACL,MAAA,MAAA,CAAO,IAAK,CAAA;AAAA,QACV,IAAI,GAAI,CAAA,EAAA;AAAA,QACR,MAAM,GAAI,CAAA,IAAA;AAAA,QACV,KAAA,EAAO,CAAC,GAAG;AAAA,OACZ,CAAA;AAAA;AACH;AAEF,EAAO,OAAA,MAAA;AACT"}
|
|
@@ -80,6 +80,11 @@ async function gqlFetch(graphqlUrl, idToken, query, variables) {
|
|
|
80
80
|
}
|
|
81
81
|
return await res.json();
|
|
82
82
|
}
|
|
83
|
+
const REVOKE_SECRET_API_TOKEN = `
|
|
84
|
+
mutation RevokeSecretApiToken($id: ID!) {
|
|
85
|
+
revokeSecretApiToken(id: $id)
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
83
88
|
const DEV_TESTING_PROJECT_UUID = "9a8ac704-1040-4836-9b72-48b771ca91b1";
|
|
84
89
|
const OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;
|
|
85
90
|
const 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}$/;
|
|
@@ -140,7 +145,7 @@ function assertMintableProjectId(projectId) {
|
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
async function mintSurfaceSystemToken(input) {
|
|
143
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
148
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B;
|
|
144
149
|
const idToken = await readAuth0IdToken();
|
|
145
150
|
if (!idToken) {
|
|
146
151
|
throw new Error("No Auth0 id_token in storage \u2014 sign in to the mobile app first.");
|
|
@@ -175,6 +180,15 @@ async function mintSurfaceSystemToken(input) {
|
|
|
175
180
|
throw new Error("Missing environmentTagId \u2014 cannot mint system token.");
|
|
176
181
|
}
|
|
177
182
|
assertMintableProjectId(projectId);
|
|
183
|
+
if (input.forceRemint) {
|
|
184
|
+
const existing = await gqlFetch(graphqlUrl, idToken, GET_USER_SYSTEM_TOKEN);
|
|
185
|
+
const existingId = (_f = (_e = (_d = existing.data) == null ? void 0 : _d.getUserSystemToken) == null ? void 0 : _e.id) != null ? _f : null;
|
|
186
|
+
if (existingId) {
|
|
187
|
+
await gqlFetch(graphqlUrl, idToken, REVOKE_SECRET_API_TOKEN, {
|
|
188
|
+
id: existingId
|
|
189
|
+
}).catch(() => void 0);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
178
192
|
try {
|
|
179
193
|
const obtained = await gqlFetch(graphqlUrl, idToken, OBTAIN_USER_SYSTEM_TOKEN, {
|
|
180
194
|
input: {
|
|
@@ -185,8 +199,8 @@ async function mintSurfaceSystemToken(input) {
|
|
|
185
199
|
algorithm: "HS256"
|
|
186
200
|
}
|
|
187
201
|
});
|
|
188
|
-
const token2 = (
|
|
189
|
-
if (token2 && !((
|
|
202
|
+
const token2 = (_i = (_h = (_g = obtained.data) == null ? void 0 : _g.obtainUserSystemToken) == null ? void 0 : _h.token) == null ? void 0 : _i.trim();
|
|
203
|
+
if (token2 && !((_j = obtained.errors) == null ? void 0 : _j.length)) {
|
|
190
204
|
if (__DEV__) {
|
|
191
205
|
console.log("[WebBuilder] obtained system token length=", token2.length);
|
|
192
206
|
}
|
|
@@ -195,8 +209,8 @@ async function mintSurfaceSystemToken(input) {
|
|
|
195
209
|
backend: graphqlUrl
|
|
196
210
|
};
|
|
197
211
|
}
|
|
198
|
-
if ((
|
|
199
|
-
throw new Error(((
|
|
212
|
+
if ((_k = obtained.errors) == null ? void 0 : _k.length) {
|
|
213
|
+
throw new Error(((_l = obtained.errors[0]) == null ? void 0 : _l.message) || "obtainUserSystemToken failed");
|
|
200
214
|
}
|
|
201
215
|
} catch (err) {
|
|
202
216
|
if (__DEV__) {
|
|
@@ -213,10 +227,10 @@ async function mintSurfaceSystemToken(input) {
|
|
|
213
227
|
}
|
|
214
228
|
}
|
|
215
229
|
const getResult = await gqlFetch(graphqlUrl, idToken, GET_USER_SYSTEM_TOKEN);
|
|
216
|
-
if ((
|
|
217
|
-
throw new Error(((
|
|
230
|
+
if ((_m = getResult.errors) == null ? void 0 : _m.length) {
|
|
231
|
+
throw new Error(((_n = getResult.errors[0]) == null ? void 0 : _n.message) || "GetUserSystemToken failed");
|
|
218
232
|
}
|
|
219
|
-
let tokenId = (
|
|
233
|
+
let tokenId = (_q = (_p = (_o = getResult.data) == null ? void 0 : _o.getUserSystemToken) == null ? void 0 : _p.id) != null ? _q : null;
|
|
220
234
|
if (!tokenId) {
|
|
221
235
|
const createResult = await gqlFetch(graphqlUrl, idToken, CREATE_SECRET_API_TOKEN, {
|
|
222
236
|
input: {
|
|
@@ -229,18 +243,18 @@ async function mintSurfaceSystemToken(input) {
|
|
|
229
243
|
algorithm: "HS256"
|
|
230
244
|
}
|
|
231
245
|
});
|
|
232
|
-
const createErr = ((
|
|
246
|
+
const createErr = ((_s = (_r = createResult.errors) == null ? void 0 : _r[0]) == null ? void 0 : _s.message) || "";
|
|
233
247
|
if (createErr && !/already have a system token/i.test(createErr)) {
|
|
234
248
|
if (/12 bytes|24 hex/i.test(createErr)) {
|
|
235
249
|
throw new Error(`Project "${projectId}" is not valid on this backend. Pick/create a real project in the app, then retry.`);
|
|
236
250
|
}
|
|
237
251
|
throw new Error(createErr);
|
|
238
252
|
}
|
|
239
|
-
if ((
|
|
253
|
+
if ((_u = (_t = createResult.data) == null ? void 0 : _t.createSecretApiToken) == null ? void 0 : _u.id) {
|
|
240
254
|
tokenId = createResult.data.createSecretApiToken.id;
|
|
241
255
|
} else {
|
|
242
256
|
const refetch = await gqlFetch(graphqlUrl, idToken, GET_USER_SYSTEM_TOKEN);
|
|
243
|
-
tokenId = (
|
|
257
|
+
tokenId = (_x = (_w = (_v = refetch.data) == null ? void 0 : _v.getUserSystemToken) == null ? void 0 : _w.id) != null ? _x : null;
|
|
244
258
|
}
|
|
245
259
|
}
|
|
246
260
|
if (!tokenId) {
|
|
@@ -249,10 +263,10 @@ async function mintSurfaceSystemToken(input) {
|
|
|
249
263
|
const reveal = await gqlFetch(graphqlUrl, idToken, REVEAL_SECRET_API_TOKEN, {
|
|
250
264
|
id: tokenId
|
|
251
265
|
});
|
|
252
|
-
if ((
|
|
253
|
-
throw new Error(((
|
|
266
|
+
if ((_y = reveal.errors) == null ? void 0 : _y.length) {
|
|
267
|
+
throw new Error(((_z = reveal.errors[0]) == null ? void 0 : _z.message) || "RevealSecretApiToken failed");
|
|
254
268
|
}
|
|
255
|
-
const token = (
|
|
269
|
+
const token = (_B = (_A = reveal.data) == null ? void 0 : _A.revealSecretApiToken) == null ? void 0 : _B.trim();
|
|
256
270
|
if (!token) {
|
|
257
271
|
throw new Error("RevealSecretApiToken returned empty token");
|
|
258
272
|
}
|
|
@@ -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\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};\n\nexport type MintSurfaceTokenResult = {\n token: string;\n /** Exact backend URL string to stamp into cdecli_token_meta. */\n backend: string;\n};\n\n/**\n * Reveal (or obtain) a system token on GRAPHQL_URL.\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\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 // 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 algorithm: 'HS256',\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 algorithm: 'HS256',\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 HS256 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/**\n * Reveal (or obtain) a system token on GRAPHQL_URL.\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\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 algorithm: 'HS256',\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 algorithm: 'HS256',\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":"uPAGA,MAAM,wBAA2B,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAOjC,MAAM,qBAAwB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAS9B,MAAM,sBAAyB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAO/B,MAAM,YAAe,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AASrB,MAAM,oBAAuB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAO7B,MAAM,uBAA0B,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAQhC,MAAM,uBAA0B,GAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAWhC,SAAS,oBAAoB,GAAqB,EAAA;AAChD,EAAA,OAAO,GAAI,CAAA,IAAA,EAAO,CAAA,OAAA,CAAQ,OAAO,EAAE,CAAA;AACrC;AAGO,SAAS,6BAA6B,UAA6B,EAAA;AACxE,EAAM,MAAA,GAAA,GAAA,CAAM,UAAY,IAAA,IAAA,GAAA,MAAA,GAAA,UAAA,CAAA,IAAA,EAAA,KAAU,MAAO,CAAA,WAAA;AACzC,EAAI,IAAA,EAAC,2BAAK,IAAQ,EAAA,CAAA,EAAA;AAChB,IAAM,MAAA,IAAI,MAAM,+BAA+B,CAAA;AAAA;AAEjD,EAAA,OAAO,oBAAoB,GAAG,CAAA;AAChC;AAGA,eAAe,QAAY,CAAA,UAAA,EAAoB,OAAiB,EAAA,KAAA,EAAe,SAA4D,EAAA;AAIzI,EAAM,MAAA,GAAA,GAAM,MAAM,KAAA,CAAM,UAAY,EAAA;AAAA,IAClC,MAAQ,EAAA,MAAA;AAAA,IACR,OAAS,EAAA;AAAA,MACP,CAAC,WAAY,CAAA,YAAY,GAAG,kBAAA;AAAA,MAC5B,CAAC,WAAA,CAAY,aAAa,GAAG,UAAU,OAAO,CAAA,CAAA;AAAA,MAC9C,CAAC,WAAY,CAAA,SAAS,GAAG,MAAA;AAAA,MACzB,CAAC,WAAA,CAAY,aAAa,GAAG,YAAa,CAAA;AAAA,KAC5C;AAAA,IACA,IAAA,EAAM,KAAK,SAAU,CAAA;AAAA,MACnB,KAAA;AAAA,MACA;AAAA,KACD;AAAA,GACF,CAAA;AACD,EAAI,IAAA,CAAC,IAAI,EAAI,EAAA;AACX,IAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAO,CAAA,KAAA,CAAM,MAAM,EAAE,CAAA;AAC5C,IAAM,MAAA,IAAI,KAAM,CAAA,CAAA,aAAA,EAAgB,GAAI,CAAA,MAAM,CAAK,EAAA,EAAA,IAAA,CAAK,KAAM,CAAA,CAAA,EAAG,GAAG,CAAC,CAAE,CAAA,CAAA;AAAA;AAErE,EAAQ,OAAA,MAAM,IAAI,IAAK,EAAA;AACzB;AACA,MAAM,uBAA0B,GAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AA8BhC,MAAM,wBAA2B,GAAA,sCAAA;AACjC,MAAM,aAAgB,GAAA,mBAAA;AACtB,MAAM,YAAe,GAAA,+EAAA;AACrB,eAAe,iBAAA,CAAkB,YAAoB,OAIlD,EAAA;AAvIH,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAwIE,EAAA,MAAM,IAAO,GAAA,MAAM,QAIhB,CAAA,UAAA,EAAY,SAAS,sBAAsB,CAAA;AAC9C,EAAI,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAL,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAa,MAAQ,EAAA;AACvB,IAAM,MAAA,IAAI,QAAM,EAAK,GAAA,IAAA,CAAA,MAAA,CAAO,CAAC,CAAb,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAgB,YAAW,6BAA6B,CAAA;AAAA;AAE1E,EAAA,MAAM,YAAU,EAAK,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAA,IAAA,KAAL,mBAAW,oBAAX,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAiC,KAAK,CAAE,CAAA,KAAA;AAhJ1D,IAAAA,IAAAA,GAAAA;AAgJ6D,IAAA,OAAA,CAAAA,GAAA,GAAA,CAAA,IAAA,IAAA,GAAA,MAAA,GAAA,CAAA,CAAG,IAAH,KAAA,IAAA,GAAA,MAAA,GAAAA,GAAS,CAAA,IAAA,EAAA;AAAA,GAApD,CAAA,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAA6D,IAA7D,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAmE,IAAU,EAAA,KAAA,EAAA;AAC7F,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAM,MAAA,IAAI,MAAM,gIAAgI,CAAA;AAAA;AAElJ,EAAA,MAAM,QAAW,GAAA,MAAM,QAMpB,CAAA,UAAA,EAAY,SAAS,YAAc,EAAA;AAAA,IACpC;AAAA,GACD,CAAA;AACD,EAAI,IAAA,CAAA,EAAA,GAAA,QAAA,CAAS,MAAT,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAiB,MAAQ,EAAA;AAC3B,IAAM,MAAA,IAAI,QAAM,EAAS,GAAA,QAAA,CAAA,MAAA,CAAO,CAAC,CAAjB,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAoB,YAAW,oBAAoB,CAAA;AAAA;AAErE,EAAM,MAAA,SAAA,GAAA,CAAA,CAAY,sCAAS,IAAT,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,gBAAf,IAA4B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,KAA5B,IAAkC,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAK,CAAE,CAAA,KAAA;AAhK7D,IAAAA,IAAAA,GAAAA;AAgKgE,IAAA,OAAA,CAAAA,GAAA,GAAA,CAAA,IAAA,IAAA,GAAA,MAAA,GAAA,CAAA,CAAG,EAAH,KAAA,IAAA,GAAA,MAAA,GAAAA,GAAO,CAAA,IAAA,EAAA;AAAA,GAAnD,CAAA,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAA4D,EAA5D,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAgE,IAAU,EAAA,KAAA,EAAA;AAC5F,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAA,MAAM,IAAI,KAAA,CAAM,CAA6C,0CAAA,EAAA,OAAO,CAAI,EAAA,CAAA,CAAA;AAAA;AAE1E,EAAA,MAAM,IAAO,GAAA,MAAM,QAIhB,CAAA,UAAA,EAAY,SAAS,oBAAsB,EAAA;AAAA,IAC5C;AAAA,GACD,CAAA;AACD,EAAI,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAL,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAa,MAAQ,EAAA;AACvB,IAAM,MAAA,IAAI,QAAM,EAAK,GAAA,IAAA,CAAA,MAAA,CAAO,CAAC,CAAb,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAgB,YAAW,2BAA2B,CAAA;AAAA;AAExE,EAAA,MAAM,qBAAmB,EAAK,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAA,IAAA,KAAL,mBAAW,kBAAX,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAA+B,KAAK,CAAE,CAAA,KAAA;AA9KjE,IAAAA,IAAAA,GAAAA;AA8KoE,IAAA,OAAA,CAAAA,GAAA,GAAA,CAAA,IAAA,IAAA,GAAA,MAAA,GAAA,CAAA,CAAG,EAAH,KAAA,IAAA,GAAA,MAAA,GAAAA,GAAO,CAAA,IAAA,EAAA;AAAA,GAAhD,CAAA,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAyD,EAAzD,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAA6D,IAAU,EAAA,KAAA,EAAA;AAChG,EAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAyD,sDAAA,EAAA,SAAS,CAAI,EAAA,CAAA,CAAA;AAAA;AAExF,EAAO,OAAA;AAAA,IACL,OAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AACA,SAAS,wBAAwB,SAAyB,EAAA;AACxD,EAAA,IAAI,SAAc,KAAA,SAAA,IAAa,CAAC,SAAA,CAAU,MAAQ,EAAA;AAChD,IAAM,MAAA,IAAI,MAAM,sEAAiE,CAAA;AAAA;AAEnF,EAAI,IAAA,CAAC,OAAW,IAAA,SAAA,KAAc,wBAA0B,EAAA;AACtD,IAAM,MAAA,IAAI,MAAM,+FAA+F,CAAA;AAAA;AAEjH,EAAI,IAAA,CAAC,cAAc,IAAK,CAAA,SAAS,KAAK,CAAC,YAAA,CAAa,IAAK,CAAA,SAAS,CAAG,EAAA;AACnE,IAAM,MAAA,IAAI,MAAM,CAAmD,gDAAA,EAAA,SAAA,CAAU,MAAM,CAAG,EAAA,EAAE,CAAC,CAAE,CAAA,CAAA;AAAA;AAE/F;AACA,eAAsB,uBAAuB,KAA+D,EAAA;AAnM5G,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAoME,EAAM,MAAA,OAAA,GAAU,MAAM,gBAAiB,EAAA;AACvC,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAM,MAAA,IAAI,MAAM,sEAAiE,CAAA;AAAA;AAEnF,EAAM,MAAA,UAAA,GAAa,4BAA6B,CAAA,KAAA,CAAM,UAAU,CAAA;AAChE,EAAA,IAAI,OAAS,EAAA;AAEX,IAAA,OAAA,CAAQ,IAAI,uCAAyC,EAAA,UAAA,EAAY,KAAM,CAAA,aAAA,GAAgB,qBAAqB,EAAE,CAAA;AAAA;AAEhH,EAAA,IAAI,OAAU,GAAA,CAAA,CAAA,EAAA,GAAA,KAAA,CAAM,OAAN,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,IAAU,EAAA,KAAA,EAAA;AACvC,EAAA,IAAI,SAAY,GAAA,CAAA,CAAA,EAAA,GAAA,KAAA,CAAM,SAAN,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAiB,IAAU,EAAA,KAAA,EAAA;AAC3C,EAAA,IAAI,gBAAmB,GAAA,CAAA,CAAA,EAAA,GAAA,KAAA,CAAM,KAAN,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAa,IAAU,EAAA,KAAA,EAAA;AAC9C,EAAA,IAAI,MAAM,aAAe,EAAA;AACvB,IAAA,MAAM,KAAQ,GAAA,MAAM,iBAAkB,CAAA,UAAA,EAAY,OAAO,CAAA;AACzD,IAAA,OAAA,GAAU,KAAM,CAAA,OAAA;AAChB,IAAA,SAAA,GAAY,KAAM,CAAA,SAAA;AAClB,IAAA,gBAAA,GAAmB,KAAM,CAAA,gBAAA;AACzB,IAAA,IAAI,OAAS,EAAA;AAEX,MAAA,OAAA,CAAQ,IAAI,sCAAwC,EAAA;AAAA,QAClD,OAAA;AAAA,QACA,SAAA;AAAA,QACA;AAAA,OACD,CAAA;AAAA;AACH;AAEF,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAM,MAAA,IAAI,MAAM,4DAAuD,CAAA;AAAA;AAEzE,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAM,MAAA,IAAI,MAAM,sEAAiE,CAAA;AAAA;AAEnF,EAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,IAAM,MAAA,IAAI,MAAM,2DAAsD,CAAA;AAAA;AAExE,EAAA,uBAAA,CAAwB,SAAS,CAAA;AACjC,EAAA,IAAI,MAAM,WAAa,EAAA;AACrB,IAAA,MAAM,QAAW,GAAA,MAAM,QAIpB,CAAA,UAAA,EAAY,SAAS,qBAAqB,CAAA;AAC7C,IAAA,MAAM,cAAa,EAAS,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,QAAA,CAAA,IAAA,KAAT,mBAAe,kBAAf,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAmC,OAAnC,IAAyC,GAAA,EAAA,GAAA,IAAA;AAC5D,IAAA,IAAI,UAAY,EAAA;AACd,MAAM,MAAA,QAAA,CAAS,UAAY,EAAA,OAAA,EAAS,uBAAyB,EAAA;AAAA,QAC3D,EAAI,EAAA;AAAA,OACL,CAAA,CAAE,KAAM,CAAA,MAAM,MAAS,CAAA;AAAA;AAC1B;AAIF,EAAI,IAAA;AACF,IAAA,MAAM,QAAW,GAAA,MAAM,QAIpB,CAAA,UAAA,EAAY,SAAS,wBAA0B,EAAA;AAAA,MAChD,KAAO,EAAA;AAAA,QACL,SAAA;AAAA,QACA,gBAAA;AAAA,QACA,OAAA;AAAA,QACA,kBAAA,EAAoB,CAAI,GAAA,EAAA,GAAK,EAAK,GAAA,EAAA;AAAA,QAClC,SAAW,EAAA;AAAA;AACb,KACD,CAAA;AACD,IAAA,MAAMC,UAAQ,EAAS,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,QAAA,CAAA,IAAA,KAAT,mBAAe,qBAAf,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAsC,UAAtC,IAA6C,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC3D,IAAA,IAAIA,MAAS,IAAA,EAAA,CAAC,EAAS,GAAA,QAAA,CAAA,MAAA,KAAT,mBAAiB,MAAQ,CAAA,EAAA;AACrC,MAAA,IAAI,OAAS,EAAA;AAEX,QAAQ,OAAA,CAAA,GAAA,CAAI,4CAA8CA,EAAAA,MAAAA,CAAM,MAAM,CAAA;AAAA;AAExE,MAAO,OAAA;AAAA,QACL,KAAAA,EAAAA,MAAAA;AAAA,QACA,OAAS,EAAA;AAAA,OACX;AAAA;AAEF,IAAI,IAAA,CAAA,EAAA,GAAA,QAAA,CAAS,MAAT,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAiB,MAAQ,EAAA;AAC3B,MAAM,MAAA,IAAI,QAAM,EAAS,GAAA,QAAA,CAAA,MAAA,CAAO,CAAC,CAAjB,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAoB,YAAW,8BAA8B,CAAA;AAAA;AAC/E,WACO,GAAK,EAAA;AACZ,IAAA,IAAI,OAAS,EAAA;AAEX,MAAQ,OAAA,CAAA,IAAA,CAAK,sEAAsE,GAAG,CAAA;AAAA;AAGxF,IAAA,MAAM,MAAM,GAAe,YAAA,KAAA,GAAQ,GAAI,CAAA,OAAA,GAAU,OAAO,GAAG,CAAA;AAC3D,IAAI,IAAA,oCAAA,CAAqC,IAAK,CAAA,GAAG,CAAG,EAAA;AAClD,MAAA,MAAM,IAAI,KAAA,CAAM,CAAY,SAAA,EAAA,SAAS,CAAoF,kFAAA,CAAA,CAAA;AAAA;AAG3H,IAAA,IAAI,CAAC,yDAAA,CAA0D,IAAK,CAAA,GAAG,CAAG,EAAA;AAExE,MAAI,IAAA,qDAAA,CAAsD,IAAK,CAAA,GAAG,CAAG,EAAA;AACnE,QAAA,MAAM,GAAe,YAAA,KAAA,GAAQ,GAAM,GAAA,IAAI,MAAM,GAAG,CAAA;AAAA;AAClD;AACF;AAEF,EAAA,MAAM,SAAY,GAAA,MAAM,QAIrB,CAAA,UAAA,EAAY,SAAS,qBAAqB,CAAA;AAC7C,EAAI,IAAA,CAAA,EAAA,GAAA,SAAA,CAAU,MAAV,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAkB,MAAQ,EAAA;AAC5B,IAAM,MAAA,IAAI,QAAM,EAAU,GAAA,SAAA,CAAA,MAAA,CAAO,CAAC,CAAlB,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAqB,YAAW,2BAA2B,CAAA;AAAA;AAE7E,EAAA,IAAI,WAAU,EAAU,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,SAAA,CAAA,IAAA,KAAV,mBAAgB,kBAAhB,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAoC,OAApC,IAA0C,GAAA,EAAA,GAAA,IAAA;AACxD,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAA,MAAM,YAAe,GAAA,MAAM,QAIxB,CAAA,UAAA,EAAY,SAAS,uBAAyB,EAAA;AAAA,MAC/C,KAAO,EAAA;AAAA,QACL,IAAM,EAAA,cAAA;AAAA,QACN,eAAiB,EAAA,OAAA;AAAA,QACjB,aAAe,EAAA,IAAA;AAAA,QACf,gBAAA;AAAA,QACA,OAAA;AAAA,QACA,SAAA;AAAA,QACA,SAAW,EAAA;AAAA;AACb,KACD,CAAA;AACD,IAAA,MAAM,cAAY,EAAa,GAAA,CAAA,EAAA,GAAA,YAAA,CAAA,MAAA,KAAb,IAAsB,GAAA,MAAA,GAAA,EAAA,CAAA,CAAA,CAAA,KAAtB,mBAA0B,OAAW,KAAA,EAAA;AACvD,IAAA,IAAI,SAAa,IAAA,CAAC,8BAA+B,CAAA,IAAA,CAAK,SAAS,CAAG,EAAA;AAChE,MAAI,IAAA,kBAAA,CAAmB,IAAK,CAAA,SAAS,CAAG,EAAA;AACtC,QAAA,MAAM,IAAI,KAAA,CAAM,CAAY,SAAA,EAAA,SAAS,CAAoF,kFAAA,CAAA,CAAA;AAAA;AAE3H,MAAM,MAAA,IAAI,MAAM,SAAS,CAAA;AAAA;AAE3B,IAAA,IAAA,CAAI,EAAa,GAAA,CAAA,EAAA,GAAA,YAAA,CAAA,IAAA,KAAb,IAAmB,GAAA,MAAA,GAAA,EAAA,CAAA,oBAAA,KAAnB,mBAAyC,EAAI,EAAA;AAC/C,MAAU,OAAA,GAAA,YAAA,CAAa,KAAK,oBAAqB,CAAA,EAAA;AAAA,KAC5C,MAAA;AACL,MAAA,MAAM,OAAU,GAAA,MAAM,QAInB,CAAA,UAAA,EAAY,SAAS,qBAAqB,CAAA;AAC7C,MAAA,OAAA,GAAA,CAAU,yBAAQ,IAAR,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAc,kBAAd,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAkC,OAAlC,IAAwC,GAAA,EAAA,GAAA,IAAA;AAAA;AACpD;AAEF,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAM,MAAA,IAAI,MAAM,6DAAwD,CAAA;AAAA;AAE1E,EAAA,MAAM,MAAS,GAAA,MAAM,QAElB,CAAA,UAAA,EAAY,SAAS,uBAAyB,EAAA;AAAA,IAC/C,EAAI,EAAA;AAAA,GACL,CAAA;AACD,EAAI,IAAA,CAAA,EAAA,GAAA,MAAA,CAAO,MAAP,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,MAAQ,EAAA;AACzB,IAAM,MAAA,IAAI,QAAM,EAAO,GAAA,MAAA,CAAA,MAAA,CAAO,CAAC,CAAf,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAkB,YAAW,6BAA6B,CAAA;AAAA;AAE5E,EAAA,MAAM,KAAQ,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,MAAA,CAAO,IAAP,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAa,yBAAb,IAAmC,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AACjD,EAAA,IAAI,CAAC,KAAO,EAAA;AACV,IAAM,MAAA,IAAI,MAAM,2CAA2C,CAAA;AAAA;AAE7D,EAAA,IAAI,OAAS,EAAA;AAEX,IAAQ,OAAA,CAAA,GAAA,CAAI,0CAA4C,EAAA,KAAA,CAAM,MAAM,CAAA;AAAA;AAEtE,EAAO,OAAA;AAAA,IACL,KAAA;AAAA,IACA,OAAS,EAAA;AAAA,GACX;AACF;AACgB,SAAA,uBAAA,CAAwB,OAAiB,EAAA,KAAA,EAAe,KAAuB,EAAA;AAC7F,EAAI,IAAA;AACF,IAAM,MAAA,GAAA,GAAM,IAAI,GAAA,CAAI,OAAO,CAAA;AAC3B,IAAI,GAAA,CAAA,YAAA,CAAa,GAAI,CAAA,OAAA,EAAS,KAAK,CAAA;AACnC,IAAI,GAAA,CAAA,YAAA,CAAa,GAAI,CAAA,MAAA,EAAQ,KAAK,CAAA;AAClC,IAAI,GAAA,CAAA,YAAA,CAAa,GAAI,CAAA,OAAA,EAAS,KAAK,CAAA;AACnC,IAAA,OAAO,IAAI,QAAS,EAAA;AAAA,GACd,CAAA,OAAA,CAAA,EAAA;AACN,IAAA,MAAM,GAAM,GAAA,OAAA,CAAQ,QAAS,CAAA,GAAG,IAAI,GAAM,GAAA,GAAA;AAC1C,IAAA,OAAO,CAAG,EAAA,OAAO,CAAG,EAAA,GAAG,SAAS,kBAAmB,CAAA,KAAK,CAAC,CAAA,MAAA,EAAS,mBAAmB,KAAK,CAAC,CAAU,OAAA,EAAA,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAAA;AAElI;AACA,SAAS,iBAAiB,KAAuB,EAAA;AApXjD,EAAA,IAAA,EAAA;AAqXE,EAAA,MAAM,OAAM,EAAM,GAAA,KAAA,CAAA,KAAA,CAAM,GAAG,CAAE,CAAA,CAAC,MAAlB,IAAuB,GAAA,EAAA,GAAA,KAAA;AACnC,EAAO,OAAA,GAAA,CAAI,MAAM,GAAG,CAAA;AACtB;AAGA,SAAS,yBAA0B,CAAA,KAAA,EAAe,OAAiB,EAAA,KAAA,EAAe,gBAAmC,EAAA;AACnH,EAAM,MAAA,SAAA,GAAY,IAAK,CAAA,SAAA,CAAU,KAAK,CAAA;AACtC,EAAM,MAAA,WAAA,GAAc,IAAK,CAAA,SAAA,CAAU,OAAO,CAAA;AAC1C,EAAM,MAAA,SAAA,GAAY,IAAK,CAAA,SAAA,CAAU,KAAK,CAAA;AACtC,EAAA,MAAM,EAAK,GAAA,IAAA,CAAK,SAAU,CAAA,gBAAA,CAAiB,KAAK,CAAC,CAAA;AACjD,EAAA,MAAM,WAAW,gBAAmB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAoBlC,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAQF,EAAO,OAAA;AAAA;AAAA;AAAA,YAAA,EAGK,SAAS,CAAA;AAAA,kBAAA,EACH,WAAW,CAAA;AAAA,gBAAA,EACb,SAAS,CAAA;AAAA,aAAA,EACZ,EAAE,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,EA8CX,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAMd;AACgB,SAAA,yBAAA,CAA0B,KAAe,EAAA,OAAA,EAAiB,KAAuB,EAAA;AAC/F,EAAA,OAAO,yBAA0B,CAAA,KAAA,EAAO,OAAS,EAAA,KAAA,EAAO,KAAK,CAAA;AAC/D;AACgB,SAAA,8BAAA,CAA+B,KAAe,EAAA,OAAA,EAAiB,KAAuB,EAAA;AACpG,EAAA,OAAO,yBAA0B,CAAA,KAAA,EAAO,OAAS,EAAA,KAAA,EAAO,IAAI,CAAA;AAC9D"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
function normalizeGraphqlUrl(url) {
|
|
2
|
+
return url.trim().replace(/\/$/, "");
|
|
3
|
+
}
|
|
4
|
+
function hostnameOf(url) {
|
|
5
|
+
try {
|
|
6
|
+
return new URL(url).hostname.toLowerCase();
|
|
7
|
+
} catch (e) {
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function isLoopbackOrPrivateHost(hostname) {
|
|
12
|
+
const host = hostname.toLowerCase();
|
|
13
|
+
if (!host) return false;
|
|
14
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1") return true;
|
|
15
|
+
if (host.endsWith(".local")) return true;
|
|
16
|
+
if (/^10\./.test(host)) return true;
|
|
17
|
+
if (/^192\.168\./.test(host)) return true;
|
|
18
|
+
if (/^172\.(1[6-9]|2\d|3[0-1])\./.test(host)) return true;
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function isLoopbackOrPrivateUrl(url) {
|
|
22
|
+
return isLoopbackOrPrivateHost(hostnameOf(url));
|
|
23
|
+
}
|
|
24
|
+
function resolveCdecliAgentTokenGraphqlUrl(appGraphqlUrl, agentEndpoint, clusterGraphqlUrl) {
|
|
25
|
+
const app = normalizeGraphqlUrl(appGraphqlUrl);
|
|
26
|
+
const cluster = normalizeGraphqlUrl(clusterGraphqlUrl);
|
|
27
|
+
if (!isLoopbackOrPrivateUrl(app)) return app;
|
|
28
|
+
if (isLoopbackOrPrivateUrl(agentEndpoint)) return app;
|
|
29
|
+
return cluster || app;
|
|
30
|
+
}
|
|
31
|
+
function shouldInlineGatewayMediaForHostedAgent(appGraphqlUrl, agentEndpoint) {
|
|
32
|
+
return isLoopbackOrPrivateUrl(appGraphqlUrl) && !isLoopbackOrPrivateUrl(agentEndpoint);
|
|
33
|
+
}
|
|
34
|
+
function decodeJwtJson(part) {
|
|
35
|
+
if (!part) return null;
|
|
36
|
+
try {
|
|
37
|
+
const b64 = part.replace(/-/g, "+").replace(/_/g, "/");
|
|
38
|
+
const pad = b64 + "=".repeat((4 - b64.length % 4) % 4);
|
|
39
|
+
const json = typeof atob === "function" ? atob(pad) : Buffer.from(pad, "base64").toString("utf8");
|
|
40
|
+
return JSON.parse(json);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function getJwtExpirySec(token) {
|
|
46
|
+
const payload = decodeJwtJson(token.split(".")[1]);
|
|
47
|
+
return typeof (payload == null ? void 0 : payload.exp) === "number" ? payload.exp : null;
|
|
48
|
+
}
|
|
49
|
+
function isJwtExpired(token, skewSec = 60) {
|
|
50
|
+
const exp = getJwtExpirySec(token);
|
|
51
|
+
if (exp == null) return false;
|
|
52
|
+
return exp < Math.floor(Date.now() / 1e3) + skewSec;
|
|
53
|
+
}
|
|
54
|
+
function isSystemTokenRowStale(expiresAt) {
|
|
55
|
+
if (!expiresAt) return false;
|
|
56
|
+
const expiryMs = new Date(expiresAt).getTime();
|
|
57
|
+
if (!Number.isFinite(expiryMs)) return false;
|
|
58
|
+
return expiryMs - Date.now() < 36e5;
|
|
59
|
+
}
|
|
60
|
+
function isAgentJwtRejected(text) {
|
|
61
|
+
const t = text.trim();
|
|
62
|
+
if (!t) return false;
|
|
63
|
+
return /invalid HS256 signature/i.test(t) || /invalid RS256 signature/i.test(t) || /invalid token/i.test(t) || /Session creation failed:\s*HTTP 401/i.test(t) || /Failed to create session[\s\S]*HTTP 401/i.test(t);
|
|
64
|
+
}
|
|
65
|
+
function isMissingRs256KeyError(err) {
|
|
66
|
+
const message = err instanceof Error ? err.message : String(err != null ? err : "");
|
|
67
|
+
return /private key|SECRET_API_TOKEN_PRIVATE_KEY|RS256/i.test(message);
|
|
68
|
+
}
|
|
69
|
+
function isAlreadyHasSystemTokenError(err) {
|
|
70
|
+
const message = err instanceof Error ? err.message : String(err != null ? err : "");
|
|
71
|
+
return /already have a system token/i.test(message);
|
|
72
|
+
}
|
|
73
|
+
function isMissingVaultSecretError(message) {
|
|
74
|
+
return /secret not found|not found in vault/i.test(message);
|
|
75
|
+
}export{getJwtExpirySec,isAgentJwtRejected,isAlreadyHasSystemTokenError,isJwtExpired,isLoopbackOrPrivateHost,isLoopbackOrPrivateUrl,isMissingRs256KeyError,isMissingVaultSecretError,isSystemTokenRowStale,resolveCdecliAgentTokenGraphqlUrl,shouldInlineGatewayMediaForHostedAgent};//# sourceMappingURL=systemTokenJwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemTokenJwt.js","sources":["../../src/hooks/systemTokenJwt.ts"],"sourcesContent":["/**\n * JWT helpers for the org-scoped system token (CDeCLI / surfaces).\n *\n * The gateway pill can show connected while cdecli-agent still 401s session\n * create: connect talks to Yantra GraphQL; the agent verifies the JWT itself.\n * A locally minted HS256 token will not verify on hosted cdecli-agent.\n */\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\nfunction hostnameOf(url: string): string {\n try {\n return new URL(url).hostname.toLowerCase();\n } catch {\n return '';\n }\n}\n\n/** Loopback, RFC1918, or Bonjour `.local` — a laptop/LAN GraphQL, not a cluster. */\nexport function isLoopbackOrPrivateHost(hostname: string): boolean {\n const host = hostname.toLowerCase();\n if (!host) return false;\n if (host === 'localhost' || host === '127.0.0.1' || host === '::1') return true;\n if (host.endsWith('.local')) return true;\n if (/^10\\./.test(host)) return true;\n if (/^192\\.168\\./.test(host)) return true;\n if (/^172\\.(1[6-9]|2\\d|3[0-1])\\./.test(host)) return true;\n return false;\n}\n\nexport function isLoopbackOrPrivateUrl(url: string): boolean {\n return isLoopbackOrPrivateHost(hostnameOf(url));\n}\n\n/**\n * GraphQL backend whose SECRET_API_TOKEN_* keys hosted cdecli-agent will use.\n *\n * Local GRAPHQL_URL signs with the laptop JWT secret; `cdecli-agent.cdebase.dev`\n * verifies with the cluster keys. Minting against CANVAS_BACKEND_GRAPHQL_URL\n * (ideback) is the same pairing canvas already uses for its index.\n */\nexport function resolveCdecliAgentTokenGraphqlUrl(\n appGraphqlUrl: string,\n agentEndpoint: string,\n clusterGraphqlUrl: string,\n): string {\n const app = normalizeGraphqlUrl(appGraphqlUrl);\n const cluster = normalizeGraphqlUrl(clusterGraphqlUrl);\n if (!isLoopbackOrPrivateUrl(app)) return app;\n if (isLoopbackOrPrivateUrl(agentEndpoint)) return app;\n return cluster || app;\n}\n\n/**\n * Local GraphQL mints S3/MinIO URLs the laptop can fetch; hosted cdecli-agent\n * cannot. Inline the bytes so the gateway re-hosts into the agent workspace\n * (web: \"saved the actual image locally — let me view it now\").\n */\nexport function shouldInlineGatewayMediaForHostedAgent(appGraphqlUrl: string, agentEndpoint: string): boolean {\n return isLoopbackOrPrivateUrl(appGraphqlUrl) && !isLoopbackOrPrivateUrl(agentEndpoint);\n}\n\nfunction decodeJwtJson(part: string | undefined): Record<string, unknown> | null {\n if (!part) return null;\n try {\n const b64 = part.replace(/-/g, '+').replace(/_/g, '/');\n const pad = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n const json = typeof atob === 'function' ? atob(pad) : Buffer.from(pad, 'base64').toString('utf8');\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nexport function getJwtAlg(token: string): string | null {\n const header = decodeJwtJson(token.split('.')[0]);\n return typeof header?.alg === 'string' ? header.alg : null;\n}\n\nexport function getJwtExpirySec(token: string): number | null {\n const payload = decodeJwtJson(token.split('.')[1]);\n return typeof payload?.exp === 'number' ? payload.exp : null;\n}\n\nexport function isJwtExpired(token: string, skewSec = 60): boolean {\n const exp = getJwtExpirySec(token);\n if (exp == null) return false;\n return exp < Math.floor(Date.now() / 1000) + skewSec;\n}\n\n/** True when a GetUserSystemToken row is expired or within an hour of expiry. */\nexport function isSystemTokenRowStale(expiresAt: string | null | undefined): boolean {\n if (!expiresAt) return false;\n const expiryMs = new Date(expiresAt).getTime();\n if (!Number.isFinite(expiryMs)) return false;\n return expiryMs - Date.now() < 3600_000;\n}\n\n/** cdecli-agent rejected the system JWT (signature / 401), not a generic LLM error. */\nexport function isAgentJwtRejected(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n return (\n /invalid HS256 signature/i.test(t) ||\n /invalid RS256 signature/i.test(t) ||\n /invalid token/i.test(t) ||\n /Session creation failed:\\s*HTTP 401/i.test(t) ||\n /Failed to create session[\\s\\S]*HTTP 401/i.test(t)\n );\n}\n\nexport function isMissingRs256KeyError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err ?? '');\n return /private key|SECRET_API_TOKEN_PRIVATE_KEY|RS256/i.test(message);\n}\n\nexport function isAlreadyHasSystemTokenError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err ?? '');\n return /already have a system token/i.test(message);\n}\n\nexport function isMissingVaultSecretError(message: string): boolean {\n return /secret not found|not found in vault/i.test(message);\n}\n"],"names":[],"mappings":"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"}
|