145 lines
5.8 KiB
JavaScript
145 lines
5.8 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const jsdom = require('jsdom');
|
|
const { JSDOM } = jsdom;
|
|
|
|
const observerModule = require('./extension/out/observer-script.js');
|
|
const observerCode = observerModule.generateApprovalObserverScript(8080);
|
|
const rawDump = JSON.parse(fs.readFileSync('C:/Users/Variet-Worker/.gemini/antigravity/bridge/dump_html.json', 'utf8'));
|
|
|
|
// Instantiate DOM
|
|
const dom = new JSDOM(rawDump.html, { runScripts: "dangerously", pretendToBeVisual: true, url: "http://localhost/" });
|
|
const window = dom.window;
|
|
const document = window.document;
|
|
|
|
// Polyfill offsetParent for visibility check
|
|
Object.defineProperty(window.HTMLElement.prototype, 'offsetParent', {
|
|
get() { return document.body; }
|
|
});
|
|
Object.defineProperty(window.HTMLElement.prototype, 'style', {
|
|
get() { return { display: 'block' }; }
|
|
});
|
|
|
|
// Mock innerText (JSDOM does not fully support it, but generic walker uses nodeValue / textContent)
|
|
// Our logic uses nodeValue for TextNodes, so it will work in JSDOM out of the box!
|
|
|
|
// Setup the DOM tree to perfectly match a real Chat conversation
|
|
const titleSpan = document.querySelector('span[title^="command("]');
|
|
if(!titleSpan) {
|
|
console.error("COULD NOT FIND command SPAN in dump?!");
|
|
process.exit(1);
|
|
}
|
|
// Find the card container
|
|
let toolContainer = titleSpan.parentElement;
|
|
while(toolContainer && !toolContainer.className.includes("border-gray-500/10") && !toolContainer.className.includes("bg-gray-500/10")) {
|
|
toolContainer = toolContainer.parentElement;
|
|
}
|
|
if(!toolContainer) toolContainer = titleSpan.parentElement; // fallback
|
|
|
|
// Create an AI text block just above it
|
|
const aiChat = document.createElement('div');
|
|
aiChat.className = 'markdown prose';
|
|
aiChat.innerHTML = '<p>안녕하세요! 시스템을 수정하기 위해 요청하신 작업을 시작합니다. <b>디스코드 릴레이 기능 복구</b>를 위해 스크립트를 실행하겠습니다.</p>';
|
|
|
|
// Wrap them up in the turn container
|
|
const parent = toolContainer.parentElement;
|
|
const convoWrapper = document.createElement('div');
|
|
convoWrapper.className = 'bg-agent-convo-background';
|
|
parent.insertBefore(convoWrapper, toolContainer);
|
|
convoWrapper.appendChild(aiChat);
|
|
convoWrapper.appendChild(toolContainer); // Move tool inside the convo wrapper as a sibling to AI chat
|
|
|
|
// Add action button to the tool container
|
|
const btn = document.createElement('button');
|
|
btn.innerHTML = '<span class="truncate">Allow</span>';
|
|
toolContainer.appendChild(btn);
|
|
|
|
console.log("Mock Button offsetParent:", btn.offsetParent ? btn.offsetParent.tagName : 'null');
|
|
console.log("Mock Button display:", btn.style.display);
|
|
console.log("Mock Button text:", btn.textContent);
|
|
|
|
// MOCK FETCH
|
|
const fetchCalls = [];
|
|
window.fetch = function(url, options) {
|
|
fetchCalls.push({url, options});
|
|
if (url.includes('/ping')) {
|
|
return Promise.resolve({ text: function() { return Promise.resolve('pong'); } });
|
|
}
|
|
if (url.includes('/pending')) {
|
|
return Promise.resolve({ json: function() { return Promise.resolve({ok: true, request_id: 'test-rid'}); } });
|
|
}
|
|
return Promise.resolve({ json: function() { return Promise.resolve({}); } });
|
|
};
|
|
|
|
// Polyfill offsetParent for visibility check
|
|
Object.defineProperty(window.HTMLElement.prototype, 'offsetParent', {
|
|
get() { return document.body; }
|
|
});
|
|
Object.defineProperty(window.HTMLElement.prototype, 'style', {
|
|
get() { return { display: 'block' }; }
|
|
});
|
|
|
|
const originalLog = console.log;
|
|
window.console.log = function(...args) {
|
|
if (args.length > 0 && typeof args[0] === 'string' && args[0].includes("NOT MATCHED")) {
|
|
let txt = args[1];
|
|
let codes = [];
|
|
if (txt) {
|
|
for(let i=0; i<txt.length; i++) codes.push(txt.charCodeAt(i));
|
|
}
|
|
originalLog('[JSDOM-WIN]', args[0], `\nRAW="${txt}"\nCODES=[${codes.join(',')}]`);
|
|
} else {
|
|
originalLog('[JSDOM-WIN]', ...args);
|
|
}
|
|
};
|
|
|
|
// Inject the observer
|
|
const scriptEl = document.createElement('script');
|
|
scriptEl.textContent = observerCode;
|
|
document.body.appendChild(scriptEl);
|
|
|
|
console.log("Observer injected, waiting for cycles...");
|
|
console.log("Total Buttons in DOM:", document.querySelectorAll('button').length);
|
|
|
|
// Emulate UI mutation to trigger the MutationObserver and force an instant scan()
|
|
setTimeout(() => {
|
|
console.log("Triggering DOM mutation to force scan()...");
|
|
document.body.appendChild(document.createElement('span'));
|
|
}, 1500);
|
|
|
|
// Give it time to finish scan().
|
|
setTimeout(() => {
|
|
console.log("\n====== FETCH CALLS ======");
|
|
if(fetchCalls.length === 0) console.log("NO FETCH CALLS MADE!");
|
|
|
|
fetchCalls.forEach(c => {
|
|
console.log(`\n[${c.options ? c.options.method || 'GET' : 'GET'}] ${c.url}`);
|
|
if(c.options && c.options.body) {
|
|
try {
|
|
let j = JSON.parse(c.options.body);
|
|
console.log("[BODY] request_id:", j.request_id);
|
|
console.log("[BODY] command:", j.command);
|
|
console.log("[BODY] description:\n" + "=".repeat(40) + "\n" + j.description + "\n" + "=".repeat(40));
|
|
} catch(e) {
|
|
console.log("[BODY]", c.options.body);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Determine success
|
|
const pendingCall = fetchCalls.find(c => c.url.includes('/pending'));
|
|
if(pendingCall && pendingCall.options && pendingCall.options.body) {
|
|
const payload = JSON.parse(pendingCall.options.body);
|
|
if(payload.description.includes("안녕하세요!") && payload.description.includes("METHOD=TITLE_SPAN")) {
|
|
console.log("\n✅ SUCCESS: Both Chat Body & Tool Command effectively extracted!");
|
|
process.exit(0);
|
|
} else {
|
|
console.log("\n❌ FAIL: Payload description missing either chat text or command string!");
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
console.log("\n❌ FAIL: /pending never called!");
|
|
process.exit(1);
|
|
}
|
|
}, 4000);
|