included more features

This commit is contained in:
rob 2025-10-15 22:28:31 -03:00
parent c3fb382127
commit 9049531298
6 changed files with 116 additions and 18 deletions

View File

@ -1,7 +1,30 @@
// ==COMMAND EXECUTOR START==
/* global GM_xmlhttpRequest */
/* global GM_notification */
(function () {
/**
* @typedef {Object} RepoCommand
* @property {string} action
* @property {string} [repo]
* @property {string} [owner]
* @property {string} [path]
* @property {string} [content]
* @property {string} [commit_message]
* @property {string} [url]
* @property {boolean} [example]
*/
/**
* @typedef {Object} FileEntry
* @property {string} [path]
* @property {string} [name]
*/
class CommandExecutor {
/**
* @param {RepoCommand} command
* @param {Element} sourceElement
* @param {string} [label]
*/
static async execute(command, sourceElement, label = '') {
const log = window.AI_REPO_LOGGER;
const cfg = window.AI_REPO_CONFIG;
@ -97,6 +120,7 @@
}
static _handleListFiles(data, label) {
/** @type {Array<string|FileEntry>} */
const files = data?.files ?? data?.result?.files;
if (!Array.isArray(files)) return;
const listing = '```text\n' + files.map(f => (typeof f === 'string' ? f : (f?.path || f?.name || JSON.stringify(f)))).join('\n') + '\n```';

View File

@ -11,12 +11,12 @@
}
function isAssistantMsg(el) {
const sels = [
const selectors = [
'[data-message-author-role="assistant"]',
'.chat-message:not([data-message-author-role="user"])',
'.message-content'
];
return sels.some(s => el.matches?.(s) || el.querySelector?.(s));
return selectors.some(s => el.matches?.(s) || el.querySelector?.(s));
}
async function settleText(el, initial, windowMs, pollMs) {
@ -47,7 +47,7 @@
for (const m of mutations) {
if (m.type === 'childList') {
for (const n of m.addedNodes) {
if (n.nodeType === 1 && isAssistantMsg(n)) { this._handle(n); should = true; }
if (n.nodeType === 1 && isAssistantMsg(n)) { void this._handle(n); should = true; }
}
}
if (m.type === 'characterData') {
@ -60,7 +60,7 @@
this.observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true });
if (cfg().get('ui.processExisting')) {
document.querySelectorAll('[data-message-author-role], .chat-message, .message-content')
.forEach(el => isAssistantMsg(el) && this._handle(el));
.forEach(el => isAssistantMsg(el) && void this._handle(el));
}
log().info('Detector started');
}
@ -98,7 +98,12 @@
try {
const parsed = window.AI_REPO_PARSER.parse(commandText);
const v = window.AI_REPO_PARSER.validate(parsed);
if (!v.isValid) throw new Error(`Validation failed: ${v.errors.join(', ')}`);
if (!v.isValid) {
// Handle validation failures inline instead of throwing; avoid local throw/catch
try { log().warn?.('Validation failed', { errors: v.errors }); } catch {}
this._addRunAgain(el, commandText, idx);
return;
}
if (v.example) { log().info('Example command skipped'); return; }
await window.AI_REPO_EXECUTOR.execute(parsed, el, `[${idx + 1}] ${parsed.action}`);
} catch (e) {
@ -120,7 +125,7 @@
let scanned = 0; let cur = anchor.nextElementSibling;
while (cur && scanned < this.clusterLookahead) {
if (!isAssistantMsg(cur)) break;
if (!this.processed.has(cur)) this._handle(cur);
if (!this.processed.has(cur)) void this._handle(cur);
scanned++; cur = cur.nextElementSibling;
}
}

View File

@ -1,7 +1,14 @@
// ==FINGERPRINT (drop-in utility) ==
(function(){
const MSG_SELECTORS = [
'[data-message-author-role="assistant"]',
'.chat-message:not([data-message-author-role="user"])',
'.message-content'
];
function norm(s){ return (s||'').replace(/\r/g,'').replace(/\u200b/g,'').replace(/[ \t]+\n/g,'\n').trim(); }
function hash(s){ let h=5381; for(let i=0;i<s.length;i++) h=((h<<5)+h)^s.charCodeAt(i); return (h>>>0).toString(36); }
function commandLikeText(el){
const blocks = el.querySelectorAll('pre code, pre, code');
for (const b of blocks) {
@ -10,8 +17,9 @@
}
return norm((el.textContent || '').slice(0, 2000));
}
function prevContextHash(el) {
const list = Array.from(document.querySelectorAll('[data-message-author-role], .chat-message, .message-content'));
const list = Array.from(document.querySelectorAll(MSG_SELECTORS.join(',')));
const idx = list.indexOf(el); if (idx <= 0) return '0';
let rem = 2000, buf = '';
for (let i=idx-1; i>=0 && rem>0; i--){
@ -20,16 +28,57 @@
}
return hash(buf.slice(-2000));
}
function intraPrefixHash(el){
const t = el.textContent || '';
const m = t.match(/@bridge@[\s\S]*?@end@/m);
const endIdx = m ? t.indexOf(m[0]) : t.length;
return hash(norm(t.slice(Math.max(0, endIdx - 2000), endIdx)));
}
window.AI_REPO_FINGERPRINT = function(el){
function domHint(node) {
if (!node) return '';
const id = node.id || '';
const cls = (node.className && typeof node.className === 'string') ? node.className.split(' ')[0] : '';
return `${node.tagName || ''}#${id}.${cls}`.slice(0, 40);
}
function ordinalForKey(el, key) {
const list = Array.from(document.querySelectorAll(MSG_SELECTORS.join(',')));
let n = 0;
for (const node of list) {
const nodeKey = node === el ? key : (() => {
const ch = hash(commandLikeText(node).slice(0, 2000));
const ph = prevContextHash(node);
const ih = intraPrefixHash(node);
return `ch:${ch}|ph:${ph}|ih:${ih}`;
})();
if (nodeKey === key) {
if (node === el) return n;
n++;
}
}
return n;
}
function fingerprintElement(el){
const ch = hash(commandLikeText(el).slice(0, 2000));
const ph = prevContextHash(el);
const ih = intraPrefixHash(el);
return `ch:${ch}|ph:${ph}|ih:${ih}`;
};
const dh = hash(domHint(el));
const key = `ch:${ch}|ph:${ph}|ih:${ih}`;
const n = ordinalForKey(el, key);
return `${key}|hint:${dh}|n:${n}`;
}
function getStableFingerprint(el) {
if (el?.dataset?.aiRcStableFp) return el.dataset.aiRcStableFp;
const fp = fingerprintElement(el);
try { if (el && el.dataset) el.dataset.aiRcStableFp = fp; } catch {}
return fp;
}
// Expose both for backward compatibility
window.AI_REPO_FINGERPRINT = fingerprintElement;
window.AI_REPO_STABLE_FINGERPRINT = getStableFingerprint;
})();

View File

@ -69,7 +69,7 @@
if (history.isProcessed(el, idx)) {
this.addRetryButton(el, cmdText, idx);
} else {
this.run(el, cmdText, idx);
void this.run(el, cmdText, idx);
}
});
}
@ -126,12 +126,13 @@
}
exposeAPI() {
window.AI_REPO_COMMANDER = {
// Public API (short name)
window.AI_REPO = {
version: config.get('meta.version'),
config,
logger,
config: config,
logger: logger,
history,
pause: () => { config.set('runtime.paused', true); logger.info('Paused'); },
pause: () => { config.set('runtime.paused', true); logger.info('Paused'); },
resume: () => { config.set('runtime.paused', false); logger.info('Resumed'); },
clearHistory: () => { history.clear(); logger.info('History cleared'); }
};
@ -147,9 +148,21 @@
logger.error(`🚨 EMERGENCY STOP: cancelled ${queuedCount} queued command(s)`);
logger.error('API disabled and scanning paused');
};
// Bridge key setter
window.AI_REPO_SET_KEY = function(k) {
if (typeof k === 'string' && k.trim()) {
config.set('api.bridgeKey', k.trim());
logger.info('Bridge key updated');
return true;
}
logger.warn('Invalid bridge key');
return false;
};
}
delay(ms) { return new Promise(r => setTimeout(r, ms)); }
destroy() {
this.observer?.disconnect();
this.processed = new WeakSet();

View File

@ -34,7 +34,12 @@
}
_fingerprint(el, idx) {
const base = window.AI_REPO_FINGERPRINT ? window.AI_REPO_FINGERPRINT(el) : this._hash((el.textContent||'').slice(0,1000));
// Use stable fingerprinting if available (caches on element.dataset)
const base = window.AI_REPO_STABLE_FINGERPRINT
? window.AI_REPO_STABLE_FINGERPRINT(el)
: (window.AI_REPO_FINGERPRINT
? window.AI_REPO_FINGERPRINT(el)
: this._hash((el.textContent||'').slice(0,1000)));
return `${base}|idx:${idx}`;
}

View File

@ -9,18 +9,20 @@
// @match https://claude.ai/*
// @match https://gemini.google.com/*
// @grant GM_xmlhttpRequest
// @grant GM_notification
// @grant GM_setClipboard
// @connect n8n.brrd.tech
// @connect *
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/config.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/logger.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/fingerprint-strong.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/storage.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/command-parser.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/command-executor.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/main.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/queue.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/command-executor.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/response-buffer.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/paste-submit.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/detector.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/debug-panel.js
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/main.js
// ==/UserScript==