akashyadav758
Fix extension COPY paths in Dockerfile and rename extension dirs
4216716
Raw
History Blame Contribute Delete
6.16 kB
/**
* Injected into MAIN world on labs.google β€” has access to window.grecaptcha
* Also intercepts TRPC fetch responses to capture fresh signed media URLs.
*/
const SITE_KEY = '6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV';
// ─── XHR Interceptor (for file uploads) ─────────────────────
const _xhrOpen = XMLHttpRequest.prototype.open;
const _xhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
this.__sniffUrl = url;
this.__sniffMethod = method;
return _xhrOpen.call(this, method, url, ...rest);
};
XMLHttpRequest.prototype.send = function (body) {
try {
const url = this.__sniffUrl || '';
if (url.includes('googleapis.com') || url.includes('labs.google') || url.includes('storage.google')) {
window.postMessage({
type: '__FLOWKIT_SNIFF__',
url,
body: typeof body === 'string' ? body : `(binary ${body?.size || body?.byteLength || '?'} bytes)`,
method: this.__sniffMethod || 'POST',
}, '*');
}
} catch {}
return _xhrSend.call(this, body);
};
// ─── TRPC Response Monitor ─────────────────────────────────
// Monkey-patch fetch to intercept TRPC responses containing media URLs.
// Fresh signed GCS URLs are extracted and forwarded to the agent.
const _originalFetch = window.fetch;
window.fetch = async function (...args) {
try {
const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';
// ─── SNIFF ALL outgoing requests (catch upload) ─────────
{
let bodyText = '';
if (args[1]?.body) {
const b = args[1].body;
if (typeof b === 'string') bodyText = b.length > 5000 ? b.slice(0, 200) + `...(${b.length} chars)` : b;
else if (b instanceof FormData) bodyText = `(FormData: ${[...b.keys()].join(', ')})`;
else if (b instanceof Blob) bodyText = `(Blob ${b.size} bytes, type=${b.type})`;
else if (b instanceof ArrayBuffer) bodyText = `(ArrayBuffer ${b.byteLength} bytes)`;
else if (b instanceof ReadableStream) bodyText = '(ReadableStream)';
else bodyText = JSON.stringify(b)?.slice(0, 2000) || '(unknown)';
}
window.postMessage({
type: '__FLOWKIT_SNIFF__',
url, body: bodyText, method: args[1]?.method || 'GET',
}, '*');
}
} catch {}
const response = await _originalFetch.apply(this, args);
try {
const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';
// Only intercept TRPC calls on labs.google that return project/flow data
if (url.includes('/fx/api/trpc/') && response.ok) {
const clone = response.clone();
clone.text().then(text => {
if (text.includes('storage.googleapis.com/ai-sandbox-videofx/')) {
window.dispatchEvent(new CustomEvent('TRPC_MEDIA_URLS', {
detail: { url, body: text },
}));
}
}).catch(() => {});
}
} catch {}
return response;
};
window.addEventListener('GET_CAPTCHA', async ({ detail }) => {
const { requestId, pageAction } = detail;
try {
await waitForGrecaptcha();
const token = await window.grecaptcha.enterprise.execute(SITE_KEY, {
action: pageAction,
});
window.dispatchEvent(new CustomEvent('CAPTCHA_RESULT', {
detail: { requestId, token },
}));
} catch (e) {
window.dispatchEvent(new CustomEvent('CAPTCHA_RESULT', {
detail: { requestId, error: e.message },
}));
}
});
function waitForGrecaptcha(timeout = 10000) {
return new Promise((resolve, reject) => {
const start = Date.now();
const check = () => {
if (window.grecaptcha?.enterprise?.execute) return resolve();
if (Date.now() - start > timeout) return reject(new Error('grecaptcha not available'));
setTimeout(check, 200);
};
check();
});
}
// ─── Video Upload Handler ───────────────────────────────────
window.addEventListener('UPLOAD_VIDEO', async ({ detail }) => {
const { requestId, videoBase64, projectId } = detail;
try {
// Convert base64 to Blob
const byteChars = atob(videoBase64);
const byteArray = new Uint8Array(byteChars.length);
for (let i = 0; i < byteChars.length; i++) {
byteArray[i] = byteChars.charCodeAt(i);
}
const blob = new Blob([byteArray], { type: 'video/mp4' });
// Step 1: POST start β€” get session URL
const startResp = await _originalFetch('/fx/api/upload-video?action=start', {
method: 'POST',
credentials: 'include',
headers: {
'X-Upload-Project-Id': projectId || '',
'X-Upload-Content-Type': 'video/mp4',
'X-Upload-Content-Length': blob.size.toString(),
},
});
const sessionUrl = startResp.headers.get('X-Upload-Session-Url') || '';
const startData = await startResp.json().catch(() => ({}));
// sessionUrl may be in header OR in response body
const finalSessionUrl = sessionUrl || startData.sessionUrl || '';
startData._sessionUrl = finalSessionUrl;
startData._status = startResp.status;
if (!finalSessionUrl) {
window.dispatchEvent(new CustomEvent('UPLOAD_VIDEO_RESULT', {
detail: { requestId, error: 'NO_SESSION_URL', startData },
}));
return;
}
// Step 2: PUT directly to GCS session URL with resumable upload headers
const uploadResp = await _originalFetch(finalSessionUrl, {
method: 'PUT',
body: blob,
headers: {
'Content-Type': 'video/mp4',
'X-Goog-Upload-Command': 'upload, finalize',
'X-Goog-Upload-Offset': '0',
},
});
const uploadData = await uploadResp.json().catch(() => ({}));
uploadData._status = uploadResp.status;
window.dispatchEvent(new CustomEvent('UPLOAD_VIDEO_RESULT', {
detail: { requestId, startData, uploadData, status: uploadResp.status },
}));
} catch (e) {
window.dispatchEvent(new CustomEvent('UPLOAD_VIDEO_RESULT', {
detail: { requestId, error: e.message },
}));
}
});