const COMCIP_ORIGIN = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com"; const COMCIP_APP_URL = `${COMCIP_ORIGIN}/ic/builder/rt/oalset_semc/live/webApps/Dashboard/?page=shell&shell=main&main=service-requests-detailed-view`; const COMCIP_QUERY_URL = `${COMCIP_ORIGIN}/ic/builder/rt/oalset_semc/live;profile=PROD/services/auth/1.1/proxy/oalsetCRMRestAPIElastic/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/searchResources/latest/custom-actions/queries`; const COMCIP_CLIENT_ID_PROBE_URL = `${COMCIP_ORIGIN}/ic/builder/rt/oalset_semc/live;profile=PROD/services/auth/1.1/proxy/oalsetSeaaSOKECustomRestAPI/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/countries`; const COMCIP_TIME_ENTRY_APP_URL = `${COMCIP_ORIGIN}/ic/builder/rt/oalset_timeentrymobile/live/webApps/timeentry/`; const COMCIP_TIME_ENTRY_CLIENT_ID_PROBE_URL = `${COMCIP_ORIGIN}/ic/builder/rt/oalset_timeentrymobile/live;profile=PROD/services/auth/1.1/proxy/oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/lookups?lookupType=SCTA_TASK_TYPE&type=CUSTOM`; const COMCIP_TIME_ENTRY_APP_VERSION = "version_1774016034953"; const COMCIP_TAB_URL_PATTERN = `${COMCIP_ORIGIN}/ic/builder/rt/oalset_semc/live*`; const COMCIP_TIME_ENTRY_TAB_URL_PATTERN = `${COMCIP_ORIGIN}/ic/builder/rt/oalset_timeentrymobile/live*`; chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if ( !message || (message.type !== "ARCH_PANEL_COMCIP_POST" && message.type !== "ARCH_PANEL_COMCIP_REQUEST") ) { return false; } requestComcipFromPage({ url: message.url || COMCIP_QUERY_URL, method: message.method || "POST", payload: message.payload, headers: message.headers, senderTabId: sender?.tab?.id, useTimeEntryFrame: Boolean(message.useTimeEntryFrame), }) .then(sendResponse) .catch((error) => { sendResponse({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), }); }); return true; }); async function requestComcipFromPage({ url, method = "POST", payload, headers = {}, senderTabId, useTimeEntryFrame = false, }) { const route = getComcipRoute(url); if (route.useSenderFrame || useTimeEntryFrame) { if (!senderTabId) { throw new Error("Unable to locate the current dashboard tab for COMCIP Time Entry."); } await waitForComcipFrameSessionReady(senderTabId, headers, route); let response = await executeComcipFetchFromFrame( senderTabId, url, method, payload, headers, route ); if (isAuthorizationFailure(response) || isOutdatedApplicationFailure(response)) { await delay(1500); await waitForComcipFrameSessionReady(senderTabId, headers, route); response = await executeComcipFetchFromFrame( senderTabId, url, method, payload, headers, route ); } return response; } const activeTab = await captureActiveTab(); const target = await getComcipTab(route, activeTab); try { await restoreActiveTab(activeTab); await waitForComcipTabReady(target.tab.id); await waitForComcipSessionReady(target.tab.id, headers, route); let response = await executeComcipFetch( target.tab.id, url, method, payload, headers, route ); if (isAuthorizationFailure(response)) { await delay(1500); await waitForComcipSessionReady(target.tab.id, headers, route); response = await executeComcipFetch( target.tab.id, url, method, payload, headers, route ); } return response; } finally { if (target.created && target.tab.id) { await chrome.tabs.remove(target.tab.id).catch(() => {}); } await restoreActiveTab(activeTab); } } function getComcipRoute(requestUrl) { const normalizedUrl = String(requestUrl || ""); if (normalizedUrl.includes("/ic/builder/rt/oalset_timeentrymobile/live")) { return { appUrl: COMCIP_TIME_ENTRY_APP_URL, clientIdProbeUrl: COMCIP_TIME_ENTRY_CLIENT_ID_PROBE_URL, tabPattern: COMCIP_TIME_ENTRY_TAB_URL_PATTERN, useSenderFrame: true, appVersion: COMCIP_TIME_ENTRY_APP_VERSION, }; } return { appUrl: COMCIP_APP_URL, clientIdProbeUrl: COMCIP_CLIENT_ID_PROBE_URL, tabPattern: COMCIP_TAB_URL_PATTERN, }; } async function getComcipTab(route, activeTab) { const tabs = await chrome.tabs.query({ url: route.tabPattern }); const existing = tabs.find((tab) => { const tabUrl = String(tab.url || ""); return tab.id && !tab.discarded && !tabUrl.includes("/services/"); }); if (existing) { return { tab: existing, created: false, }; } if (route.requireExistingTab) { throw new Error( "A sessao COMCIP Time Entry nao esta pronta. Abra/autentique o Time Entry uma vez e tente novamente." ); } const created = await chrome.tabs.create({ url: route.appUrl, active: false, windowId: activeTab?.windowId, }); await restoreActiveTab(activeTab); return { tab: created, created: true, }; } async function captureActiveTab() { const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true, }); if (!activeTab?.id) { return null; } return { id: activeTab.id, windowId: activeTab.windowId, }; } async function restoreActiveTab(activeTab) { if (!activeTab?.id) { return; } await chrome.windows.update(activeTab.windowId, { focused: true }).catch(() => {}); await chrome.tabs.update(activeTab.id, { active: true }).catch(() => {}); } async function waitForComcipTabReady(tabId) { const startedAt = Date.now(); while (Date.now() - startedAt < 45000) { const tab = await chrome.tabs.get(tabId); const url = String(tab.url || ""); if (tab.status === "complete" && url.startsWith(COMCIP_ORIGIN)) { await delay(1000); return; } await delay(500); } throw new Error("Timed out while loading the COMCIP origin page."); } async function waitForComcipSessionReady(tabId, headers = {}, route) { const startedAt = Date.now(); let lastStatus = ""; while (Date.now() - startedAt < 60000) { const probe = await executeComcipProbe(tabId, headers, route).catch((error) => ({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), })); if (probe?.ok && probe?.appBuilderClientId) { return probe; } lastStatus = probe?.status ? `${probe.status} ${probe.statusText || ""}`.trim() : probe?.error || "not ready"; await delay(1000); } throw new Error(`Timed out while waiting for COMCIP authenticated session (${lastStatus}).`); } async function waitForComcipFrameSessionReady(tabId, headers = {}, route) { const startedAt = Date.now(); let lastStatus = ""; while (Date.now() - startedAt < 60000) { const probe = await executeComcipFrameProbe(tabId, headers, route).catch((error) => ({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), })); if (probe?.ok && probe?.matched) { return probe; } lastStatus = probe?.status ? `${probe.status} ${probe.statusText || ""}`.trim() : probe?.error || "not ready"; await delay(1000); } throw new Error( `Timed out while waiting for COMCIP Time Entry frame session (${lastStatus}).` ); } async function executeComcipFrameProbe(tabId, headers = {}, route) { const injectionResults = await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", args: [ route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, headers || {}, route?.appVersion || "", ], func: async (probeUrl, sourceHeaders, routeAppVersion) => { function isTimeEntryFrame() { const href = String(window.location.href || ""); const frameName = String(window.name || ""); const isComcipOrigin = href.startsWith( "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/" ); return ( (frameName === "arch-panel-extension-comcip-timeentry-frame" && isComcipOrigin) || href.includes("oalset_timeentrymobile") || href.includes("/webApps/timeentry") ); } if (!isTimeEntryFrame()) { return { matched: false, href: window.location.href, frameName: window.name || "", }; } const response = await fetch(probeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "vb-proxy-header-rest-framework-version": "3", "x-vb-application-version": routeAppVersion || sourceHeaders["x-vb-application-version"] || "", }, }); return { matched: true, ok: response.ok, status: response.status, statusText: response.statusText, appBuilderClientId: response.headers.get("x-appbuilder-client-id") || "", href: window.location.href, frameName: window.name || "", }; }, }); const matchedResult = (injectionResults || []) .map((item) => item.result) .find((result) => result?.matched); if (!matchedResult) { const frameHints = (injectionResults || []) .map((item) => item.result) .filter(Boolean) .map((result) => result.href || result.frameName || "unknown") .slice(0, 5) .join(" | "); throw new Error( `COMCIP Time Entry frame was not found in the dashboard tab.${ frameHints ? ` Frames: ${frameHints}` : "" }` ); } return matchedResult; } async function executeComcipProbe(tabId, headers = {}, route) { const [injectionResult] = await chrome.scripting.executeScript({ target: { tabId }, world: "MAIN", args: [route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, headers || {}], func: async (probeUrl, sourceHeaders) => { const APP_VERSION = "version_1754044416761"; const response = await fetch(probeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "x-vb-application-version": sourceHeaders["x-vb-application-version"] || APP_VERSION, }, }); return { ok: response.ok, status: response.status, statusText: response.statusText, appBuilderClientId: response.headers.get("x-appbuilder-client-id") || "", href: window.location.href, }; }, }); if (!injectionResult) { throw new Error("COMCIP probe did not return a result."); } return injectionResult.result; } async function executeComcipFetch(tabId, requestUrl, method, payload, headers, route) { const [injectionResult] = await chrome.scripting.executeScript({ target: { tabId }, world: "MAIN", args: [ requestUrl, method || "POST", payload || null, headers || {}, route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, route?.appVersion || "", ], func: async ( requestUrl, requestMethod, requestPayload, sourceHeaders, clientIdProbeUrl, routeAppVersion ) => { const APP_VERSION = "version_1754044416761"; function parseResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickResponseHeaders(headers) { const safeHeaders = {}; const exposedHeaders = [ "content-type", "x-appbuilder-client-id", "x-invalid-appbuilder-client-id", "x-appbuilder-repeat-request", "vb-proxy-status-actual", "vb-proxy-version", ]; for (const key of exposedHeaders) { const value = headers.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } async function resolveAppBuilderClientId() { try { const response = await fetch(clientIdProbeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "x-vb-application-version": sourceHeaders["x-vb-application-version"] || APP_VERSION, }, }); return response.headers.get("x-appbuilder-client-id") || ""; } catch { return ""; } } const appBuilderClientId = sourceHeaders["x-appbuilder-client-id"] || (await resolveAppBuilderClientId()); const requestHeaders = { accept: "*/*", authorization: "Session", "vb-proxy-header-preference": "transient", "x-vb-application-version": sourceHeaders["x-vb-application-version"] || APP_VERSION, "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", }; if (appBuilderClientId) { requestHeaders["x-appbuilder-client-id"] = appBuilderClientId; } const normalizedMethod = String(requestMethod || "POST").toUpperCase(); const fetchOptions = { method: normalizedMethod, credentials: "include", cache: "no-store", headers: requestHeaders, }; if (normalizedMethod !== "GET" && normalizedMethod !== "HEAD") { requestHeaders["content-type"] = "application/json"; fetchOptions.body = JSON.stringify(requestPayload || {}); } const response = await fetch(requestUrl, { ...fetchOptions, }); const text = await response.text(); return { ok: response.ok, status: response.status, statusText: response.statusText, headers: pickResponseHeaders(response.headers), payload: parseResponseText(text), debug: { href: window.location.href, origin: window.location.origin, headerKeys: Object.keys(requestHeaders), }, }; }, }); if (!injectionResult) { throw new Error("COMCIP page script did not return a result."); } return injectionResult.result; } async function executeComcipFetchFromFrame( tabId, requestUrl, method, payload, headers, route ) { const injectionResults = await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", args: [ requestUrl, method || "POST", payload || null, headers || {}, route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, route?.appVersion || "", ], func: async ( requestUrl, requestMethod, requestPayload, sourceHeaders, clientIdProbeUrl, routeAppVersion ) => { function isTimeEntryFrame() { const href = String(window.location.href || ""); const frameName = String(window.name || ""); const isComcipOrigin = href.startsWith( "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/" ); return ( (frameName === "arch-panel-extension-comcip-timeentry-frame" && isComcipOrigin) || href.includes("oalset_timeentrymobile") || href.includes("/webApps/timeentry") ); } if (!isTimeEntryFrame()) { return { matched: false, href: window.location.href, frameName: window.name || "", }; } function parseResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickResponseHeaders(responseHeaders) { const safeHeaders = {}; const exposedHeaders = [ "content-type", "x-appbuilder-client-id", "x-invalid-appbuilder-client-id", "x-appbuilder-repeat-request", "vb-proxy-status-actual", "vb-proxy-version", ]; for (const key of exposedHeaders) { const value = responseHeaders.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } async function resolveAppBuilderClientId() { try { const response = await fetch(clientIdProbeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "vb-proxy-header-rest-framework-version": "3", "x-vb-application-version": routeAppVersion || sourceHeaders["x-vb-application-version"] || "", }, }); return response.headers.get("x-appbuilder-client-id") || ""; } catch { return ""; } } const appBuilderClientId = sourceHeaders["x-appbuilder-client-id"] || (await resolveAppBuilderClientId()); const requestHeaders = { accept: "*/*", authorization: "Session", "vb-proxy-header-preference": "transient", "vb-proxy-header-rest-framework-version": "3", "x-vb-application-version": routeAppVersion || sourceHeaders["x-vb-application-version"] || "", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", }; if (appBuilderClientId) { requestHeaders["x-appbuilder-client-id"] = appBuilderClientId; } const normalizedMethod = String(requestMethod || "POST").toUpperCase(); const fetchOptions = { method: normalizedMethod, credentials: "include", cache: "no-store", headers: requestHeaders, }; if (normalizedMethod !== "GET" && normalizedMethod !== "HEAD") { requestHeaders["content-type"] = "application/json"; fetchOptions.body = JSON.stringify(requestPayload || {}); } const response = await fetch(requestUrl, fetchOptions); const text = await response.text(); return { matched: true, ok: response.ok, status: response.status, statusText: response.statusText, headers: pickResponseHeaders(response.headers), payload: parseResponseText(text), debug: { href: window.location.href, origin: window.location.origin, frameName: window.name || "", headerKeys: Object.keys(requestHeaders), frameFetch: true, }, }; }, }); const matchedResult = (injectionResults || []) .map((item) => item.result) .find((result) => result?.matched); if (!matchedResult) { const frameHints = (injectionResults || []) .map((item) => item.result) .filter(Boolean) .map((result) => result.href || result.frameName || "unknown") .slice(0, 5) .join(" | "); throw new Error( `COMCIP Time Entry frame did not return a result.${ frameHints ? ` Frames: ${frameHints}` : "" }` ); } return matchedResult; } async function executeComcipDirectFetch( requestUrl, method = "GET", payload = null, headers = {}, route ) { const APP_VERSION = "version_1754044416761"; function parseResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickResponseHeaders(responseHeaders) { const safeHeaders = {}; const exposedHeaders = [ "content-type", "x-appbuilder-client-id", "x-invalid-appbuilder-client-id", "x-appbuilder-repeat-request", "vb-proxy-status-actual", "vb-proxy-version", ]; for (const key of exposedHeaders) { const value = responseHeaders.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } async function resolveAppBuilderClientId() { try { const response = await fetch(route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": headers["accept-language"] || "pt-BR", }, }); return response.headers.get("x-appbuilder-client-id") || ""; } catch { return ""; } } const appBuilderClientId = headers["x-appbuilder-client-id"] || (await resolveAppBuilderClientId()); const requestHeaders = { accept: "*/*", authorization: "Session", "vb-proxy-header-preference": "transient", "accept-language": headers["accept-language"] || "pt-BR", }; if (!route?.omitAppVersion && headers["x-vb-application-version"]) { requestHeaders["x-vb-application-version"] = headers["x-vb-application-version"]; } if (appBuilderClientId) { requestHeaders["x-appbuilder-client-id"] = appBuilderClientId; } const normalizedMethod = String(method || "GET").toUpperCase(); const fetchOptions = { method: normalizedMethod, credentials: "include", cache: "no-store", headers: requestHeaders, }; if (normalizedMethod !== "GET" && normalizedMethod !== "HEAD") { requestHeaders["content-type"] = "application/json"; fetchOptions.body = JSON.stringify(payload || {}); } const response = await fetch(requestUrl, fetchOptions); const text = await response.text(); return { ok: response.ok, status: response.status, statusText: response.statusText, headers: pickResponseHeaders(response.headers), payload: parseResponseText(text), debug: { directFetch: true, origin: chrome.runtime.getURL(""), headerKeys: Object.keys(requestHeaders), }, }; } function isAuthorizationFailure(response) { if (!response) { return false; } const payloadText = typeof response.payload === "string" ? response.payload : JSON.stringify(response.payload || ""); return ( response.status === 401 || response.status === 500 && /vb:\/\/trap\/model\/no_auth/i.test(payloadText) || /No user authentication for the service/i.test(payloadText) || /missingAnonymous/i.test(payloadText) || /401 Authorization Required/i.test(payloadText) || /Authorization Required/i.test(payloadText) ); } function isOutdatedApplicationFailure(response) { if (!response) { return false; } const payloadText = typeof response.payload === "string" ? response.payload : JSON.stringify(response.payload || ""); return /out-dated version of the application/i.test(payloadText); } function delay(milliseconds) { return new Promise((resolve) => { setTimeout(resolve, milliseconds); }); }