included more features
This commit is contained in:
parent
c3fb382127
commit
9049531298
|
|
@ -1,7 +1,30 @@
|
||||||
// ==COMMAND EXECUTOR START==
|
// ==COMMAND EXECUTOR START==
|
||||||
/* global GM_xmlhttpRequest */
|
/* global GM_xmlhttpRequest */
|
||||||
|
/* global GM_notification */
|
||||||
(function () {
|
(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 {
|
class CommandExecutor {
|
||||||
|
/**
|
||||||
|
* @param {RepoCommand} command
|
||||||
|
* @param {Element} sourceElement
|
||||||
|
* @param {string} [label]
|
||||||
|
*/
|
||||||
static async execute(command, sourceElement, label = '') {
|
static async execute(command, sourceElement, label = '') {
|
||||||
const log = window.AI_REPO_LOGGER;
|
const log = window.AI_REPO_LOGGER;
|
||||||
const cfg = window.AI_REPO_CONFIG;
|
const cfg = window.AI_REPO_CONFIG;
|
||||||
|
|
@ -97,6 +120,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
static _handleListFiles(data, label) {
|
static _handleListFiles(data, label) {
|
||||||
|
/** @type {Array<string|FileEntry>} */
|
||||||
const files = data?.files ?? data?.result?.files;
|
const files = data?.files ?? data?.result?.files;
|
||||||
if (!Array.isArray(files)) return;
|
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```';
|
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) {
|
function isAssistantMsg(el) {
|
||||||
const sels = [
|
const selectors = [
|
||||||
'[data-message-author-role="assistant"]',
|
'[data-message-author-role="assistant"]',
|
||||||
'.chat-message:not([data-message-author-role="user"])',
|
'.chat-message:not([data-message-author-role="user"])',
|
||||||
'.message-content'
|
'.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) {
|
async function settleText(el, initial, windowMs, pollMs) {
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
for (const m of mutations) {
|
for (const m of mutations) {
|
||||||
if (m.type === 'childList') {
|
if (m.type === 'childList') {
|
||||||
for (const n of m.addedNodes) {
|
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') {
|
if (m.type === 'characterData') {
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
this.observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true });
|
this.observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true });
|
||||||
if (cfg().get('ui.processExisting')) {
|
if (cfg().get('ui.processExisting')) {
|
||||||
document.querySelectorAll('[data-message-author-role], .chat-message, .message-content')
|
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');
|
log().info('Detector started');
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +98,12 @@
|
||||||
try {
|
try {
|
||||||
const parsed = window.AI_REPO_PARSER.parse(commandText);
|
const parsed = window.AI_REPO_PARSER.parse(commandText);
|
||||||
const v = window.AI_REPO_PARSER.validate(parsed);
|
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; }
|
if (v.example) { log().info('Example command skipped'); return; }
|
||||||
await window.AI_REPO_EXECUTOR.execute(parsed, el, `[${idx + 1}] ${parsed.action}`);
|
await window.AI_REPO_EXECUTOR.execute(parsed, el, `[${idx + 1}] ${parsed.action}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -120,7 +125,7 @@
|
||||||
let scanned = 0; let cur = anchor.nextElementSibling;
|
let scanned = 0; let cur = anchor.nextElementSibling;
|
||||||
while (cur && scanned < this.clusterLookahead) {
|
while (cur && scanned < this.clusterLookahead) {
|
||||||
if (!isAssistantMsg(cur)) break;
|
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;
|
scanned++; cur = cur.nextElementSibling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
// ==FINGERPRINT (drop-in utility) ==
|
// ==FINGERPRINT (drop-in utility) ==
|
||||||
(function(){
|
(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 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 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){
|
function commandLikeText(el){
|
||||||
const blocks = el.querySelectorAll('pre code, pre, code');
|
const blocks = el.querySelectorAll('pre code, pre, code');
|
||||||
for (const b of blocks) {
|
for (const b of blocks) {
|
||||||
|
|
@ -10,8 +17,9 @@
|
||||||
}
|
}
|
||||||
return norm((el.textContent || '').slice(0, 2000));
|
return norm((el.textContent || '').slice(0, 2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevContextHash(el) {
|
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';
|
const idx = list.indexOf(el); if (idx <= 0) return '0';
|
||||||
let rem = 2000, buf = '';
|
let rem = 2000, buf = '';
|
||||||
for (let i=idx-1; i>=0 && rem>0; i--){
|
for (let i=idx-1; i>=0 && rem>0; i--){
|
||||||
|
|
@ -20,16 +28,57 @@
|
||||||
}
|
}
|
||||||
return hash(buf.slice(-2000));
|
return hash(buf.slice(-2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
function intraPrefixHash(el){
|
function intraPrefixHash(el){
|
||||||
const t = el.textContent || '';
|
const t = el.textContent || '';
|
||||||
const m = t.match(/@bridge@[\s\S]*?@end@/m);
|
const m = t.match(/@bridge@[\s\S]*?@end@/m);
|
||||||
const endIdx = m ? t.indexOf(m[0]) : t.length;
|
const endIdx = m ? t.indexOf(m[0]) : t.length;
|
||||||
return hash(norm(t.slice(Math.max(0, endIdx - 2000), endIdx)));
|
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 ch = hash(commandLikeText(el).slice(0, 2000));
|
||||||
const ph = prevContextHash(el);
|
const ph = prevContextHash(el);
|
||||||
const ih = intraPrefixHash(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)) {
|
if (history.isProcessed(el, idx)) {
|
||||||
this.addRetryButton(el, cmdText, idx);
|
this.addRetryButton(el, cmdText, idx);
|
||||||
} else {
|
} else {
|
||||||
this.run(el, cmdText, idx);
|
void this.run(el, cmdText, idx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -126,12 +126,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
exposeAPI() {
|
exposeAPI() {
|
||||||
window.AI_REPO_COMMANDER = {
|
// Public API (short name)
|
||||||
|
window.AI_REPO = {
|
||||||
version: config.get('meta.version'),
|
version: config.get('meta.version'),
|
||||||
config,
|
config: config,
|
||||||
logger,
|
logger: logger,
|
||||||
history,
|
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'); },
|
resume: () => { config.set('runtime.paused', false); logger.info('Resumed'); },
|
||||||
clearHistory: () => { history.clear(); logger.info('History cleared'); }
|
clearHistory: () => { history.clear(); logger.info('History cleared'); }
|
||||||
};
|
};
|
||||||
|
|
@ -147,9 +148,21 @@
|
||||||
logger.error(`🚨 EMERGENCY STOP: cancelled ${queuedCount} queued command(s)`);
|
logger.error(`🚨 EMERGENCY STOP: cancelled ${queuedCount} queued command(s)`);
|
||||||
logger.error('API disabled and scanning paused');
|
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)); }
|
delay(ms) { return new Promise(r => setTimeout(r, ms)); }
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.observer?.disconnect();
|
this.observer?.disconnect();
|
||||||
this.processed = new WeakSet();
|
this.processed = new WeakSet();
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
_fingerprint(el, idx) {
|
_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}`;
|
return `${base}|idx:${idx}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,20 @@
|
||||||
// @match https://claude.ai/*
|
// @match https://claude.ai/*
|
||||||
// @match https://gemini.google.com/*
|
// @match https://gemini.google.com/*
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
|
// @grant GM_notification
|
||||||
// @grant GM_setClipboard
|
// @grant GM_setClipboard
|
||||||
// @connect n8n.brrd.tech
|
// @connect n8n.brrd.tech
|
||||||
// @connect *
|
// @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/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/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/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-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/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/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/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/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/debug-panel.js
|
||||||
|
// @require https://gitea.brrd.tech/rob/AI-Repo-Commander/raw/branch/refactor-structure/src/main.js
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
Loading…
Reference in New Issue