included more features
This commit is contained in:
parent
c3fb382127
commit
9049531298
|
|
@ -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```';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})();
|
||||
|
|
|
|||
23
src/main.js
23
src/main.js
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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}`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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==
|
||||
Loading…
Reference in New Issue